/* ==========================================================================
   Seryno — theme styles

   The layout system here mirrors preview/nocturne.html exactly; colours and
   type come from theme.json. Anything WordPress-specific (header blocks,
   wp-element-button, editor chrome) lives in its own section below.
   ========================================================================== */

:root {
	/* Text that sits on the night sky. Deliberately independent of the page
	   background, so a dark canvas cannot make a night section unreadable. */
	--s-on-night:        #E6E6FA;
	--s-on-night-muted:  rgba(230, 230, 250, .74);
	--s-on-night-faint:  rgba(230, 230, 250, .46);
	--s-on-night-line:   rgba(230, 230, 250, .16);
	--s-on-night-accent: #DDA0DD;
	--s-night-2:         #241B33;

	--s-teal-hover: #4ADFE8;
	--s-on-teal:    #0A2A2D;

	--seryno-ease:    cubic-bezier(.22, .61, .36, 1);
	--seryno-maxw:    1344px;
	--seryno-measure: 48rem;
	--seryno-hairline: var(--wp--preset--color--hairline);

	/* Every content measure runs through this. 1 = the original width. */
	--w: 1.2;

	--gutter: clamp(1.25rem, 5vw, 4rem);
}

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

body {
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
	overflow-x: hidden;
}

::selection { background: var(--wp--preset--color--plum); color: #fff; }
:focus-visible { outline: 2px solid var(--wp--preset--color--accent); outline-offset: 3px; }

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

.skip-link:focus {
	position: fixed; top: 1rem; left: 1rem; z-index: 999;
	padding: .75rem 1.25rem;
	background: var(--wp--preset--color--ink);
	color: #fff; border-radius: 999px;
	clip-path: none; width: auto; height: auto;
}

/* --- Header ---------------------------------------------------------------
   Floating by default (Customizer → Seryno → Floating navigation). At the top
   it is invisible furniture on the hero; once you scroll it frosts over
   whatever is behind it and flips its type from lavender to ink.
   -------------------------------------------------------------------------- */

.seryno-nav-sentinel {
	position: absolute;
	top: 0;
	left: 0;
	width: 1px;
	height: 72px;
	pointer-events: none;
}

.seryno-header {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 100;
	background: transparent;
	border-bottom: 1px solid transparent;
	/* The padding lives here rather than on the block, so it can shrink when the
	   bar frosts. A `padding` attribute on the group would render as an inline
	   style and no `.is-stuck` rule could ever beat it. */
	padding-block: clamp(1.1rem, 2.2vw, 1.75rem);
	transition: border-color .45s var(--seryno-ease),
	            padding .45s var(--seryno-ease);
}

/* The frost is painted by a pseudo-element rather than by the header itself.
   `backdrop-filter` makes an element a containing block for *fixed*-position
   descendants, and the mobile menu overlay is exactly that — so with the filter
   on the header, an open menu resolved against the 71px bar instead of the
   viewport and collapsed into it. Measured: 70.9px tall frosted, 815px at the
   top of the page. A pseudo-element has no descendants to capture, so the
   overlay goes back to fixing against the viewport in both states.

   `inset: 0` covers the padding box, which leaves the header's own border-bottom
   outside the frost and crisp. */
.seryno-header::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	background: transparent;
	transition: background .45s var(--seryno-ease),
	            backdrop-filter .45s var(--seryno-ease);
}

/* The bar tracks the same measure as every section's shell, so the mark lines up
   with the content edge below it instead of being squeezed into the 816px
   content width WordPress gives a constrained group. */
.seryno-header__bar {
	max-width: var(--seryno-maxw);
	margin-inline: auto;
	padding-inline: var(--gutter);
	text-align: left;
}

/* Toggle off: the header sits at the top of the page and stays there. */
.seryno-nav-static .seryno-header { position: absolute; }

/* Over the hero: transparent, lavender type.
   Specificity note: core ships
   `.wp-block-navigation .wp-block-navigation-item__content.wp-block-navigation-item__content
   { color: inherit }` (0,3,0), so a rule that only names the anchor by tag
   loses and the links stay ink-soft — invisible on the night sky. Naming the
   content class takes us to (0,3,1) and the cascade lands where it should. */
.seryno-header .wp-block-site-title a,
.seryno-header .wp-block-navigation a.wp-block-navigation-item__content {
	color: var(--s-on-night-muted);
	transition: color .45s var(--seryno-ease);
}

.seryno-header .wp-block-site-title a { color: var(--s-on-night); }

/* Scrolled: frosted glass, hairline, ink type. */
.seryno-header.is-stuck {
	padding-block: clamp(.7rem, 1.4vw, 1rem);
	border-bottom-color: var(--wp--preset--color--hairline);
}

/* Real frost needs the pane to stay translucent — the blur can only show if
   there is something to see through it. .64 ground, 14px blur, 140% saturate.
   Above ~.90 it flattens into a solid strip.
   NOTE: backdrop-filter only samples what is painted below it in the same
   backdrop root, which is why this header must live outside any
   `isolation: isolate` section. */
.seryno-header.is-stuck::before {
	background: rgba(248, 244, 254, .64);
	backdrop-filter: saturate(140%) blur(14px);
	-webkit-backdrop-filter: saturate(140%) blur(14px);
}

.seryno-header.is-stuck .wp-block-site-title a { color: var(--wp--preset--color--ink); }
.seryno-header.is-stuck .wp-block-navigation a.wp-block-navigation-item__content { color: var(--wp--preset--color--ink-soft); }

/* Without backdrop-filter, an opaque bar beats unreadable type. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.seryno-header.is-stuck::before { background: rgba(248, 244, 254, .95); }
}

/* --- Navigation ------------------------------------------------------------
   One rule serves hover and the current page: it draws in from the left on
   hover and simply stays put on the active item, so hovering previews exactly
   what selection looks like.
   -------------------------------------------------------------------------- */

.wp-block-navigation .wp-block-navigation-item__content {
	position: relative;
	padding-block: .35rem;
	transition: color .3s var(--seryno-ease);
}

.wp-block-navigation .wp-block-navigation-item__content::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 2px;
	background: var(--wp--preset--color--glow-teal);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform .35s var(--seryno-ease), background .3s var(--seryno-ease);
}

.wp-block-navigation .wp-block-navigation-item__content:hover::after,
.wp-block-navigation .current-menu-item .wp-block-navigation-item__content::after,
.wp-block-navigation .current_page_item .wp-block-navigation-item__content::after {
	transform: scaleX(1);
}

/* Retract to the right on leave, so it withdraws rather than snapping back. */
.wp-block-navigation .wp-block-navigation-item__content:not(:hover)::after {
	transform-origin: right;
}

.wp-block-navigation :is(.current-menu-item, .current_page_item) .wp-block-navigation-item__content:not(:hover)::after {
	transform-origin: left;
}

.seryno-header .wp-block-navigation .wp-block-navigation-item__content:hover,
.seryno-header .wp-block-navigation :is(.current-menu-item, .current_page_item) .wp-block-navigation-item__content {
	color: var(--s-on-night);
}

/* Frosted over white the teal is only 1.83:1 and vanishes. Switch to the
   other half of the icon gradient — the blue — at 5.14:1. */
.seryno-header.is-stuck .wp-block-navigation .wp-block-navigation-item__content::after {
	background: var(--wp--preset--color--glow);
}

.seryno-header.is-stuck .wp-block-navigation .wp-block-navigation-item__content:hover,
.seryno-header.is-stuck .wp-block-navigation :is(.current-menu-item, .current_page_item) .wp-block-navigation-item__content {
	color: var(--wp--preset--color--ink);
}

/* theme.json underlines every link on hover, which is right in prose and wrong
   here: the animated rule under each item is already the indicator, so the two
   together read as two separate selections.
   Specificity note: core's rule is `:root :where(a:where(...):hover)` — the
   `:where()` contributes nothing, so it lands at (0,1,0) and this outranks it. */
.seryno-header a:hover { text-decoration: none; }

/* --- The mobile overlay ---------------------------------------------------
   The rules above dress the links for the ground the *bar* sits on. When the
   hamburger opens, those same links move onto the overlay's own cream panel, so
   the lavender that reads over the hero drops to about 1.1:1 on white and the
   menu looks empty. Inside an open overlay the panel decides the colour.
   -------------------------------------------------------------------------- */

.seryno-header .wp-block-navigation__responsive-container.is-menu-open a.wp-block-navigation-item__content,
.seryno-header.is-stuck .wp-block-navigation__responsive-container.is-menu-open a.wp-block-navigation-item__content {
	color: var(--wp--preset--color--ink-soft);
}

.seryno-header .wp-block-navigation__responsive-container.is-menu-open a.wp-block-navigation-item__content:hover,
.seryno-header .wp-block-navigation__responsive-container.is-menu-open :is(.current-menu-item, .current_page_item) a.wp-block-navigation-item__content,
.seryno-header.is-stuck .wp-block-navigation__responsive-container.is-menu-open a.wp-block-navigation-item__content:hover {
	color: var(--wp--preset--color--ink);
}

/* Room to breathe, and a size worth tapping. */
.seryno-header .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation__container {
	gap: .35rem;
}

.seryno-header .wp-block-navigation__responsive-container.is-menu-open a.wp-block-navigation-item__content {
	font-size: 1.0625rem;
	letter-spacing: .04em;
	padding-block: .7rem;
}

/* The sliding rule is a desktop affordance — in a stacked list there is no row
   for it to slide along, so it reads as clutter on every item. The item you are
   *on* is the exception: the menu still has to say where you are, and hiding the
   rule outright left mobile with no indicator at all. */
.seryno-header .wp-block-navigation__responsive-container.is-menu-open .wp-block-navigation-item__content::after {
	display: none;
}

.seryno-header .wp-block-navigation__responsive-container.is-menu-open :is(.current-menu-item, .current_page_item) .wp-block-navigation-item__content::after {
	display: block;
	background: var(--wp--preset--color--glow);
	transform: scaleX(1);
}

/* Colour alone is too quiet at this size, so the current item also takes weight.
   Specificity note: this has to out-rank the `.is-stuck` overlay colour rule,
   which is itself (0,5,1) — the extra `.wp-block-navigation` gets there. */
.seryno-header .wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open :is(.current-menu-item, .current_page_item) a.wp-block-navigation-item__content,
.seryno-header.is-stuck .wp-block-navigation .wp-block-navigation__responsive-container.is-menu-open :is(.current-menu-item, .current_page_item) a.wp-block-navigation-item__content {
	color: var(--wp--preset--color--ink);
	font-weight: 600;
}

/* The hamburger and the close cross are the only controls in the bar, and they
   were taking ink-soft on every ground — invisible against the night hero. */
.seryno-header .wp-block-navigation__responsive-container-open {
	color: var(--s-on-night);
	transition: color .45s var(--seryno-ease);
}

.seryno-header.is-stuck .wp-block-navigation__responsive-container-open {
	color: var(--wp--preset--color--ink);
}

.seryno-header .wp-block-navigation__responsive-container-close {
	color: var(--wp--preset--color--ink);
}

* { box-sizing: border-box; }

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

body {
	margin: 0;
	background: var(--wp--preset--color--cream);
	color: var(--wp--preset--color--ink-body);
	font-family: "Instrument Sans", -apple-system, BlinkMacSystemFont, sans-serif;
	font-size: 1.0625rem;
	line-height: 1.62;
	letter-spacing: -.004em;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	overflow-x: hidden;
}

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

