/* ==========================================================================
   somsorium — motion & scroll effects (progressive enhancement)
   Motion language modelled on the reference the team likes: a pronounced
   rise-in on scroll, endless marquees, gently slanted section edges, springy
   hover micro-interactions, and swipeable card carousels. No library, no build.

   The hidden state is gated behind html.reveal-ready (armed by an inline <head>
   script), so with JavaScript off nothing is ever hidden. Everything collapses
   to "no motion" under prefers-reduced-motion.
   ========================================================================== */
:root {
	--ease-rise: cubic-bezier(.22, 1, .36, 1);     /* smooth ease-out for reveals */
	--ease-spring: cubic-bezier(.34, 1.56, .64, 1); /* slight overshoot for hovers */
	--reveal-dist: 5rem;   /* full zebra-grade rise */
	--reveal-dur: .9s;
}

/* --- reveal on scroll (rise-in) ------------------------------------------- */
.reveal { transition: opacity var(--reveal-dur) var(--ease-rise), transform var(--reveal-dur) var(--ease-rise);
	transition-delay: calc(var(--reveal-i, 0) * 90ms); will-change: opacity, transform; }
.reveal-ready .reveal { opacity: 0; transform: translateY(var(--reveal-dist)); }
.reveal-ready .reveal.reveal--left  { transform: translateX(calc(-1 * var(--reveal-dist))); }
.reveal-ready .reveal.reveal--right { transform: translateX(var(--reveal-dist)); }
.reveal-ready .reveal.reveal--scale { transform: scale(.94); }
.reveal-ready .reveal.reveal--none  { transform: none; }
.reveal-ready .reveal.is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
	.reveal, .reveal-ready .reveal, .reveal-ready .reveal.is-visible {
		opacity: 1 !important; transform: none !important; transition: none !important;
	}
}

/* --- marquee (endless ticker) --------------------------------------------- */
/* The shift is one repeat of the item set, measured and set by reveal.js. The
   fallback of 100% is only reached with JavaScript off, where there is a single
   set and nothing to loop seamlessly anyway. */
@keyframes somsorium-marquee {
	from { transform: translateX(0); }
	to   { transform: translateX(calc(-1 * var(--marquee-shift, 100%))); }
}
.marquee { overflow: hidden; }
.marquee__track { display: flex; align-items: center; width: max-content; gap: var(--marquee-gap, 3.5rem);
	animation: somsorium-marquee var(--marquee-dur, 32s) linear infinite; }
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__item { flex: 0 0 auto; display: inline-flex; align-items: center; gap: .55rem;
	font-family: var(--mono); font-size: .82rem; letter-spacing: .12em; text-transform: uppercase; color: var(--mint-dark); }
/* WordPress gives every sibling after the first a 24px block gap. In a flex row
   that lands as a top margin, so the first phrase sat 24px above all the others.
   Specificity has to clear .wp-block-group.is-layout-flow > :where(…). */
.marquee .marquee__track > .marquee__item { margin-block: 0; }
.marquee__item::before { content: "◆"; color: var(--mint); font-size: .6em; }
.marquee--dark { background: var(--navy); padding: 16px 0; }
.marquee--dark .marquee__item { color: #9fc4c0; }
.marquee--dark .marquee__item::before { color: var(--mint); }
@media (prefers-reduced-motion: reduce) {
	.marquee { overflow-x: auto; }
	.marquee__track { animation: none; }
}

/* --- slanted section edges ------------------------------------------------ */
/* The slant is carved out of the section's OWN box. It used to be pulled up with
   a negative margin, which made the band paint over the bottom of the section
   above it — on every product page that swallowed the last column of the status
   roadmap. The section now stays in normal flow; the edge is cut inside its own
   area and the lost height is given back as padding, so nothing overlaps and the
   headline keeps its distance from the diagonal. */
.slant-top, .slant-bottom, .slant-both { --_slant: 3.2vw; }
.slant-top   { clip-path: polygon(0 var(--_slant), 100% 0, 100% 100%, 0 100%); padding-top: calc(var(--sec-y) + var(--_slant)); }
.slant-bottom{ clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--_slant)), 0 100%); padding-bottom: calc(var(--sec-y) + var(--_slant)); }
.slant-both  { clip-path: polygon(0 var(--_slant), 100% 0, 100% calc(100% - var(--_slant)), 0 100%);
	padding-top: calc(var(--sec-y) + var(--_slant)); padding-bottom: calc(var(--sec-y) + var(--_slant)); }