::selection { background: var(--wp--preset--color--plum); color: #fff; }
:focus-visible { outline: 2px solid var(--wp--preset--color--accent); outline-offset: 3px; }

.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

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

h1, h2, h3, .display {
	font-family: "Cormorant", "Iowan Old Style", Georgia, serif;
	font-weight: 400;
	color: var(--wp--preset--color--ink);
	margin: 0;
	line-height: 1.02;
	letter-spacing: -.026em;
	text-wrap: balance;
}

/* The floors are set against the 17px body, not against the desktop sizes.
   Cormorant runs optically small — at the same px it reads ~three-quarters the
   size of Instrument Sans — so a heading floor has to clear the body type by a
   wide margin or a phone shows headings *smaller* than the paragraphs under
   them. 1.4rem Cormorant ≈ 1rem of the body face: not a hierarchy.

   The whole h1/h2 ladder is at 0.8 of what it was — floor, slope and ceiling
   each scaled by the same factor, so the type keeps its shape as the viewport
   moves rather than shrinking at one width and not another. h3 is deliberately
   untouched, which leaves it close to h2 at the mobile floor (1.7rem against
   1.8rem); scale it by the same 0.8 if that gap needs to reopen. */
.display { font-size: clamp(2rem, 3.65vw, 3.24rem); line-height: .98; letter-spacing: -.03em; }
h2 { font-size: clamp(1.8rem, 3.36vw, 2.8rem); }
h3 { font-size: clamp(1.7rem, 2.2vw, 2rem); line-height: 1.1; }

p { margin: 0 0 1.25em; }
p:last-child { margin-bottom: 0; }
em { font-style: italic; }
a { color: inherit; text-decoration: none; }

.seryno-eyebrow {
	font-size: .6875rem;
	font-weight: 600;
	letter-spacing: .2em;
	text-transform: uppercase;
	color: var(--wp--preset--color--ink-muted);
	margin: 0 0 1.1rem;
}

/* The lede is the one place the theme wants a genuinely light weight, and
   Instrument Sans cannot give it one — its `wght` axis floors at 400, and
   browsers do not synthesise lighter the way they fake bold. So the lede alone
   is set in Roboto Light 300 (bundled, latin subset, 16KB).
   Instrument Sans stays the fallback, so a failed font load degrades to the
   body face at its own lightest rather than to a system serif. */
.seryno-lede {
	/* Fallback spelled out: an undefined custom property makes `font-family`
	   invalid at computed-value time, which silently inherits — the same trap
	   that collapsed the h1s. */
	font-family: var(--wp--preset--font-family--lede, "Roboto", "Instrument Sans", -apple-system, BlinkMacSystemFont, sans-serif);
	font-size: clamp(1.0625rem, 1.4vw, 1.3rem);
	font-weight: 300;
	line-height: 1.6;
	letter-spacing: -.002em;
	color: var(--wp--preset--color--ink-body);
	max-width: calc(38rem * var(--w));
	margin-inline: auto;
}

/* --- Centred shell -------------------------------------------------------- */

.seryno-shell {
	max-width: var(--seryno-maxw);
	margin-inline: auto;
	padding-inline: var(--gutter);
	text-align: center;
}

.seryno-narrow { max-width: var(--seryno-measure); margin-inline: auto; }

.seryno-section { padding-block: clamp(4.5rem, 11vw, 9rem); position: relative; overflow: clip; }
.seryno-section--tight { padding-block: clamp(3rem, 6vw, 5rem); }

/* Long-form paragraphs read badly ragged on both sides. Centre the block,
   set the text flush left inside it. */
.seryno-copy { text-align: left; max-width: calc(36rem * var(--w)); margin-inline: auto; }

/* --- Ornament ------------------------------------------------------------- */

.seryno-ornament {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 1rem;
	margin-block: clamp(2.5rem, 6vw, 4rem);
}

/* A line break here would count as a flex item and pull the star off centre by
   half the gap. The shortcode trims its SVG so none is emitted, but an asset
   edited later must not be able to reintroduce it. */
.seryno-ornament br { display: none; }

.seryno-ornament__rule { width: clamp(2.5rem, 10vw, 6rem); height: 1px; background: var(--wp--preset--color--hairline); }
.seryno-night .seryno-ornament__rule { background: var(--s-on-night-line); }

/* ==========================================================================
   THE NIGHT BLOCK — reusable. Drop `.seryno-night` on any full-width section and it
   becomes a piece of sky: dark ground, star field, moon, luminous type.
   ========================================================================== */

.seryno-night {
	position: relative;
	background: var(--wp--preset--color--night);
	color: var(--s-on-night-muted);
	isolation: isolate;
	overflow: clip;
}

.seryno-night::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -2;
	background:
		radial-gradient(72% 58% at 70% 10%, rgba(139, 127, 168, .30), transparent 70%),
		radial-gradient(64% 52% at 18% 88%, rgba(74, 44, 90, .45), transparent 74%),
		linear-gradient(180deg, var(--wp--preset--color--night) 0%, var(--s-night-2) 58%, var(--wp--preset--color--night) 100%);
}

.seryno-night :is(h1, h2, h3) { color: var(--s-on-night); }
.seryno-night .seryno-eyebrow { color: var(--s-on-night-faint); }
.seryno-night .seryno-lede { color: var(--s-on-night-muted); }
.seryno-night .seryno-copy { color: var(--s-on-night-muted); }
.seryno-night em { color: var(--s-on-night-accent); }

/* Star layer inside a night block */
.seryno-night .seryno-night__field {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	/* The field is a direct child of a constrained group, so WordPress caps it at
	   the 816px content width and the stars stop short of the section's edges.
	   It is a backdrop, not content. */
	max-width: none;
	z-index: -1;
	opacity: .9;
	animation: twinkle 8s ease-in-out infinite alternate;
}

@keyframes twinkle { from { opacity: .55; } to { opacity: 1; } }

/* Motif placement inside a night block */
.seryno-night .moon-slot {
	position: absolute;
	top: clamp(4.5rem, 11vh, 8rem);
	right: clamp(1.5rem, 7vw, 7rem);
	z-index: -1;
	animation: drift 26s ease-in-out infinite alternate;
}

@keyframes drift {
	from { transform: translate3d(0, 0, 0); }
	to   { transform: translate3d(0, -14px, 0); }
}

@media (max-width: 760px) {
	.seryno-night .moon-slot { top: 5rem; right: 1rem; transform: scale(.62); transform-origin: top right; }
}

/* --- Hero ----------------------------------------------------------------- */

.seryno-hero {
	min-height: 100svh;
	display: flex;
	align-items: center;
	padding-block: 9rem 5rem;
}

.seryno-hero .display { max-width: 18ch; margin-inline: auto; }


.seryno-hero__meta { margin-top: 2rem; }

/* --- Tools, centred with ornament dividers -------------------------------- */

.seryno-tools { max-width: calc(46rem * var(--w)); margin-inline: auto; }

.seryno-tool { padding-block: clamp(2rem, 4vw, 2.75rem); }

.seryno-tool + .seryno-tool { border-top: 1px solid var(--wp--preset--color--hairline); }

.seryno-tool .seryno-tool__no {
	/* Specificity note: the number is a paragraph block in WordPress, so this
	   has to out-rank `.seryno-tool p` below, which sets its own size,
	   colour and measure. */
	display: block;
	max-width: none;
	font-size: .75625rem;   /* .6875rem +10% */
	letter-spacing: .2em;
	color: var(--wp--preset--color--accent);
	margin-bottom: .7rem;
}

.seryno-tool h3 { margin-bottom: .6rem; }
.seryno-tool p { margin: 0 auto; max-width: 34rem; color: var(--wp--preset--color--ink-body); font-size: 1rem; }

/* ==========================================================================
   THE STORY — how it works, dealt one card at a time

   The content is a spiral: three things that happen *to* you, then two where
   you can get a hand in. Order carries real meaning, so the marks are structure
   rather than ornament, and the reader is handed one step at a time.

   This was a scroll-snap sequence, one full screen per step, and it was wrong
   twice over. It read as five separate pages rather than as one movement, and
   `proximity` snapping still let an ordinary scroll settle between two steps
   with nothing resolved. The same idea is now told as a deck: a tall track, a
   sticky stack pinned inside it, and each card sliding up over the one before
   while the cards behind shrink, lift and dim into a visible pile. The reader
   gets the one-at-a-time reveal without the page ever taking the scroll away —
   `assets/js/story.js` only reads scroll position and writes transforms.

   Progressive enhancement, deliberately inverted. The stacking is opt-in: the
   cards below are an ordinary vertical list, and `.is-stacked` is added by the
   script and by nothing else. So no JavaScript, a narrow viewport, a
   reduced-motion preference and the block editor all get the readable list
   rather than a heap of absolutely-positioned invisible cards. The two media
   queries at the end of this block re-flatten it with `!important` as well,
   because the transforms are inline and would otherwise survive the class.

   Light on purpose. The cards need something to be white *against*, so the
   section is a sand band between two cream ones — the one place in the page
   where the surface, not a hairline, does the separating.

   The card is a plate, not a split screen. Its illustration is a wide drawing on
   white paper, which gives the layout two rules. It goes *under* the copy rather
   than beside it, because a landscape drawing in a 44%-wide column is a thumbnail.
   And it sits on bare white with no tray behind it: the drawing's own paper is
   pure white, so any panel under it — the sand fill this used to have — is just a
   visible rectangle around a picture that has no edges. What frames the card
   instead is a hairline held a few pixels inside its border, so the card reads as
   a printed plate and the drawing reads as ink on it. The drawing is centred in
   what the copy leaves, and on a wide card it is narrower than the plate; the
   slack is white on white, so the card can keep a shape of its own.

   Steps number themselves from a CSS counter, so a step added, removed or
   reordered in the Site Editor renumbers the rest with no markup to keep in
   sync, and the script reads the count back off the DOM to lengthen the track
   to match. Letters or digits is a block style on the track.
   ========================================================================== */

/* Typed, so the value story.js writes on every frame of a handover is parsed once
   into a number instead of being re-parsed out of a token stream everywhere it is
   read, and so an unset card has a real 0 rather than a guaranteed fallback.

   `inherits: true` is not the lazy choice, it is the only one that works. The
   veil is painted by `::before`, and a pseudo-element inherits from the element it
   hangs off — so a non-inherited property set on the card is invisible to it, and
   the veil would resolve to its initial value and never darken at all. What
   `inherits: false` would have bought — an invalidation that stops at the card
   instead of walking its subtree — story.js buys instead by not writing the
   property at all on the frames where it has not changed. */
@property --seryno-card-veil {
	syntax: "<number>";
	inherits: true;
	initial-value: 0;
}

.seryno-story {
	background: var(--wp--preset--color--sand);

	/* The card is a landscape plate rather than a full screen: a shorter card
	   leaves the stacked edges behind it visible without having to lift them
	   further, and a pinned card cannot be taller than the screen it is pinned to
	   anyway. Capped in rem so it stops growing on tall monitors, floored against
	   the viewport so a laptop in landscape still gets the whole card with the
	   header clear of it. */
	--seryno-story-h: min(39rem, calc(100svh - 7.5rem));
	/* Centred in what is left of the screen, but never tucked under the floating
	   header, which measures ~71px once frosted. */
	--seryno-story-top: max(5.25rem, calc((100svh - var(--seryno-story-h)) / 2));
	/* Scroll spent on each handover. Below about 0.7 screens the cards snap
	   past each other; above about one they feel becalmed. */
	--seryno-story-travel: 88svh;
	/* How far the deck is held off the screen edges. Its own token rather than
	   the page gutter, because a card is a surface with text inside it and needs
	   more room at the edge than a centred column of copy does. */
	--seryno-story-inset:  var(--gutter);
	/* The card's inner inset. Shared, so the drawing lines up with the copy above
	   it instead of being independently guessed at. */
	--seryno-story-pad:    clamp(1.575rem, 2.94vw, 2.625rem);
	/* How far the frame hairline sits inside the card's border. */
	--seryno-story-frame:  6px;
	--seryno-story-radius: 26px;
	/* The plate's own width, not the drawing's. A landscape drawing tall enough to
	   read needs less width than the card wants to have, so it is centred and the
	   card is wider than it — which costs nothing to look at, because the slack is
	   white beside a drawing on white paper. */
	--seryno-story-cardw:  76rem;
}

@media (max-width: 600px) {
	.seryno-story { --seryno-story-inset: 1.5rem; }
}

@media (min-width: 900px) {
	.seryno-story { --seryno-story-radius: 30px; }
}

.seryno-story.seryno-section { padding-block: clamp(4rem, 9vw, 7rem); }

.seryno-story__track {
	position: relative;
	counter-reset: seryno-step;
	margin-block-start: clamp(2.5rem, 6vw, 4.5rem);
}

/* The gutter is held by the stack rather than by the track, because the track is
   an `alignfull` group and `.wp-block-group.alignfull { padding-inline: 0 }`
   below is (0,2,0) — any padding here would need the block classes spelled back
   out to beat it. Sizing the stack keeps that out of the markup, and it gives
   the rail below one width to measure itself against. */
.seryno-story__stack {
	display: grid;
	gap: clamp(.75rem, 1.5vw, 1.25rem);
	/* `svw`, not `%`. The track is an alignfull group, which WordPress bleeds
	   past the viewport with negative root-padding margins — so `100%` here is
	   viewport + 2 root paddings, and on a phone the subtraction resolved to
	   exactly the screen width: cards flush against both edges. The track is
	   centred on the viewport, so measuring from the viewport keeps the insets
	   symmetric. */
	width: min(var(--seryno-story-cardw), 100svw - 2 * var(--seryno-story-inset));
	margin-inline: auto;
}

/* --- The card ------------------------------------------------------------- */

.seryno-story__step {
	counter-increment: seryno-step;
	/* The frame hairline below is positioned against this. */
	position: relative;
	display: grid;
	/* Copy takes what it needs; the drawing takes the rest. `minmax(0, 1fr)`
	   rather than `1fr`, or the row's automatic minimum is the drawing's own
	   height and a tall picture pushes the card past the pinned stack. */
	grid-template-rows: auto minmax(0, 1fr);
	background: var(--wp--preset--color--cream);
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--seryno-story-radius);
	overflow: hidden;
	/* Two layers: a tight one that seats the card on the sand, and a wide soft
	   one that lifts the front card clear of the pile behind it.

	   The soft one is held at 36px rather than the 60px it used to be, and the
	   reason is the deck rather than the drawing. A blur is fill the GPU pays for
	   on every repaint of the card, over an area that grows with the square of the
	   radius — and under the deck a card is a 1216x624 plate at 2x that repaints
	   each time its veil deepens. At 60px that blur was about a fifth of the
	   track's dropped frames all by itself; at 36px it is a couple, and the
	   shadow is a little tighter under the plate rather than absent from it.
	   Anything softer belongs to a card that is not being animated. */
	box-shadow: 0 2px 6px rgba(46, 15, 96, .05), 0 15px 36px rgba(46, 15, 96, .11);
}

/* The frame. A second hairline a few pixels inside the border, which is what
   gives the card its printed-plate edge now that nothing inside it is a filled
   panel. Drawn rather than inset with padding, so it costs the copy and the
   drawing no width — and `pointer-events: none` because it covers the card. */
.seryno-story__step::after {
	content: "";
	position: absolute;
	inset: var(--seryno-story-frame);
	/* A shade past `hairline`, which is mixed to disappear into sand and vanishes
	   entirely against the white of a card. Ink rather than lilac, so at this
	   weight it reads as a ruled line and not as a colour. */
	border: 1px solid rgba(46, 15, 96, .14);
	border-radius: calc(var(--seryno-story-radius) - var(--seryno-story-frame));
	pointer-events: none;
}

/* The card's head: the mark in its own column, the heading beside it, the
   description under the heading — indented past the mark, so the mark reads as a
   marginal note against a block of text rather than as part of the first line.
   A grid, not flow, because the spacing between heading and description then
   comes from `row-gap` instead of from margins WordPress's layout styles would
   overrule at (0,2,0). */
.seryno-story__copy {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	column-gap: clamp(.75rem, 1.4vw, 1.05rem);
	row-gap: .5rem;
	/* Baseline, not centre: on a narrow card the heading takes two lines, and a
	   mark centred against both of them floats between the lines instead of
	   marking the first one. */
	align-items: baseline;
	padding: var(--seryno-story-pad) var(--seryno-story-pad) clamp(.9rem, 1.6vw, 1.35rem);
}

/* The chapter mark. It used to be the loud element on a full screen with nothing
   else on it; at the head of a card it only has to number the step, so it is a
   token instead — set small in the display face, italic, in a hairline disc.
   Unfilled: on a white card beside a drawing on white paper, the sand disc this
   used to have was the one lilac object on the plate. */
.seryno-story__copy::before {
	content: counter(seryno-step, upper-alpha);
	grid-area: 1 / 1;
	display: grid;
	place-items: center;
	inline-size: 2.25rem;
	block-size: 2.25rem;
	font-family: "Cormorant", "Iowan Old Style", Georgia, serif;
	font-weight: 500;
	font-style: italic;
	font-size: 1.25rem;
	line-height: 1;
	/* Italic serif leans out of a centred box, so the optical centre needs a
	   nudge back that `place-items` cannot give it. */
	text-indent: .04em;
	color: var(--wp--preset--color--accent);
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 999px;
}

.is-style-seryno-steps-numbers .seryno-story__copy::before { content: counter(seryno-step, decimal-leading-zero); }
.is-style-seryno-steps-letters .seryno-story__copy::before { content: counter(seryno-step, upper-alpha); }

/* Placed, not auto-flowed: the mark holds 1/1, so the heading has to be told to
   take 1/2 or it would drop to the next row and leave the mark alone on the
   first one. */
.seryno-story__copy h3 { grid-area: 1 / 2; font-size: clamp(1.35rem, 1.89vw, 1.575rem); }

.seryno-story__copy p {
	grid-area: 2 / 2;
	/* Body copy, at the size the rest of the page sets it — the preset is already
	   fluid, so mobile and desktop track the page rather than the card. */
	font-size: var(--wp--preset--font-size--medium);
	color: var(--wp--preset--color--ink-body);
	/* No measure cap: the description runs the width of its column, so it stops at
	   the card's own right padding — the same inset the copy has on the left. */
}

/* `row-gap` does the spacing, so the block margins have to go — and they need a
   selector that beats the (0,2,0) `:root :where(.is-layout-flow) > *` rules
   WordPress prints for the copy group. Two classes and an element, (0,2,1). */
.seryno-story__step .seryno-story__copy h3,
.seryno-story__step .seryno-story__copy p { margin: 0; }

/* The drawing takes the copy's own inset, so its sides line up with the text
   above it and the space under it equals the space beside it. No top margin —
   the copy's bottom padding already is that gap — and no fill, no radius, no
   border: the illustration's paper is white and so is the card, so anything drawn
   around the picture is a box around something with no visible edges.

   The selector carries its weight: the step is a flow-layout group, and the
   layout styles WordPress prints for it — `:root :where(.is-layout-flow) > *`,
   specificity (0,2,0) — zero every child's block margins. A bare class here
   loses that fight and the bottom margin collapses to nothing, seating the
   drawing on the card's border. `figure` in the compound is what wins it,
   (0,2,1), independent of stylesheet order. */
.seryno-story__step > figure.seryno-story__media {
	/* The bottom margin is the copy's bottom padding, not the side inset: the
	   drawing is centred in what the card has left, so this margin *is* the gap
	   under it, and it should match the gap over it rather than the space at its
	   sides — which is slack, and invisible. */
	margin: 0 var(--seryno-story-pad) clamp(1rem, 1.8vw, 1.5rem);
	padding: 0;
	display: grid;
	/* An explicit `minmax(0, 1fr)` row rather than `place-items` alone. The block
	   is stretched to a definite height by the card, but an auto row inside it is
	   sized by its own content — so `max-height: 100%` on the drawing below would
	   resolve against an indefinite height, be dropped, and let a tall picture
	   run straight out through the bottom of the card. */
	grid-template-rows: minmax(0, 1fr);
	place-items: center;
	/* A grid item's automatic minimum size is its content, which would let a wide
	   drawing push the card past the pinned stack. */
	min-height: 0;
	overflow: hidden;
}

/* Fitted to whatever the card has left rather than sized in its own right. On a
   wide card that means the height cap is the one that binds and the drawing is
   centred with white either side of it; on a narrow one the width cap takes over.
   Either way the slack is white beside a drawing on white paper, which is to say
   it does not show up at all. */
.seryno-story__media img {
	max-height: 100%;
	max-width: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
}

/* Flat, the card has no fixed height, so `max-height: 100%` on the drawing
   resolves against an indefinite row and is dropped — the picture then renders at
   its natural height and every card in the list is a screen tall. This is the cap
   that replaces it, and it covers the reduced-motion desktop case too. */
.seryno-story__track:not(.is-stacked) .seryno-story__media img {
	max-height: min(46svh, 24rem);
}

/* Stacked single column, though, an empty image block is just a blank space
   between the copy and the next card — so there it collapses and the step reads
   as text until its drawing exists. The editor always keeps the block, or there
   would be nothing to drop an image onto. */
@media (max-width: 899px) {
	.seryno-story__step .wp-block-image:not(:has(img)) { display: none; }
}

.editor-styles-wrapper .seryno-story__step .wp-block-image { display: block; }

/* --- The deck -------------------------------------------------------------
   Switched on by story.js once it has found a stack of at least two cards on a
   wide enough viewport with motion allowed. Everything above stays the fallback.
   -------------------------------------------------------------------------- */

.seryno-story__track.is-stacked {
	/* One screen to read the first card, then a travel's worth of scroll per
	   handover. The script writes the count; the default matches the shipped
	   pattern, so the height is already right on the frame before it runs. */
	height: calc(100svh + (var(--seryno-story-steps, 5) - 1) * var(--seryno-story-travel));
}

.seryno-story__track.is-stacked .seryno-story__stack {
	display: block;
	position: sticky;
	top: var(--seryno-story-top);
	height: var(--seryno-story-h);
}

/* Cards are absolute and in source order, so each one paints over the last with
   no z-index to keep in step with the markup. */