/* --- hover micro-interactions --------------------------------------------- */
@keyframes somsorium-spin { from { transform: rotate(0); } to { transform: rotate(360deg); } }
.card, .produkt, .stat { transition: transform .35s var(--ease-spring), box-shadow .35s ease, border-color .35s ease; }
.card:hover, .stat:hover { transform: translateY(-4px); box-shadow: 0 14px 30px rgba(13, 36, 51, .1); border-color: color-mix(in srgb, var(--mint) 45%, var(--line)); }
.produkt:hover { transform: translateY(-3px); box-shadow: 0 18px 40px rgba(13, 36, 51, .09); }
.spin-on-hover { transition: transform .3s var(--ease-spring); }
.spin-on-hover:hover { transform: rotate(180deg); }
.spin-on-hover--full:hover { animation: somsorium-spin .7s var(--ease-rise); }
@media (prefers-reduced-motion: reduce) {
	.card, .produkt, .stat, .spin-on-hover { transition: none; }
	.card:hover, .produkt:hover, .stat:hover, .spin-on-hover:hover { transform: none; box-shadow: none; }
}

/* --- horizontal card scrollers -------------------------------------------- */
.h-scroll { display: flex; gap: 18px; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-left: 24px;
	-webkit-overflow-scrolling: touch; padding: 4px 24px 20px; margin: 26px -24px 0;
	scrollbar-width: thin; scrollbar-color: var(--mint) transparent; cursor: grab; overscroll-behavior-x: contain; }