.seryno-story__track.is-stacked .seryno-story__step {
	position: absolute;
	inset: 0;
	margin: 0;
	/* The pile is built by lifting and shrinking the cards behind, so they have
	   to shrink towards their own top edge — scaling about the centre would sink
	   those edges back under the front card instead of exposing them. */
	transform-origin: top center;
	/* Where a card waits before its handover. The script overwrites this on its
	   first frame; this is what the card looks like in between.

	   `visibility`, not `opacity: 0`. A zero-opacity card is still a painted,
	   composited thing as far as the compositor is concerned — five plate-sized
	   layers, each with a 60px-blur shadow and a rounded clip, held live for the
	   whole page. Hidden cards drop their layers instead, so the deck's cost
	   tracks how many cards are actually on screen rather than how many exist. */
	transform: translateY(105%);
	visibility: hidden;
}

.seryno-story__track.is-stacked .seryno-story__step:first-child {
	transform: none;
	visibility: visible;
}

/* `will-change` only while the deck is anywhere near the screen. Each card is a
   plate-sized layer — on a 2× display roughly 12MB of texture — and pinning five
   of them for the lifetime of the page is memory the rest of the page (the
   coverflow's own stage, most of all) then has to compete for. The observer in
   story.js adds this a full viewport before the track arrives, which is far
   enough out that the promotion itself never lands mid-scroll. */
.seryno-story__track.is-stacked.is-live .seryno-story__step {
	will-change: transform;
}

/* The veil's own layer, apart from the card's. Without this the opacity write
   below has nowhere to land but the card's existing layer — the one carrying
   the blurred box-shadow — so every veil step repaints that shadow along with
   it. A card with nothing stacked on it never veils, but the last handover
   veils every card behind it at once, which is where that cost was still
   showing up. Promoted, the veil is its own flat-colour texture and the
   shadow is painted once and left alone. */
.seryno-story__track.is-stacked.is-live .seryno-story__step::before {
	will-change: opacity;
}

/* How a card recedes once the next one is over it: a wash of the section's own
   sand across its face, written by the script as `--seryno-card-veil`. Fading the
   card itself instead — which is what this used to do — makes it translucent, and
   a translucent card with a drawing on it shows the drawing behind it through its
   own. Above the frame hairline as well as the copy, so the whole plate recedes
   together; `z-index` because `::after` would otherwise paint over it. */
.seryno-story__track.is-stacked .seryno-story__step::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 2;
	border-radius: inherit;
	background: var(--wp--preset--color--sand);
	opacity: var(--seryno-card-veil, 0);
	pointer-events: none;
}

/* Flatten again if the enhancement stops being appropriate. `!important` is
   load-bearing: story.js clears its inline transforms on the way out, but these
   also have to beat them if a media query flips mid-scroll before it does. */
@media (max-width: 899px) {
	.seryno-story__track.is-stacked { height: auto; }

	.seryno-story__track.is-stacked .seryno-story__stack {
		display: grid;
		position: static;
		height: auto;
	}

	.seryno-story__track.is-stacked .seryno-story__step {
		position: relative;
		inset: auto;
		transform: none !important;
		opacity: 1 !important;
		visibility: visible !important;
		will-change: auto !important;
	}
}

@media (prefers-reduced-motion: reduce) {
	.seryno-story__track.is-stacked { height: auto; }

	.seryno-story__track.is-stacked .seryno-story__stack {
		display: grid;
		position: static;
		height: auto;
	}

	.seryno-story__track.is-stacked .seryno-story__step {
		position: relative;
		inset: auto;
		transform: none !important;
		opacity: 1 !important;
		visibility: visible !important;
		will-change: auto !important;
	}
}

/* --- Progress rail --------------------------------------------------------
   The deck shows which card you are on but not how many are left. The rail is
   the only thing that does, so it stays: a hairline in the margin beside the
   cards, filling as the track goes by.
   -------------------------------------------------------------------------- */

/* The wrapper is what sticks: zero height, so it holds a place in the flow
   without taking any, and it spans the whole track — which is what a sticky
   element needs to stay put across all the cards. The rail itself is positioned
   inside it. */
.seryno-story__railwrap {
	position: sticky;
	top: 0;
	height: 0;
	z-index: 1;
	pointer-events: none;
}

.seryno-story__rail {
	position: absolute;
	top: 50svh;
	/* Pinned to the outside of the card, not to the viewport edge, so the gap
	   between rail and card stays put as the window widens. The card width is
	   measured in `svw` for the same reason the stack's is: the full-bleed track
	   this sits in is wider than the viewport, so `100%` overshoots. */
	left: calc(50% + min(var(--seryno-story-cardw), 100svw - 2 * var(--gutter)) / 2 + clamp(1rem, 2vw, 2.25rem));
	transform: translateY(-50%);
	width: 2px;
	height: clamp(5rem, 20vh, 9rem);
	background: var(--wp--preset--color--hairline);
	border-radius: 2px;
	overflow: hidden;
}

.seryno-story__progress {
	display: block;
	width: 100%;
	height: 100%;
	transform-origin: top;
	/* Filled by default: where scroll-driven animation is unsupported the rail
	   reads as a quiet hairline rather than an empty gutter. */
	transform: scaleY(1);
	background: linear-gradient(180deg, var(--wp--preset--color--glow), var(--wp--preset--color--glow-teal));
}

@supports (animation-timeline: view()) {
	.seryno-story { timeline-scope: --seryno-story; }
	.seryno-story__track { view-timeline: --seryno-story block; }

	.seryno-story__progress {
		animation: seryno-story-progress linear both;
		animation-timeline: --seryno-story;
		/* The track is several screens tall, so `contain` is the window in which
		   it covers the viewport completely — which is exactly the window in
		   which the stack is pinned. The rail and the deck retime together. */
		animation-range: contain 0% contain 100%;
	}

	@keyframes seryno-story-progress {
		from { transform: scaleY(.06); }
		to   { transform: scaleY(1); }
	}
}

/* Below this the card is as wide as the viewport allows and the rail has nothing
   left to sit in but the page gutter. Tracks the card's width — 76rem plus the two
   gutters it is held off the edges by. */
@media (max-width: 1320px) {
	.seryno-story__railwrap { display: none; }
}

/* In the editor the cards lie flat at their natural height — `.is-stacked` is
   never added there, so this is only about giving the stack back its gap. */
.editor-styles-wrapper .seryno-story__railwrap { display: none; }

/* ==========================================================================
   THE COVERFLOW — a look inside

   Device screenshots in a shallow 3D fan: the active one square on and full
   size, the rest turned to face it and stepped away across the stage. Clicking
   the front one opens its walkthrough.

   No card, no frame, no plate behind a screenshot. Two kinds of image are
   expected and the section takes both — a capture that already has the device
   bezel in it goes in as it is, and a bare screen gets the bezel drawn around
   it by the "Device frame" block style further down. Either way it sits
   straight on the white, so it reads as the device rather than as a picture of
   one on a tile.

   The fan's geometry lives in custom properties rather than in the script, so
   how far the screens spread, how hard they turn and how far back they step are
   all tunable here — including per breakpoint, which is what keeps the outer
   screens on stage on a phone.

   Opt-in, like the story deck. `.is-coverflow` is added by
   assets/js/coverflow.js and by nothing else, so the rules below are ordered
   fallback-first: without the script — and in the editor — the section is a
   plain grid of screenshots with their captions and YouTube links as text.
   ========================================================================== */

.seryno-flow {
	background: var(--wp--preset--color--cream);

	/* One screenshot's width. The spread below is a percentage of it, so the
	   arrangement holds its proportions as this changes. */
	--flow-w:     clamp(9.5rem, 17vw, 14rem);
	/* Tall enough for a 9:19.5 capture with its bezel. */
	--flow-ratio: 2.06;

	/* The screen's name, above the fan and above each screenshot in the grid.
	   The star's blue from the ornament rule — the one other place in the theme
	   a small piece of type is set apart from the plum around it. */
	--flow-name: #1959C5;

	/* The fan. Read by the script; unitless on purpose — percentages of a
	   screenshot's width for the spread, degrees for the turn, pixels for the
	   step back. Opened up well past a narrow V: the screens are meant to sit
	   beside each other rather than hide behind the front one. */
	--flow-spread-1: 92;
	--flow-spread-n: 56;
	--flow-angle:    30;
	--flow-depth-1:  90;
	--flow-depth-n:  46;
}

/* --- Fallback: a grid of screenshots -------------------------------------- */

.seryno-flow__stage {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(var(--flow-w), 1fr));
	gap: clamp(1.5rem, 4vw, 3rem);
	max-width: calc(60rem * var(--w));
	margin-inline: auto;
	margin-block-start: clamp(2.5rem, 6vw, 4rem);
	padding-inline: var(--gutter);
	justify-items: center;
}

/* Roboto, the theme's other sans — the face the lede is set in. Only the Light
   300 file is bundled, so the bold below is the browser's own thickening of it
   rather than a drawn bold; add a Roboto 700 `fontFace` to the "lede" family in
   theme.json and both names here pick it up with no change to this file.
   Fallback spelled out for the same reason as the lede: an undefined custom
   property makes `font-family` invalid at computed-value time and it silently
   inherits instead. */
.seryno-flow__label,
.seryno-flow__titletext {
	font-family: var(--wp--preset--font-family--lede, "Roboto", "Instrument Sans", -apple-system, BlinkMacSystemFont, sans-serif);
}

.seryno-flow__label {
	margin: 0 0 .85rem;
	font-size: .9375rem;
	line-height: 1.4;
	/* Bold at the size the copy around it runs at — a name for the screen, not a
	   second heading competing with the section's own. */
	font-weight: 700;
	color: var(--flow-name);
	text-align: center;
}

/* The stage is a flow-layout group as far as WordPress is concerned, whatever
   this file does with it, so core prints `:root :where(.is-layout-flow) > * + *`
   and hands every screen but the first a 24px top margin. In the grid that is a
   row of screenshots sitting a margin lower than the one beside them; in the fan
   it is worse, because a margin on an absolutely positioned box moves it — `top`
   places the margin edge, not the border edge — so the first screen came to the
   middle 24px above where the other twelve do. Zero here rather than in the
   coverflow rules: both arrangements want it, and neither has a use for a margin
   the gap or the transform is not already providing. The (0,2,0) selector is
   what beats core's (0,1,0). */
.seryno-flow__stage > .seryno-flow__item { margin-block: 0; }

.seryno-flow__shot { margin: 0; position: relative; }

.seryno-flow__shot img {
	display: block;
	width: 100%;
	height: auto;
	margin-inline: auto;
}

/* Capped to the same width the fan uses, so the drawn bezel below — which is
   sized from `--flow-w` — is exact in both arrangements rather than close. In
   the fan the item is already that wide, so this costs nothing there. */
.seryno-flow__shot { max-width: var(--flow-w); }

/* A landscape capture — the app on three devices at once — in a fan measured for
   a phone held upright. Half again as wide, which is enough for the desktop in
   it to read and still narrow enough that the picture only overhangs its
   neighbours in the fan rather than reaching the screen beyond them. The `min()`
   is for the grid fallback, where the column is what it is and a picture wider
   than its column would sit on the one beside it. */
.seryno-flow__shot.is-wide { max-width: min(100%, calc(var(--flow-w) * 1.5)); }

.seryno-flow__blurb {
	margin: 1rem 0 0;
	font-size: .9375rem;
	line-height: 1.55;
	color: var(--wp--preset--color--ink-body);
	text-align: center;
}

.seryno-flow__detail {
	margin-top: .75rem;
	font-size: .875rem;
	color: var(--wp--preset--color--ink-muted);
	text-align: center;
}

.seryno-flow__detail a { color: var(--wp--preset--color--accent); text-decoration: underline; }

/* The pattern ships the name and the caption with the opening screen's words
   already in them, but an older copy — or one someone emptied — should not
   reserve space. */
.seryno-flow__caption:has(.seryno-flow__captiontext:empty) { display: none; }
.seryno-flow__title:has(.seryno-flow__titletext:empty) { display: none; }

/* The shared name and caption belong to the carousel. In the grid fallback every
   screen already carries both of its own, so these would do nothing but repeat
   one screen's. Kept in the editor, where they are blocks to select and type
   into rather than duplicates. */
.seryno-flow:not(.is-coverflow) .seryno-flow__caption:not(.editor-styles-wrapper *),
.seryno-flow:not(.is-coverflow) .seryno-flow__title:not(.editor-styles-wrapper *) { display: none; }

/* --- The drawn device frame -----------------------------------------------
   Block style, so it is a click in the editor's Styles panel per screenshot:
   on for a bare screen capture, off for a PNG that already has a device around
   it. Every measure is a fraction of `--flow-w`, so the bezel stays in
   proportion at every breakpoint instead of thickening as the screen shrinks.
   -------------------------------------------------------------------------- */

.seryno-flow__shot.is-style-seryno-device {
	padding: calc(var(--flow-w) * .034);
	background: linear-gradient(155deg, #43434e 0%, #17171c 22%, #17171c 78%, #43434e 100%);
	border-radius: calc(var(--flow-w) * .132);
	box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .07);
}

.seryno-flow__shot.is-style-seryno-device img {
	border-radius: calc(var(--flow-w) * .1);
}

/* The island. Decorative and inside an aria-hidden-by-nature figure, so it
   carries no content of its own. */
.seryno-flow__shot.is-style-seryno-device::after {
	content: "";
	position: absolute;
	top: calc(var(--flow-w) * .058);
	left: 50%;
	transform: translateX(-50%);
	width: calc(var(--flow-w) * .24);
	height: calc(var(--flow-w) * .038);
	background: #0b0b0f;
	border-radius: 999px;
}

/* --- The fan -------------------------------------------------------------- */

.is-coverflow .seryno-flow__stage {
	display: block;
	position: relative;
	height: calc(var(--flow-w) * var(--flow-ratio));
	max-width: none;
	padding-inline: 0;
	/* Low enough to feel like a stage rather than a fisheye. */
	perspective: 1600px;
	perspective-origin: 50% 46%;
	/* A dragged screenshot would otherwise be picked up as an image drag. */
	touch-action: pan-y;
	-webkit-user-select: none;
	user-select: none;
}

.is-coverflow .seryno-flow__item {
	position: absolute;
	top: 0;
	left: 50%;
	width: var(--flow-w);
	height: 100%;
	margin-left: calc(var(--flow-w) / -2);
	transform-origin: 50% 50%;
	cursor: pointer;
	/* `visibility` rather than `filter`: the wash is a filter on the shot inside,
	   never on the item, so that leg of the transition never had anything to
	   animate. What the item does need is a way to stop existing once it has
	   faded — every screen here carries a 3D transform, which is its own
	   compositing layer, and a screen at `opacity: 0` keeps that layer and keeps
	   rasterising its three-function wash while being entirely invisible. Six of
	   the thirteen are off-stage at any moment.

	   Visibility is not interpolated, it is swapped at one end of the timing
	   function — going out it holds `visible` for the whole fade and flips at the
	   end, coming in it flips immediately and the fade runs under it. Which is
	   exactly the wanted behaviour, and the reason it belongs in the transition
	   rather than being set on its own. */
	transition: transform .55s var(--seryno-ease),
	            opacity .55s var(--seryno-ease),
	            visibility .55s var(--seryno-ease);
}

.is-coverflow .seryno-flow__shot {
	height: 100%;
	display: grid;
	place-items: center;
}

.is-coverflow .seryno-flow__shot img {
	width: auto;
	max-width: 100%;
	max-height: 100%;
	height: auto;
	object-fit: contain;
	pointer-events: none;
}

/* The wide screen overhangs its slot rather than widening it. The slot is what
   the script positions — every offset in the fan is a percentage of one screen's
   width — so a genuinely wider item would be thrown further out of the fan than
   the arrangement asks for, and only on the side it happens to sit. Overhanging
   leaves that arithmetic alone: the item is a phone's width like every other,
   and the picture inside it is centred and simply larger. */
.is-coverflow .seryno-flow__shot.is-wide {
	max-width: none;
	overflow: visible;
}

/* `justify-self`, and the auto side margins off. Those margins are what centres
   a screenshot in the grid fallback, but an auto margin resolves to zero the
   moment the space it is dividing goes negative — which is what a picture wider
   than its slot is — and the overhang then all falls on one side. */
.is-coverflow .seryno-flow__shot.is-wide img {
	max-width: 150%;
	margin-inline: 0;
	justify-self: center;
}

/* --- The wash -------------------------------------------------------------
   How an inactive screen recedes. `--flow-wash` runs 0 at the front to 1 at the
   back and is written by the script; everything below turns it into light.

   Not opacity, and not a white overlay. Opacity lets the page show through the
   artwork, which on white turns a dark phone into a grey ghost of itself; an
   overlay would need to be a rectangle, and these screenshots have transparent
   corners, so it would paint a white box around every rounded bezel. A filter
   is the only one of the three that follows the artwork's own silhouette.

   `contrast()` below 1 pulls every value towards mid-grey — which is what
   actually lifts a near-black bezel — and the brightness lifts the result the
   rest of the way towards the page. Saturation comes down with them so a
   colourful screenshot does not stay vivid while going pale.
   -------------------------------------------------------------------------- */

.is-coverflow .seryno-flow__shot {
	--wash-mul: 1;
	--wash: calc(var(--flow-wash, 0) * var(--wash-mul));
	filter:
		contrast(calc(1 - var(--wash) * .6))
		brightness(calc(1 + var(--wash) * .34))
		saturate(calc(1 - var(--wash) * .5));
	transition: filter .55s var(--seryno-ease);
}

/* The front screen is unfiltered outright rather than filtered by zero: a
   no-op filter still costs the browser a separate rasterisation of the one
   image that has to stay sharpest. */
.is-coverflow .seryno-flow__item[aria-current="true"] .seryno-flow__shot { filter: none; }

/* Hovering a side screen half-clears its wash. This is why the wash is a
   multiplier and not a single number: the script owns `--flow-wash` inline, and
   an inline custom property cannot be overridden by a rule — but the multiplier
   it is combined with can. */
.is-coverflow .seryno-flow__item:hover .seryno-flow__shot { --wash-mul: .45; }

.is-coverflow .seryno-flow__shot.is-style-seryno-device {
	width: 100%;
	height: 100%;
	place-items: stretch;
}

.is-coverflow .seryno-flow__shot.is-style-seryno-device img {
	width: 100%;
	height: 100%;
	max-height: none;
	object-fit: cover;
}

/* The shadow is cast by the artwork's own silhouette rather than by a box, so a
   rounded bezel on transparency does not sit on a rectangle of shade. Only the
   active screen carries one — on a turned screen it would fall in the wrong
   direction for the angle. */
.is-coverflow .seryno-flow__item[aria-current="true"] {
	filter: drop-shadow(0 16px 34px rgba(46, 15, 96, .26));
}

/* --- Hover: this opens ----------------------------------------------------
   Deliberately not a transform. The script owns every screen's transform, so a
   hover rule that touched it would be overwritten on the next arrangement —
   these lift by shadow and light instead.
   -------------------------------------------------------------------------- */

/* A side screen picks up a shadow to go with the wash it sheds above: it is
   clickable, but it only comes forward. */
.is-coverflow .seryno-flow__item:not([aria-current="true"]):hover {
	filter: drop-shadow(0 10px 22px rgba(46, 15, 96, .18));
}

.is-coverflow .seryno-flow__item[aria-current="true"]:hover,
.is-coverflow .seryno-flow__item[aria-current="true"]:focus-visible {
	filter: drop-shadow(0 22px 46px rgba(46, 15, 96, .36));
}

/* The play badge. Only ever on the active screen, because it is the only one a
   click opens — a badge on a side screen would promise the wrong thing. */
.is-coverflow .seryno-flow__item::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 3.5rem;
	height: 3.5rem;
	margin: -1.75rem 0 0 -1.75rem;
	border-radius: 999px;
	background: rgba(255, 255, 255, .92) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9 6.5v11l9-5.5z' fill='%232E0F60'/%3E%3C/svg%3E") center / 1.6rem no-repeat;
	box-shadow: 0 6px 20px rgba(0, 0, 0, .3);
	opacity: 0;
	transform: scale(.82);
	transition: opacity .3s var(--seryno-ease), transform .3s var(--seryno-ease);
	pointer-events: none;
}

.is-coverflow .seryno-flow__item[aria-current="true"]:hover::after,
.is-coverflow .seryno-flow__item[aria-current="true"]:focus-visible::after {
	opacity: 1;
	transform: scale(1);
}

/* --- With the walkthrough modal off ---------------------------------------
   The "Screens: no walkthrough" block style. Every promise the middle screen
   makes about opening has to come off with it: the play badge, the deepening
   shadow under the cursor, and the pointer itself. Side screens are untouched
   — they still come forward when clicked.
   -------------------------------------------------------------------------- */

.is-coverflow.is-style-seryno-flow-static .seryno-flow__item[aria-current="true"],
.is-coverflow .is-style-seryno-flow-static .seryno-flow__item[aria-current="true"] {
	cursor: default;
}

.is-coverflow.is-style-seryno-flow-static .seryno-flow__item[aria-current="true"]::after,
.is-coverflow .is-style-seryno-flow-static .seryno-flow__item[aria-current="true"]::after {
	content: none;
}

.is-coverflow.is-style-seryno-flow-static .seryno-flow__item[aria-current="true"]:hover,
.is-coverflow .is-style-seryno-flow-static .seryno-flow__item[aria-current="true"]:hover {
	filter: drop-shadow(0 16px 34px rgba(46, 15, 96, .26));
}

/* The name moves out of the item and into the line above the fan, the blurb into
   the shared caption below it, and the long copy belongs to the modal. All three
   stay in the DOM for the editor and for the script to read. */
.is-coverflow .seryno-flow__label,
.is-coverflow .seryno-flow__blurb,
.is-coverflow .seryno-flow__detail { display: none; }

/* --- The name above the fan -----------------------------------------------
   The active screen's name, swapped by the script as the fan turns. It sits
   directly over the screens rather than with the section's heading: it names
   what is being looked at, and it changes every few seconds, so it belongs to
   the carousel and not to the copy.
   -------------------------------------------------------------------------- */

.seryno-flow__title {
	margin-block-start: clamp(2.5rem, 6vw, 4rem);
	padding-inline: var(--gutter);
	text-align: center;
}

/* The fan's own top margin is the space under the heading, and the name is
   standing in that space now — so what is left between the name and the screens
   is the gap the name needs, not the gap the section did. Only in the carousel:
   in the grid fallback the name is hidden but still a sibling, and a hidden
   element cannot hold the space it was given. */
.is-coverflow .seryno-flow__title + .seryno-flow__stage { margin-block-start: clamp(.85rem, 1.8vw, 1.25rem); }