.h-scroll.is-dragging { cursor: grabbing; scroll-snap-type: none; user-select: none; }
.h-scroll::-webkit-scrollbar { height: 8px; }
.h-scroll::-webkit-scrollbar-thumb { background: var(--mint); border-radius: 99px; }
.h-scroll::-webkit-scrollbar-track { background: transparent; }
.h-scroll > * { flex: 0 0 auto; scroll-snap-align: start; width: min(82vw, 300px); margin-top: 0 !important; }
@supports (mask-image: linear-gradient(#000, #000)) {
	.h-scroll--fade { -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%);
		mask-image: linear-gradient(90deg, transparent 0, #000 26px, #000 calc(100% - 26px), transparent 100%); }
}
@media (max-width: 640px) {
	.grid--scroll, .grid.is-scrollable { display: flex; grid-template-columns: none; gap: 14px; overflow-x: auto;
		scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; padding-bottom: 14px; margin-inline: -24px;
		padding-inline: 24px; scrollbar-width: thin; scrollbar-color: var(--mint) transparent; }
	.grid--scroll > *, .grid.is-scrollable > * { flex: 0 0 auto; width: min(80vw, 300px); scroll-snap-align: start; }
}

/* --- nav entrance (plays once on load) ------------------------------------ */
@keyframes nav-brand-in { from { opacity: 0; transform: translateX(-1.6rem); } to { opacity: 1; transform: none; } }
@keyframes nav-item-in  { from { opacity: 0; transform: translateY(-.55rem); } to { opacity: 1; transform: none; } }
.site-header .brand { animation: nav-brand-in .6s var(--ease-rise) both; }
.site-header .nav-toggle { animation: nav-brand-in .6s var(--ease-rise) both; }
.nav-links > li { animation: nav-item-in .5s var(--ease-rise) both; }
.nav-links > li:nth-child(1) { animation-delay: .14s; }
.nav-links > li:nth-child(2) { animation-delay: .21s; }
.nav-links > li:nth-child(3) { animation-delay: .28s; }
.nav-links > li:nth-child(4) { animation-delay: .35s; }
.nav-links > li:nth-child(5) { animation-delay: .42s; }
.nav-links > li:nth-child(n+6) { animation-delay: .49s; }
@media (max-width: 720px) { /* mobile menu slides via .nav.open — don't double-animate items */
	.nav-links > li { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
	.site-header .brand, .site-header .nav-toggle, .nav-links > li { animation: none; }
}

/* --- typewriter (rotating typed headline, zebra-style) -------------------- */
.typewriter { }
.typewriter__caret {
	display: inline-block; width: .07em; height: 1em; margin-left: .06em; vertical-align: -.12em;
	background: var(--mint); animation: tw-blink 1.05s steps(1) infinite;
}
@keyframes tw-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
.typewriter--glow .typewriter__text {
	background: linear-gradient(90deg, #fff, var(--mint)); -webkit-background-clip: text; background-clip: text; color: transparent;
	animation: tw-glow 3.2s ease-in-out infinite;
}
@keyframes tw-glow { 0%, 100% { filter: drop-shadow(0 0 0 rgba(100, 199, 178, 0)); } 50% { filter: drop-shadow(0 0 14px rgba(100, 199, 178, .45)); } }
@media (prefers-reduced-motion: reduce) {
	.typewriter__caret { animation: none; opacity: 1; }
	.typewriter--glow .typewriter__text { animation: none; }
}

/* ── new components (2026-09) ────────────────────────────────────────────────
   Motion here explains a relationship: a line that draws itself between two
   states, a dot that marks where we are. Nothing moves just to move. */

/* Status roadmap — the connecting line grows in as the section arrives. */
.roadmap__step::before { transform-origin: left center; transform: scaleX(0); transition: transform .8s var(--ease-rise); }
.roadmap.is-visible .roadmap__step::before,
.reveal.is-visible .roadmap__step::before { transform: scaleX(1); }
.roadmap.is-visible .roadmap__step:nth-child(2)::before { transition-delay: .18s; }
.roadmap.is-visible .roadmap__step:nth-child(3)::before { transition-delay: .36s; }
/* The "today" dot keeps a slow pulse — it marks a live position, not a fixed point. */
.roadmap__step--now::after { animation: somsorium-pulse 3.4s ease-in-out infinite; }
@keyframes somsorium-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(100, 199, 178, .45); }
  50%      { box-shadow: 0 0 0 7px rgba(100, 199, 178, 0); }
}

/* Step flow — the vertical connector draws downward while scrolling past. */
.steps li:not(:last-child)::after,
.timeline li:not(:last-child)::after { transform-origin: top center; transform: scaleY(0); transition: transform .7s var(--ease-rise); }
.steps.is-visible li::after,
.timeline.is-visible li::after,
.reveal.is-visible .steps li::after,
.reveal.is-visible .timeline li::after { transform: scaleY(1); }
.steps.is-visible li:nth-child(2)::after { transition-delay: .1s; }
.steps.is-visible li:nth-child(3)::after { transition-delay: .2s; }
.steps.is-visible li:nth-child(4)::after { transition-delay: .3s; }

/* Journey rail — the connector draws along the direction of travel: downward on
   a phone, left to right on a desktop, one stage after the next. */
.journey__stage::before { transform-origin: top center; transform: scaleY(0); transition: transform .6s var(--ease-rise); }
.journey.is-visible .journey__stage::before,
.reveal.is-visible .journey__stage::before { transform: none; }
.journey__stage:nth-child(2)::before { transition-delay: .08s; }
.journey__stage:nth-child(3)::before { transition-delay: .16s; }
.journey__stage:nth-child(4)::before { transition-delay: .24s; }
.journey__stage:nth-child(5)::before { transition-delay: .32s; }
.journey__stage:nth-child(6)::before { transition-delay: .40s; }
.journey__stage:nth-child(7)::before { transition-delay: .48s; }
@media (min-width: 900px) {
  .journey__stage::before { transform-origin: left center; transform: scaleX(0); }
}

/* Algae culture — the growth is not driven from here any more.

   It used to hang off the reveal observer: come into view, transition once. The
   front-page journey now takes its progress from thread.js instead, so the
   culture fills on exactly the scroll that draws the line reaching it rather
   than on a transition with a duration of its own. The rules live with the rest
   of the figure in base.css, as pure functions of --thread-progress, and there
   is deliberately no transition on them: the value arrives already eased, and a
   CSS transition on top would be a second easing fighting the first. */

/* Ecosystem flow — hovering a node lifts it slightly; the rest stays put. */
.flow__node { transition: border-color .25s, box-shadow .25s, transform .25s var(--ease-rise); }
.flow__node:hover, .flow__node:focus-visible { transform: translateY(-3px); }
.flow__detail { transition: opacity .25s; }
.flow__detail.is-swapping { opacity: 0; }

/* Carousel — arrows and dots only. The track itself uses native smooth scroll. */
.carousel__prev, .carousel__next { transition: border-color .2s, color .2s, transform .2s var(--ease-rise); }
.carousel__prev:hover, .carousel__next:hover { transform: translateY(-2px); }
.carousel__dot { transition: background-color .25s, width .25s var(--ease-rise); }

@media (prefers-reduced-motion: reduce) {
  .roadmap__step::before { transform: scaleX(1); transition: none; }
  .roadmap__step--now::after { animation: none; }
  .steps li::after, .timeline li::after { transform: scaleY(1); transition: none; }
  .journey__stage::before { transform: none; transition: none; }
  .flow__node, .flow__detail, .carousel__prev, .carousel__next, .carousel__dot { transition: none; }
  .flow__node:hover, .flow__node:focus-visible,
  .carousel__prev:hover, .carousel__next:hover { transform: none; }
}