.seryno-flow__titletext {
	margin: 0 auto;
	max-width: 26rem;
	/* Bold, at the size the caption below runs at. Two lines of type under one
	   picture: the name and the sentence are the same size and differ only in
	   weight, so the pair reads as one caption rather than as a heading with
	   something under it. */
	font-size: clamp(1rem, 1.3vw, 1.0625rem);
	font-weight: 700;
	line-height: 1.5;
	color: var(--flow-name);
	/* One line, and the height is reserved for one: a name long enough to wrap
	   would move the whole fan down as it swapped in. */
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* --- Caption -------------------------------------------------------------- */

.seryno-flow__caption {
	margin-block-start: clamp(1.75rem, 4vw, 2.75rem);
	padding-inline: var(--gutter);
	text-align: center;
	/* Reserved so the section does not jog by a line when the caption swaps. */
	min-height: 3.4em;
}

.seryno-flow__captiontext {
	margin: 0 auto;
	/* Wider than the phone above it on purpose. A caption is the only place a
	   screen is described now that nothing opens, so it is sized to hold a
	   sentence on two lines rather than to line up with the bezel. */
	max-width: 26rem;
	font-size: clamp(1rem, 1.3vw, 1.0625rem);
	line-height: 1.65;
	color: var(--wp--preset--color--ink-body);
	/* Three lines, then an ellipsis — a backstop for a caption written longer
	   than the two lines the space reserves, not a budget to write to. Keep them
	   near 100 characters and this never fires. */
	display: -webkit-box;
	-webkit-line-clamp: 3;
	line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* --- Dots ----------------------------------------------------------------- */

.seryno-flow__dots {
	display: flex;
	justify-content: center;
	gap: .55rem;
	margin-block-start: clamp(1.25rem, 3vw, 2rem);
}

.seryno-flow__dots button {
	width: 7px;
	height: 7px;
	padding: 0;
	border: 0;
	border-radius: 999px;
	background: var(--wp--preset--color--hairline);
	cursor: pointer;
	transition: background .3s var(--seryno-ease), transform .3s var(--seryno-ease);
}

.seryno-flow__dots button:hover { background: var(--wp--preset--color--ink-muted); }

.seryno-flow__dots button.is-on {
	background: var(--wp--preset--color--accent);
	transform: scale(1.35);
}

/* --- The walkthrough modal ------------------------------------------------
   Stays dark whatever the section does: it is a video surface, and a bright
   plate around a black 16:9 embed reads as a hole punched in the page. A
   `<dialog>`, so the focus trap, the inert background and the Esc key are the
   browser's rather than ours.
   -------------------------------------------------------------------------- */

.seryno-flow__modal {
	width: min(56rem, calc(100vw - 2rem));
	max-height: calc(100svh - 2rem);
	/* A band across the top for the close button. The video used to carry it as
	   an overlay, which put it straight on top of YouTube's own settings and
	   captions buttons — a corner of the embed we do not control and cannot move
	   out of the way. */
	padding: 2.9rem 0 0;
	border: 1px solid var(--s-on-night-line);
	border-radius: 20px;
	background: var(--s-night-2);
	color: var(--s-on-night-muted);
	overflow: auto;
	overscroll-behavior: contain;
}

.seryno-flow__modal::backdrop {
	background: rgba(10, 9, 16, .72);
	backdrop-filter: blur(3px);
	-webkit-backdrop-filter: blur(3px);
}

.seryno-flow__close {
	position: absolute;
	top: .45rem;
	right: .55rem;
	z-index: 2;
	width: 2rem;
	height: 2rem;
	display: grid;
	place-items: center;
	padding: 0;
	font-size: 1.35rem;
	line-height: 1;
	color: var(--s-on-night-muted);
	background: transparent;
	border: 1px solid transparent;
	border-radius: 999px;
	cursor: pointer;
	transition: color .25s var(--seryno-ease), background .25s var(--seryno-ease);
}

.seryno-flow__close:hover {
	color: var(--s-on-night);
	background: rgba(230, 230, 250, .09);
	border-color: var(--s-on-night-line);
}

/* The media slot takes either a walkthrough or a still, and collapses to
   nothing when the screen has neither. */
.seryno-flow__media { display: none; }

.seryno-flow__modal.has-media .seryno-flow__media { display: block; }

/* 16:9 for a video, because that is the shape the embed arrives in. */
.seryno-flow__media.is-video {
	aspect-ratio: 16 / 9;
	background: #000;
}

.seryno-flow__media.is-video iframe {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

/* A still keeps its own shape instead — a screenshot is usually tall, and
   letterboxing one into 16:9 wastes most of the modal. Capped so a portrait
   capture cannot push the copy under it off screen. */
.seryno-flow__media.is-still figure { margin: 0; }

.seryno-flow__media.is-still img {
	display: block;
	width: auto;
	max-width: 100%;
	max-height: min(62svh, 34rem);
	margin-inline: auto;
	height: auto;
}

.seryno-flow__media.is-still figcaption {
	padding: .75rem clamp(1.5rem, 3.5vw, 2.25rem) 0;
	text-align: center;
	font-size: .85rem;
	color: var(--s-on-night-muted);
}

.seryno-flow__modalcopy {
	padding: clamp(1.5rem, 3.5vw, 2.25rem);
	font-size: 1rem;
	line-height: 1.7;
}

.seryno-flow__modalcopy p { margin: 0; max-width: 44rem; }
.seryno-flow__modalcopy p + p { margin-top: .9rem; }

/* --- Narrow ---------------------------------------------------------------
   The fan still works small — it is transforms, not layout — but the outer
   screens have to come in or they leave the stage entirely.
   -------------------------------------------------------------------------- */

@media (max-width: 900px) {
	.seryno-flow {
		--flow-spread-1: 70;
		--flow-spread-n: 38;
		--flow-depth-1:  76;
		--flow-depth-n:  38;
	}
}

@media (max-width: 600px) {
	.seryno-flow {
		--flow-w: clamp(8rem, 42vw, 11rem);
		--flow-spread-1: 58;
		--flow-spread-n: 28;
		--flow-angle:    34;
	}

	.is-coverflow .seryno-flow__item::after { width: 3rem; height: 3rem; margin: -1.5rem 0 0 -1.5rem; }
}

/* The turn is the whole effect, so with motion turned down the fan stops
   animating between arrangements — and the script stops advancing on its own. */
@media (prefers-reduced-motion: reduce) {
	.is-coverflow .seryno-flow__item { transition: none; }
}

/* --- Pricing -------------------------------------------------------------- */

.seryno-plans {
	display: grid;
	grid-template-columns: 1fr 1fr;
	max-width: calc(54rem * var(--w));
	margin-inline: auto;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 24px;
	overflow: hidden;
}

.seryno-plan { padding: clamp(2rem, 4vw, 3rem) clamp(1.25rem, 3vw, 2.5rem); }
.seryno-plan + .seryno-plan { border-left: 1px solid var(--wp--preset--color--hairline); background: var(--wp--preset--color--sand); }

.seryno-plan__price {
	font-family: "Cormorant", Georgia, serif;
	font-size: clamp(2.5rem, 5vw, 3.75rem);
	line-height: 1;
	color: var(--wp--preset--color--ink);
	margin: .4rem 0 1.75rem;
	letter-spacing: -.03em;
}

.seryno-plan__price span {
	display: block;
	font-family: "Instrument Sans", sans-serif;
	font-size: .75rem;
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--wp--preset--color--ink-muted);
	margin-top: .5rem;
}

.seryno-plan ul { list-style: none; margin: 0 0 2rem; padding: 0; }
.seryno-plan li { padding-block: .5rem; font-size: .9375rem; color: var(--wp--preset--color--ink-soft); }
.seryno-plan li + li { border-top: 1px solid var(--wp--preset--color--hairline); }

@media (max-width: 720px) {
	.seryno-plans { grid-template-columns: 1fr; border-radius: 20px; }
	.seryno-plan + .seryno-plan { border-left: 0; border-top: 1px solid var(--wp--preset--color--hairline); }
}

/* --- FAQ ------------------------------------------------------------------ */

.seryno-faq { max-width: calc(42rem * var(--w)); margin-inline: auto; text-align: left; border-top: 1px solid var(--wp--preset--color--hairline); }

.seryno-faq details { border-bottom: 1px solid var(--wp--preset--color--hairline); }

.seryno-faq summary {
	list-style: none;
	cursor: pointer;
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 1.5rem;
	padding-block: 1.2rem;
	font-family: "Cormorant", Georgia, serif;
	font-size: 1.35rem;
	color: var(--wp--preset--color--ink);
	transition: color .3s var(--seryno-ease);
}

.seryno-faq summary::-webkit-details-marker { display: none; }
.seryno-faq summary:hover { color: var(--wp--preset--color--plum); }

.seryno-faq summary::after {
	content: "+";
	font-size: 1.2075rem;   /* 1.05rem +15% */
	color: var(--wp--preset--color--ink-muted);
	transition: transform .4s var(--seryno-ease);
}

.seryno-faq details[open] summary::after { transform: rotate(45deg); }
.seryno-faq p { padding-bottom: 1.35rem; margin: 0; font-size: .9375rem; }

/* --- Honest note ---------------------------------------------------------- */

.seryno-note {
	max-width: calc(44rem * var(--w));
	margin-inline: auto;
	background: var(--wp--preset--color--sand);
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 22px;
	padding: clamp(1.75rem, 4vw, 2.75rem);
	text-align: left;
}

.seryno-note p { font-size: .9375rem; }
.seryno-note strong { color: var(--wp--preset--color--ink); font-weight: 600; }

/* --- Signup --------------------------------------------------------------- */

.seryno-signup { max-width: calc(30rem * var(--w)); margin: clamp(2rem, 5vw, 3rem) auto 0; }

.seryno-signup__form {
	display: flex;
	gap: .5rem;
	background: rgba(255, 255, 255, .06);
	border: 1px solid var(--s-on-night-line);
	border-radius: 999px;
	padding: .35rem .35rem .35rem 1.35rem;
	transition: border-color .35s var(--seryno-ease);
}

.seryno-signup__form:focus-within { border-color: var(--wp--preset--color--glow-teal); }

.seryno-signup__input {
	flex: 1; min-width: 0; border: 0; background: none;
	font: inherit; color: var(--s-on-night); padding: .7rem 0;
}

.seryno-signup__input::placeholder { color: var(--s-on-night-faint); }
.seryno-signup__input:focus { outline: none; }

.seryno-signup__button {
	border: 0; cursor: pointer; font: inherit; font-size: .8125rem; font-weight: 600;
	background: var(--wp--preset--color--glow-teal); color: var(--s-on-teal);
	border-radius: 999px; padding: .75rem 1.5rem;
	transition: background .35s var(--seryno-ease);
}

.seryno-signup__button:hover { background: var(--s-teal-hover); }
.seryno-signup__note { margin-top: 1rem; font-size: .8125rem; color: var(--s-on-night-faint); }

/* The closing panel's action row: the email pill with the launch-state CTA
   beside it. The pill takes the room it needs and the button sits alongside;
   below ~34rem of row they stack. */
.seryno-waitlist__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: .85rem 1rem;
	margin-top: clamp(2rem, 5vw, 3rem);
}

/* The seryno_cta shortcode returns an inline <a>, so the shortcode block hands it
   over still wrapped in a paragraph — the flex item is that wrapper, not the
   button. Size and centre the children generically so both lay out the same. */
.seryno-waitlist__actions > * { flex: none; margin: 0; text-align: center; }

.seryno-waitlist__actions > .seryno-signup {
	flex: 1 1 22rem;
	max-width: calc(30rem * var(--w));
}

/* The CTA is a link, not a form control, so it needs the pill's optical height. */
.seryno-waitlist__actions .seryno-cta { padding-block: .95rem; }

@media (max-width: 560px) {
	.seryno-waitlist__actions { flex-direction: column; align-items: stretch; }
}

@media (max-width: 460px) {
	.seryno-signup__form { flex-direction: column; border-radius: 20px; padding: .85rem; }
	.seryno-signup__button { width: 100%; }
}

/* --- Footer --------------------------------------------------------------- */

.seryno-footer { background: var(--wp--preset--color--sand); border-top: 1px solid var(--wp--preset--color--hairline); padding-block: clamp(3rem, 7vw, 4.5rem) 2rem; }

.seryno-footer .seryno-shell { text-align: center; }

.seryno-footer__motto {
	font-family: "Cormorant", Georgia, serif;
	font-style: italic;
	font-size: 1.3rem;
	color: var(--wp--preset--color--ink);
	margin: 1rem 0 2.25rem;
}

.seryno-footer__nav { display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem 2rem; margin-bottom: 2.25rem; }
.seryno-footer__nav a { font-size: .875rem; color: var(--wp--preset--color--ink-soft); }
.seryno-footer__nav a:hover { color: var(--wp--preset--color--plum); }

/* --- Socials -------------------------------------------------------------
   core/social-links supplies the icons, so there are no SVGs to ship and the
   URLs stay editable in the Site Editor. `is-style-logos-only` drops the
   coloured pills and lets the marks take our own ink, which is the only variant
   that sits quietly in a footer.
   -------------------------------------------------------------------------- */

.seryno-socials.wp-block-social-links {
	gap: .9rem;
	margin-inline: 0;
	padding-left: 0;
}

.seryno-socials .wp-block-social-link { margin: 0; }

/* `logos-only` paints its marks with `fill: currentColor`, so the colour has to
   be driven from `color` on the anchor — setting `fill` alone would be undone by
   core's own rule. */
.seryno-socials .wp-block-social-link-anchor {
	color: var(--wp--preset--color--ink-muted);
	transition: color .3s var(--seryno-ease), transform .3s var(--seryno-ease);
}

.seryno-socials .wp-block-social-link-anchor:hover {
	color: var(--wp--preset--color--plum);
	transform: translateY(-2px);
}

.seryno-socials .wp-block-social-link-anchor svg {
	width: 22px;
	height: 22px;
	fill: currentColor;
}

/* On a night ground the ink-muted mark disappears; lift it to the lavender. */
.seryno-night .seryno-socials .wp-block-social-link-anchor { color: var(--s-on-night-muted); }
.seryno-night .seryno-socials .wp-block-social-link-anchor:hover { color: var(--s-on-night-accent); }

/* This border is the footer's only dividing line. A separator block above it
   drew a second identical hairline — see parts/footer.html. */
.seryno-footer__legal {
	border-top: 1px solid var(--wp--preset--color--hairline);
	padding-top: 1.5rem;
	font-size: .75rem;
	color: var(--wp--preset--color--ink-muted);
	display: grid;
	gap: .4rem;
}

.seryno-footer .brand { justify-content: center; }
.seryno-footer .brand__name { color: var(--wp--preset--color--ink); }
.seryno-footer .brand svg path { fill: var(--wp--preset--color--plum); }

/* --- Day-side motif placement --------------------------------------------- */

.seryno-motif-abs { position: absolute; z-index: -1; pointer-events: none; }

.planet-slot { right: -4rem; top: 12%; opacity: .55; }
.orbit-slot  { left: 50%; top: 50%; transform: translate(-50%, -50%); opacity: .5; }

@media (max-width: 900px) { .seryno-motif-abs { display: none; } }

/* --- Reveals -------------------------------------------------------------- */

@supports (animation-timeline: view()) {
	.seryno-reveal { animation: rise linear both; animation-timeline: view(); animation-range: entry 3% cover 24%; }
	@keyframes rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
}

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


/* ==========================================================================
   WordPress specifics
   ========================================================================== */

/* Core button element — theme.json sets the colours; these are the manners. */
.wp-element-button,
.wp-block-button__link {
	transition: background-color .3s var(--seryno-ease),
	            color .3s var(--seryno-ease),
	            transform .3s var(--seryno-ease);
	will-change: transform;
}

.wp-element-button:hover,
.wp-block-button__link:hover { transform: translateY(-1px); }
.wp-element-button:active,
.wp-block-button__link:active { transform: translateY(0); }

/* Ghost button — the secondary action beside every primary CTA. */
.is-style-seryno-ghost .wp-block-button__link,
.seryno-cta--ghost {
	background: transparent;
	color: var(--wp--preset--color--ink);
	border: 1px solid var(--wp--preset--color--hairline);
}

.is-style-seryno-ghost .wp-block-button__link:hover,
.seryno-cta--ghost:hover {
	background: var(--wp--preset--color--sand);
	border-color: transparent;
	color: var(--wp--preset--color--plum);
}

/* On a night block the ghost button needs the night's own hairline. */
.seryno-night .is-style-seryno-ghost .wp-block-button__link,
.seryno-night .seryno-cta--ghost {
	border-color: var(--s-on-night-line);
	color: var(--s-on-night);
}

.seryno-night .is-style-seryno-ghost .wp-block-button__link:hover,
.seryno-night .seryno-cta--ghost:hover {
	background: transparent;
	border-color: var(--wp--preset--color--plum-light);
	color: var(--s-on-night-accent);
}

/* Blocks the patterns lean on */
.wp-block-columns { margin-block: 0; }
.wp-block-group.alignfull { padding-inline: 0; }

/* --- Page seams -----------------------------------------------------------
   WordPress drops a block gap between every top-level block and between every
   child of a constrained group. On a page built from full-bleed sections that
   gap is a cream band at each seam: above the hero, and either side of the
   night closing panel — which is what put daylight between the panel and the
   footer. The sections carry their own vertical rhythm, so every seam here is
   flush by design.

   Specificity note: core's rules are `:where(.wp-site-blocks) > *` (0,0,1) and
   `:root :where(.is-layout-constrained) > *` (0,1,0). A plain class selector in
   this stylesheet outranks the first and ties-then-wins the second on order.
   -------------------------------------------------------------------------- */

.wp-site-blocks > * { margin-block-start: 0; }

/* The same seam, one level down. On the front page the sections are the Home
   page's own content, so they arrive inside a post-content wrapper rather than
   as children of `main` — and it is the wrapper, not the hero, that picks up
   any gap above it. Zeroed here so the hero meets the header flush whichever
   shape the template has. */
main > .wp-block-post-content { margin-block-start: 0; }

.seryno-hero,
.seryno-section { margin-block-start: 0; }

.is-style-seryno-plain {
	list-style: none;
	padding-left: 0;
	display: grid;
	gap: .55rem;
}

/* Post lists */
.seryno-posts .wp-block-post {
	border-top: 1px solid var(--wp--preset--color--hairline);
	padding-block: 2rem;
}

.seryno-posts .wp-block-post-title a {
	color: var(--wp--preset--color--ink);
	transition: color .25s var(--seryno-ease);
}

.seryno-posts .wp-block-post-title a:hover { color: var(--wp--preset--color--plum); }

.seryno-article__meta {
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--ink-muted);
	display: flex; flex-wrap: wrap; gap: .6rem; align-items: center;
}

.seryno-article__meta > * + *::before { content: "\00b7"; margin-right: .6rem; opacity: .6; }

/* --- Tag chips -----------------------------------------------------------
   Shape and palette lifted from the app's dispute cards — see
   `_buildEveningTagChip` in lib/presentation/screens/disputes/disputes_screen.dart:
   a 20px pill, 9×4 padding, 11px medium type, and a five-colour cycle. The app
   walks the palette by list index (`index % palette.length`); :nth-of-type(5n)
   is the same walk in CSS.

   The separator is set to a single space in the templates rather than the block
   default ", ". Whitespace-only text between flex items is dropped, so the
   commas cannot survive as stray anonymous items between the pills.
   -------------------------------------------------------------------------- */

.seryno-tags { display: flex; flex-wrap: wrap; gap: .4rem; }

.seryno-tags a {
	display: inline-block;
	border-radius: 20px;
	padding: 4px 9px;
	font-size: 11px;
	font-weight: 500;
	letter-spacing: .1px;
	line-height: 1.45;
	transition: opacity .25s var(--seryno-ease);
}

.seryno-tags a:hover { opacity: .82; text-decoration: none; }

.seryno-tags a:nth-of-type(5n+1) { background: #2E2660; color: #AFA5E8; }  /* violet */
.seryno-tags a:nth-of-type(5n+2) { background: #103A40; color: #6ECBD6; }  /* teal   */
.seryno-tags a:nth-of-type(5n+3) { background: #3A2808; color: #D4A46A; }  /* amber  */
.seryno-tags a:nth-of-type(5n+4) { background: #3A1028; color: #D48098; }  /* rose   */
.seryno-tags a:nth-of-type(5n)   { background: #103028; color: #6EC898; }  /* sage   */

.seryno-prose > * { max-width: var(--seryno-measure); margin-inline: auto; }
.seryno-prose p { font-size: 1.1875rem; line-height: 1.72; margin-block: 0 1.4em; }

/* Article headings need their own ladder. The global h2/h3 sizes are tuned for
   full-bleed marketing sections (h2 runs to 3.5rem), which left a body
   subheading almost as large as the post title above it. */
.seryno-prose h2 { font-size: clamp(1.65rem, 2.6vw, 1.95rem); margin-block: 2.4em .6em; }
.seryno-prose h3 { font-size: clamp(1.4rem, 1.8vw, 1.5rem); margin-block: 2em .5em; }
.seryno-prose h4 { font-size: clamp(1.05rem, 1.4vw, 1.15rem); margin-block: 1.8em .4em; }
.seryno-prose > p:first-of-type {
	font-size: var(--wp--preset--font-size--lede);
	line-height: 1.55;
	color: var(--wp--preset--color--ink);
}

/* Store badges / pre-launch note */
.seryno-stores { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: .9rem; }
.seryno-stores__badge img { display: block; height: 48px; width: auto; transition: opacity .3s var(--seryno-ease); }
.seryno-stores__badge:hover img { opacity: .82; }
/* Pre-launch: three platform marks, then when. Flex rather than inline text so
   the marks sit on the type's optical centre — dropped inline they ride the
   baseline and hang below a line of small caps. */
.seryno-stores__soon {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: .55rem .8rem;
	font-size: .6875rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
	color: var(--s-on-night-faint); margin: 0;
}

.seryno-stores__on {
	display: inline-flex;
	align-items: center;
	gap: .6rem;
	/* A step brighter than the wording: the marks are the part being read. */
	color: var(--s-on-night-muted);
}

.seryno-stores__icon { display: block; width: 15px; height: 15px; }

/* The letter-spacing above is trailing whitespace on the last character, which
   pushes the whole line off centre by half a space. */
.seryno-stores__when { margin-right: -.2em; }

@media print {
	.seryno-header, .seryno-footer, .seryno-signup, .seryno-stores { display: none; }
}

/* ==========================================================================
   Pattern-specific pieces
   ========================================================================== */

/* The two display lines: the hero headline and the closing panel.
   18ch is chosen, not guessed: the hero line breaks to three lines at 16ch and
   below, and the closing panel collapses to a single line at 20ch and above.
   17–19ch is the window where both read as two lines; 18 sits in the middle.
   `ch` rather than `px` so the measure tracks the clamped font size. */
.seryno-display {
	/* The floor matters more than the ceiling. At 1.65rem it resolved to 26px on
	   a phone while every section h2 sat at 32px — the page's biggest heading
	   was its smallest, which reads as a mistake rather than as a hierarchy.
	   2rem is theme.json's own stated floor for h1, so the display heading still
	   agrees with the scale it is supposed to top. */
	font-size: clamp(2rem, 3.65vw, 3.24rem);
	line-height: .98;
	letter-spacing: -.03em;
	max-width: 18ch;
	margin-inline: auto;
}

.seryno-night .seryno-display em { color: var(--s-on-night-accent); font-style: italic; }

/* Headline measures, as classes rather than inline styles.

   A heading's inline `style` has to match what the block editor would write out
   from its own attributes, and `max-width` is not one of them — put it in the
   markup and every pattern using it opens with "Block contains unexpected or
   invalid content". So the clamp lives here and the pattern carries a class.
   Sits after `.seryno-display` so the closing panel's 14ch beats the 18ch it
   inherits from that rule.

   The counts are 1.5× what they were, which is not a 50% widening: `ch` tracks
   the font size, and the h2 ladder above just came down to 0.8, so a count left
   alone would have taken the measure down with it. 0.8 × 1.5 lands the rendered
   column 20% wider than it used to be — the same headings across a longer line,
   at smaller type. Change these to 14/18/20 to keep the old width instead. */
.seryno-measure-14,
.seryno-measure-18,
.seryno-measure-20 { margin-inline: auto; }
.seryno-measure-14 { max-width: 21ch; }
.seryno-measure-18 { max-width: 27ch; }
.seryno-measure-20 { max-width: 30ch; }

/* "Seryno answers it." */
.seryno-answer {
	font-family: var(--wp--preset--font-family--display);
	font-style: italic;
	font-size: clamp(1.6rem, 3vw, 2.25rem);
	color: var(--wp--preset--color--plum);
	margin-top: clamp(2.5rem, 5vw, 3.5rem);
}

/* The hero fills the viewport and centres itself in it. */
.seryno-hero {
	min-height: 100svh;
	display: flex;
	align-items: center;
	padding-block: clamp(7rem, 14vh, 10rem) clamp(3rem, 7vh, 5rem);
}

.seryno-hero__meta { margin-top: clamp(1.5rem, 3vw, 2rem); }

/* --- Page head — the compact hero ----------------------------------------
   Inner pages get a short band of the same night sky rather than the hero's full
   viewport, so the header has something dark to sit on and can float
   transparent, then frost on scroll, exactly as it does on the front page. That
   behaviour is not styling: `nav.js` only leaves the bar transparent when the
   first section inside <main> is a `.seryno-night`, which this band is.

   The top padding has to clear the *unstuck* bar, which measures ~96px, hence a
   9rem floor rather than the section rhythm.
   -------------------------------------------------------------------------- */

.seryno-pagehead {
	padding-block: clamp(9rem, 20vh, 12rem) clamp(2.75rem, 6vh, 4rem);
	text-align: center;
}

/* Sized off the h1 preset rather than off a number of its own. It used to top
   out at 3.15rem against an h2 running to 3.5rem, which put the page's title
   *below* every section heading under it — the one inversion the ladder cannot
   afford. Matching the preset keeps h1 above h2 at the floor, at the ceiling and
   at every width between, because both are the same clamp scaled alike. */
.seryno-pagehead .seryno-pagehead__title,
.seryno-pagehead h1 {
	max-width: 26ch;
	margin-inline: auto;
	font-size: clamp(2rem, 3.6vw, 3rem);
	line-height: 1.04;
	letter-spacing: -.028em;
}

.seryno-pagehead .seryno-lede { margin-top: 1.25rem; }

/* The eyebrow and the article meta are both ink-toned by default and vanish here. */
.seryno-pagehead .seryno-article__meta { color: var(--s-on-night-faint); justify-content: center; }
.seryno-pagehead .seryno-article__meta a { color: var(--s-on-night-muted); }
.seryno-pagehead .seryno-tags { justify-content: center; }

/* The full moon rides high, just under the bar. It sits at z-index -1, so where
   a long title reaches it the type crosses in front rather than colliding. */
.seryno-night .moon-slot--small {
	top: clamp(5.5rem, 12vh, 7.5rem);
	bottom: auto;
	right: clamp(1rem, 6vw, 5rem);
}

@media (max-width: 760px) {
	.seryno-night .moon-slot--small {
		top: 5rem;
		right: .5rem;
		transform: scale(.66);
		transform-origin: top right;
	}
}

.seryno-pagehead__meta { margin-top: 1rem; color: var(--s-on-night-faint); }

/* The body that follows a page head carries the rhythm the old main padding did.
   It is `alignfull` so an alignwide child (a featured image) can still reach the
   wide measure, which means it has to supply its own gutter — `.alignfull` groups
   have their inline padding zeroed for the full-bleed night sections.
   Specificity note: that zeroing rule is `.wp-block-group.alignfull` (0,2,0) and
   sits later in this file, so the gutter has to be restored at (0,3,0) to land
   regardless of order. */
.seryno-pagebody.wp-block-group.alignfull {
	padding-block: clamp(3.5rem, 8vw, 6rem) clamp(4rem, 10vw, 7rem);
	padding-inline: var(--gutter);
}

/* The closing panel's moon sits low, so it doesn't collide with the heading. */
/* Specificity note: the base rule is `.seryno-night .moon-slot` (0,2,0), so a
   bare `.moon-slot--low` (0,1,0) can never override its `top`. Match the
   ancestor to win. */
.seryno-night .moon-slot--low {
	top: auto;
	bottom: 12%;
	right: 8%;
}

/* Pricing runs on a columns block, so the grid has to win over its flex. */
.seryno-plans.wp-block-columns {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 0;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: 24px;
	overflow: hidden;
	max-width: calc(54rem * var(--w));
	margin-inline: auto;
}

.seryno-plans .wp-block-column { flex-basis: auto !important; margin: 0; }

.seryno-plan + .seryno-plan {
	border-left: 1px solid var(--wp--preset--color--hairline);
	background: var(--wp--preset--color--sand);
}

.seryno-plan .wp-block-list { margin-block: 0 2rem; }

.seryno-plan li {
	padding-block: .5rem;
	font-size: var(--wp--preset--font-size--small);
	color: var(--wp--preset--color--ink-body);
	text-align: center;
}

.seryno-plan li + li { border-top: 1px solid var(--wp--preset--color--hairline); }

@media (max-width: 720px) {
	.seryno-plans.wp-block-columns { grid-template-columns: 1fr; border-radius: 20px; }
	.seryno-plan + .seryno-plan {
		border-left: 0;
		border-top: 1px solid var(--wp--preset--color--hairline);
	}
}

/* The ornament's star comes in as a raw <svg> from the shortcode. */
.seryno-ornament svg { display: block; flex: none; }

/* App screens — used by the seryno/screens pattern, once you add real images. */
.seryno-screens {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: clamp(1rem, 3vw, 2.5rem);
	align-items: center;
	max-width: calc(62rem * var(--w));
	margin-inline: auto;
}

.seryno-screens figure { margin: 0; }

.seryno-shot {
	border-radius: 20px;
	overflow: hidden;
	box-shadow: 0 2px 6px rgba(46, 15, 96, .06), 0 30px 70px rgba(46, 15, 96, .13);
	transition: transform .6s var(--seryno-ease);
}

.seryno-shot img { display: block; width: 100%; height: auto; }

.seryno-screens figure:nth-child(1) .seryno-shot { transform: translateY(2rem); }
.seryno-screens figure:nth-child(3) .seryno-shot { transform: translateY(-1.5rem); }
.seryno-screens figure:hover .seryno-shot { transform: translateY(0) scale(1.02); }

.seryno-screens figcaption {
	font-size: .75rem;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--ink-muted);
	margin-top: 1.1rem;
	text-align: center;
}

@media (max-width: 760px) {
	.seryno-screens { grid-template-columns: 1fr; max-width: 20rem; }
	.seryno-screens figure:nth-child(1) .seryno-shot,
	.seryno-screens figure:nth-child(3) .seryno-shot { transform: none; }
	.seryno-screens figure:nth-child(3) { display: none; }
}

@media (prefers-reduced-motion: reduce) {
	.seryno-shot { transform: none !important; }
}

/* ==========================================================================
   Empty containers collapse
   --------------------------------------------------------------------------
   Several of the containers above are sized independently of what is inside
   them — `.seryno-section` carries up to 9rem of padding on each edge,
   `.seryno-hero` a whole viewport, the coverflow stage a fixed height with
   absolutely positioned children, the story track several screens of scroll
   runway. Emptied out in the Site Editor they keep every pixel of that and
   show nothing, and `overflow: clip` on the section removes the last visual
   clue. The result is a band of blank cream with no block to select and no
   sidebar control to turn down — the space is in this file, not in the block.

   So each of them is made to answer for its content: no content, no box. This
   is the same idiom already used for the coverflow caption further up, applied
   to the containers that can strand a screenful.

   Note `svg` is deliberately absent from the probe below. Every motif on this
   site — starfield, moon, ornament — is an inline SVG placed as decoration, so
   a section holding nothing but a starfield is still an empty section and
   should go. Empty paragraphs and captionless figures are excluded for the
   same reason: deleting the text out of a block is the usual way one of these
   is left behind, and `<p></p>` should not be what keeps 288px alive.

   `:has()` is load-bearing, which makes the fallback the right one: where it
   is unsupported the whole rule is invalid and dropped, and the theme behaves
   exactly as it did before.
   ========================================================================== */

/* `:not(.editor-styles-wrapper *)` is what keeps this off the editor, where
   `functions.php` loads this same stylesheet and where an empty container is
   not a mistake but a block you are about to fill — collapsed, it would leave
   nothing to select and nothing to drop onto. It has to be written as an
   exclusion on the subject rather than as a `display` override further down,
   because these containers are variously `grid`, `flex` and `block`: any
   single value handed back would be the wrong one for most of them.

   The generic probe, once, for every container that is sized by this file
   rather than by its contents. The `main > …` arm catches a plain Group
   dropped into the page and then emptied — no class of ours on it, the same
   blank band — and is held to the page's own top level so it cannot reach a
   structural wrapper inside a pattern. */
:is(
	.seryno-section,
	.seryno-hero,
	.seryno-pagehead,
	.seryno-shell,
	main > .wp-block-post-content > .wp-block-group
):not(.editor-styles-wrapper *):not(:has(:is(
	h1, h2, h3, h4, h5, h6,
	p:not(:empty), li:not(:empty), a:not(:empty), td, th,
	img, picture, video, audio, canvas, iframe, embed, object,
	input, textarea, select, button,
	blockquote, pre, hr
))) { display: none; }

/* The two scripted layouts, by their own parts rather than by the generic
   probe: both are built to hold their size for children that have not arrived
   yet — the stage because its items are absolute, the track because its height
   is scroll runway — so an empty one is invisible by construction. */
.seryno-flow__stage:not(.editor-styles-wrapper *):not(:has(.seryno-flow__item, .seryno-flow__shot)) { display: none; }
.seryno-story__track:not(.editor-styles-wrapper *):not(:has(.seryno-story__step)) { display: none; }
