/**
 * GEEKS Timeline.
 *
 * Everything here is scoped to the widget: the engine places the arc, the words
 * and the controls in pixels, and this file only says what they look like and
 * which way round they sit. The custom properties are written by the engine from
 * the panel, so a setting changes a value here rather than a rule.
 */

.geeks-tl {
	--gtl-ink: #2f3437;
	--gtl-soft: #6b7276;
	--gtl-faint: #a8aeb1;
	--gtl-line: rgba( 47, 52, 55, .28 );
	position: relative;
}

.geeks-tl * {
	box-sizing: border-box;
}

/* Nothing until it is placed.

   The markup is a stack of empty boxes at their natural positions, and every one
   of them is moved into place by measurement a frame or two later; shown during
   that, the widget looks half-assembled and then snaps together. The engine
   lifts this the moment the first layout is done.

   The animation is a failsafe, not decoration: if the script never runs — an
   error, a blocked file, JavaScript switched off — the stage appears anyway a
   few seconds later rather than leaving a hole in the page. */
.geeks-tl.is-waiting .geeks-tl-stage {
	opacity: 0;
}

/* The failsafe is for a page where the script never ran — an error, a blocked
   file, JavaScript switched off — so that a widget is never a hole in the page.
   It must therefore be impossible for a LIVE widget: the engine stamps is-live
   the moment it builds, and without that guard this timer lifted the cover on
   its own after four seconds, showing a timeline that had not been snapped to
   and had not played its entrance. */
.geeks-tl.is-waiting:not( .is-live ) .geeks-tl-stage {
	animation: geeks-tl-failsafe 0s linear 4s forwards;
}

@keyframes geeks-tl-failsafe {

	to {
		opacity: 1;
	}
}

/* ---------- the stage ---------- */
/* Something to stand on.

   Every picture in here is a layer that fades: with a delay on the first one,
   or with the background switched off entirely, a transparent stage shows the
   page THROUGH the timeline — the header above it, the footer below it, sliding
   past behind the words. The colour is the author's (Style → The stage); this is
   what it starts as, so a widget is never see-through by accident. */
.geeks-tl-stage {
	background-color: #fff;
	position: relative;
	width: 100%;
	height: var( --tl-h, 100vh );
	overflow: hidden;
	touch-action: none;
	-webkit-user-select: none;
	user-select: none;
	-webkit-tap-highlight-color: transparent;
	color: var( --gtl-ink );
}

/* One pile for every picture, background and overlay alike, in the order they
   arrived: a milestone's background covers whatever the milestone before it
   left, and its overlay then sits on that. The engine puts the layers here and
   DOM order does the stacking; the markup only reserves the box. */
.geeks-tl-pics {
	position: absolute;
	inset: 0;
	z-index: 1;
	pointer-events: none;
}

.geeks-tl-pic {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: center;
	will-change: opacity, mask-position;
}

/* A flat wash or a printed pattern over the pictures, under everything the
   reader touches. */
.geeks-tl-ov {
	position: absolute;
	inset: 0;
	z-index: 5;
	pointer-events: none;
	display: none;
	background-repeat: repeat;
}

/* ---------- the arc ---------- */
.geeks-tl-svg {
	position: absolute;
	inset: 0;
	z-index: 6;
	width: 100%;
	height: 100%;
	pointer-events: none;
	overflow: visible;
}

.geeks-tl-svg circle {
	fill: none;
	stroke: var( --arc-col, var( --gtl-line ) );
	stroke-width: var( --arc-w, 1 );
}

.geeks-tl-marks {
	position: absolute;
	inset: 0;
	z-index: 7;
	pointer-events: none;
}

.geeks-tl .gtl-mark {
	position: absolute;
	left: 0;
	top: 0;
	display: flex;
	flex-direction: column;
	align-items: var( --mark-align, center );
	gap: var( --dot-gap, 7px );
	transform-origin: 50% 50%;
	pointer-events: auto;
	cursor: pointer;
	width: var( --mark-w, 150px );
	font-size: clamp( 11px, .95vw, 15px );
	text-align: var( --mark-text, center );
	will-change: transform, opacity;
}

/* Which way a label falls away from its dot is the arrangement, not a style:
   below the dot along the top, above it along the bottom, beside it either
   side. */
.geeks-tl-marks[ data-side="bottom" ] .gtl-mark {
	flex-direction: column-reverse;
}

.geeks-tl-marks[ data-side="left" ] .gtl-mark {
	flex-direction: row;
	align-items: flex-start;
}

.geeks-tl-marks[ data-side="right" ] .gtl-mark {
	flex-direction: row-reverse;
	align-items: flex-start;
}

/* The same four, the other way about.

   A label leans in towards the words in the middle by default; this is the
   author asking for it to fall away from them instead. It is only ever which of
   the two — the dot or its words — comes first, so nothing but the direction
   changes: the alignment above still applies, and so does everything measured
   from the mark.

   These come after the four above deliberately. Each is worth exactly as much
   as the rule it overrides, so it is source order that settles it. */
.geeks-tl-marks[ data-lab="far" ] .gtl-mark {
	flex-direction: column-reverse;
}

.geeks-tl-marks[ data-side="bottom" ][ data-lab="far" ] .gtl-mark {
	flex-direction: column;
}

.geeks-tl-marks[ data-side="left" ][ data-lab="far" ] .gtl-mark {
	flex-direction: row-reverse;
}

.geeks-tl-marks[ data-side="right" ][ data-lab="far" ] .gtl-mark {
	flex-direction: row;
}

.geeks-tl .gtl-txt {
	display: flex;
	flex-direction: column;
	gap: var( --yr-gap, 3px );
	align-items: var( --mark-align, center );
	width: 100%;
}

.geeks-tl-marks[ data-side="left" ] .gtl-txt,
.geeks-tl-marks[ data-side="right" ] .gtl-txt {
	flex: 1;
	min-width: 0;
	width: auto;
}

.geeks-tl .gtl-disc {
	width: var( --dot );
	height: var( --dot );
	/* Round unless the author says otherwise. Per cent is of the dot itself, so
	   50% is a circle at any size and 0 is a square. */
	border-radius: var( --dot-r, 50% );
	overflow: hidden;
	display: grid;
	place-items: center;
	flex: none;
	background-color: var( --gtl-soft );
	background-size: cover;
	background-position: center;
	transition: transform .28s cubic-bezier( .2, .7, .3, 1 );
	transform: scale( var( --dot-scale, 1 ) );
	will-change: transform;
}

.geeks-tl .gtl-yr {
	font-size: 1.55em;
	letter-spacing: .01em;
	color: var( --gtl-soft );
	line-height: 1;
}

.geeks-tl .gtl-sub {
	font-size: .92em;
	color: var( --gtl-faint );
	line-height: 1.25;
	max-width: 100%;
}

/* The first paint is a placement, not a move: without this the dots slide in
   from wherever the transition thinks they were. */
.geeks-tl.is-booting .gtl-mark,
.geeks-tl.is-booting .gtl-disc,
.geeks-tl.is-booting .geeks-tl-pill,
.geeks-tl.is-booting .gtl-t {
	transition: none !important;
}

/* ---------- the words ---------- */
.geeks-tl-stem {
	position: absolute;
	z-index: 7;
	pointer-events: none;
	transform-origin: 50% 50%;
	display: none;
}

.geeks-tl-stem i {
	display: block;
	width: 100%;
	height: 100%;
	background: var( --stem-col, #c9ced2 );
}

.geeks-tl-content {
	position: absolute;
	z-index: 8;
	transform-origin: 50% 50%;
	display: flex;
	flex-direction: column;
	/* Where the block's own edge sits, and where each line sits inside it: two
	   questions with two answers, both centred until the author says otherwise. */
	align-items: var( --content-align, center );
	text-align: var( --content-text, center );
	width: min( 46em, 86% );
	max-width: 100%;
	pointer-events: none;
	will-change: transform, opacity;
}

.geeks-tl-date {
	font-size: clamp( 18px, 2vw, 30px );
	color: var( --gtl-faint );
	line-height: 1;
}

.geeks-tl-title span.ch,
.geeks-tl-date span.ch {
	display: inline-block;
	white-space: pre;
	will-change: filter, opacity;
}

/* A word is a word in both states.

   The reveal wraps each word in a span, and a span cannot break inside itself;
   bare text can, at a hyphen — so the heading re-wrapped the instant the reveal
   finished and the spans collapsed. At rest the words keep a wrapper of their
   own, and this is all it does. */
.geeks-tl-title span.w,
.geeks-tl-date span.w {
	white-space: nowrap;
}

.geeks-tl-title {
	margin: var( --title-gap, 10px ) 0 0;
	font-size: clamp( 30px, 4.6vw, 70px );
	font-weight: 400;
	line-height: 1.05;
	letter-spacing: -.015em;
	color: var( --gtl-ink );
	width: var( --title-w, 100% );
	max-width: 100%;
}

.geeks-tl-desc {
	margin: var( --desc-gap, 14px ) 0 0;
	font-size: clamp( 13px, 1.15vw, 18px );
	color: var( --gtl-soft );
	line-height: 1.5;
	width: var( --desc-w, 560px );
	max-width: 100%;
}

.geeks-tl-types {
	margin-top: var( --types-gap, 14px );
	display: flex;
	gap: .5em;
	flex-wrap: wrap;
	justify-content: center;
}

.geeks-tl-types span {
	font-size: .78em;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var( --gtl-faint );
	border: 1px solid var( --gtl-line );
	border-radius: 999px;
	padding: .35em .9em;
}

/* ---------- the slider and the reel, in one box ---------- */

/* Both of them sit on the same edge, one beside the other, and the space
   between them is the author's. A flex box with a gap is what says that: the
   engine no longer has to know how thick the reel is in order to shift the
   slider clear of it, because the browser measures.

   `pointer-events` matters here. The box spans both children AND the gap
   between them, and the stage behind it takes drags — so the box itself lets
   presses through and only its children take them. */
.geeks-tl-controls {
	position: absolute;
	z-index: 9;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var( --ctl-gap, 10px );
	pointer-events: none;
}

.geeks-tl-controls > * {
	pointer-events: auto;
}

.geeks-tl-slider {
	display: flex;
	align-items: center;
	gap: 2px;
	height: var( --slider-h, 44px );
	background: var( --slider-bg, rgba( 255, 255, 255, .55 ) );
	backdrop-filter: blur( 8px );
	border-radius: var( --slider-r, 999px );
	padding: var( --slider-pad, 2px );
	box-shadow: 0 1px 14px rgba( 0, 0, 0, .08 );
	user-select: none;
}

.geeks-tl-slider.vertical {
	flex-direction: column;
	height: auto;
	width: var( --slider-h, 44px );
}

/* Reversed: the two buttons change ends. Only the slider's own children are
   re-ordered — the track's insides are laid out by the engine and are not
   touched by this. */
.geeks-tl-slider.rev {
	flex-direction: row-reverse;
}

.geeks-tl-slider.vertical.rev {
	flex-direction: column-reverse;
}

.geeks-tl-nav {
	display: inline-flex;
	align-items: center;
	gap: .5em;
	border: 0;
	background: var( --nav-bg, rgba( 255, 255, 255, .9 ) );
	border-radius: var( --pill-r, 999px );
	height: var( --inner-h, 40px );
	padding: 0 1.1em;
	cursor: pointer;
	font: inherit;
	font-size: 13px;
	color: var( --nav-col, var( --gtl-ink ) );
	white-space: nowrap;
	flex: none;
	transition: background-color .18s ease, color .18s ease;
}

/* Every state is said out loud, and said with enough weight to beat a parent
   theme's button rules — Hello Elementor colours its own buttons on hover and
   on press, and without these its colours arrive instead of the author's. */
.geeks-tl .geeks-tl-nav:hover {
	background: var( --nav-bg-h, var( --nav-bg, rgba( 255, 255, 255, .9 ) ) );
	color: var( --nav-col-h, var( --nav-col, var( --gtl-ink ) ) );
}

/* Focus, and being held down, wear the same third colour: both mean this
   button is the one the reader is on. Written after hover, so it wins while
   the pointer is over it as well. */
.geeks-tl .geeks-tl-nav:focus,
.geeks-tl .geeks-tl-nav:focus-visible,
.geeks-tl .geeks-tl-nav:active {
	background: var( --nav-bg-a, var( --nav-bg-h, var( --nav-bg, rgba( 255, 255, 255, .9 ) ) ) );
	color: var( --nav-col-a, var( --nav-col-h, var( --nav-col, var( --gtl-ink ) ) ) );
}

/* An icon with no words beside it wants a round button, not a wide one. */
.geeks-tl-nav:not( :has( .geeks-tl-nav-t ) ) {
	padding: 0;
	width: var( --inner-h, 40px );
	justify-content: center;
}

.geeks-tl-nav svg,
.geeks-tl-nav i {
	width: 1em;
	height: 1em;
	font-size: 1.15em;
	line-height: 1;
	display: block;
	fill: currentColor;
}

.geeks-tl-slider.vertical .geeks-tl-nav {
	width: var( --inner-h, 40px );
	height: auto;
	padding: 1.1em 0;
	writing-mode: vertical-rl;
	text-orientation: mixed;
}

.geeks-tl-slider.vertical .geeks-tl-pill {
	writing-mode: vertical-rl;
	text-orientation: mixed;
}

/* The words turn with the control; the arrow has to be turned itself.

   `writing-mode` lays a vertical button out top to bottom and turns its TEXT
   with it — but an icon is replaced content and is left exactly as it was, so
   a back arrow went on pointing left down the side of a vertical slider. A
   quarter turn puts it right: a left arrow reads as up, a right one as down,
   which is the way a vertical band runs.

   THREE things can turn an arrow — a vertical slider, a flipped one, and a
   reversed one — and they add up rather than replace each other. Written as a
   transform apiece the last rule in the file would simply win, so each sets its
   own share and this adds them. */
.geeks-tl-nav svg,
.geeks-tl-nav i {
	transform: rotate( calc( var( --nav-turn, 0deg ) + var( --nav-rev, 0deg ) ) );
}

.geeks-tl-slider.vertical .geeks-tl-nav {
	--nav-turn: 90deg;
}

/* Flipped for the right-hand slider the whole control is turned, so the icon
   comes back round with it and needs no quarter turn of its own. */
.geeks-tl-slider.vertical.flip .geeks-tl-nav {
	--nav-turn: 270deg;

	transform: rotate( 180deg );
}

/* Reversed, Back is at the other end of the track and points the other way. */
.geeks-tl-slider.rev .geeks-tl-nav {
	--nav-rev: 180deg;
}

.geeks-tl-slider.vertical.flip .geeks-tl-years span {
	transform: translateY( -50% ) rotate( 180deg );
}

.geeks-tl-track {
	position: relative;
	flex: 1;
	height: var( --inner-h, 40px );
	min-width: 0;
	cursor: grab;
}

.geeks-tl-slider.vertical .geeks-tl-track {
	width: var( --inner-h, 40px );
	height: auto;
	flex: 1;
	min-height: 0;
}

.geeks-tl-track.dragging {
	cursor: grabbing;
}

.geeks-tl-ticks {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0 var( --tick-end, 10px ) 0 var( --tick-start, 10px );
}

.geeks-tl-slider.vertical .geeks-tl-ticks {
	flex-direction: column;
	padding: var( --tick-start, 10px ) 0 var( --tick-end, 10px );
}

.geeks-tl .gtl-tick {
	width: 1px;
	height: 9px;
	background: var( --tick-col, var( --gtl-ink ) );
	opacity: .22;
	transition: opacity .3s, height .3s, width .3s;
}

.geeks-tl-slider.vertical .gtl-tick {
	width: 9px;
	height: 1px;
}

.geeks-tl .gtl-tick.near {
	opacity: .5;
	height: 13px;
}

.geeks-tl-slider.vertical .gtl-tick.near {
	width: 13px;
	height: 1px;
}

.geeks-tl .gtl-tick.on {
	opacity: 1;
	height: 17px;
}

.geeks-tl-slider.vertical .gtl-tick.on {
	width: 17px;
	height: 1px;
}

.geeks-tl-pill {
	position: absolute;
	top: 0;
	left: 0;
	height: var( --inner-h, 40px );
	min-width: 66px;
	border-radius: var( --pill-r, 999px );
	background: var( --pill-bg, #fff );
	display: grid;
	place-items: center;
	font-size: 13px;
	color: var( --pill-col, var( --gtl-ink ) );
	will-change: transform;
	/* Hoverable, so it can answer the pointer. The drag still belongs to the
	   track: a press here bubbles up to it exactly as one on the track does. */
	pointer-events: auto;
	overflow: hidden;
	transition: background-color .18s ease, color .18s ease;
}

.geeks-tl-pill:hover {
	background: var( --pill-bg-h, var( --pill-bg, #fff ) );
	color: var( --pill-col-h, var( --pill-col, var( --gtl-ink ) ) );
}

/* The handle's third state, and NOT :active.

   The moment the handle is pressed the track takes pointer capture, so the
   events — and with them :hover and :active — belong to the track. The pill
   would fall back to its resting colours half way through a drag, which is
   exactly what it did. The track wears a class for the length of the drag;
   that is what the held state hangs on. It is written after :hover and with
   more weight, because during a drag the pointer is over the handle too. */
.geeks-tl-track.dragging .geeks-tl-pill,
.geeks-tl-track.dragging .geeks-tl-pill:hover {
	background: var( --pill-bg-a, var( --pill-bg-h, var( --pill-bg, #fff ) ) );
	color: var( --pill-col-a, var( --pill-col-h, var( --pill-col, var( --gtl-ink ) ) ) );
}

.geeks-tl-pill:hover .geeks-tl-years span {
	color: inherit;
}

/* The strip takes the pill's own box, so a date can size itself against it —
   without a width of its own the vertical dates measured zero and vanished. */
.geeks-tl-years {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	will-change: transform;
}

.geeks-tl-years span {
	position: absolute;
	top: 0;
	height: 100%;
	display: grid;
	place-items: center;
	white-space: nowrap;
	transform: translateX( -50% );
}

.geeks-tl-slider.vertical .geeks-tl-years span {
	transform: translateY( -50% );
	writing-mode: vertical-rl;
	text-orientation: mixed;
}

.geeks-tl-slider.vertical .geeks-tl-pill {
	width: var( --inner-h, 40px );
	height: 66px;
	min-width: 0;
	font-size: 12px;
}

/* ---------- the types ---------- */
.geeks-tl-reel {
	display: flex;
	align-items: center;
	gap: 1.4em;
	overflow: hidden;
	height: 34px;
	-webkit-mask-image: linear-gradient( 90deg, transparent, #000 12%, #000 88%, transparent );
	mask-image: linear-gradient( 90deg, transparent, #000 12%, #000 88%, transparent );
	cursor: grab;
	user-select: none;
}

.geeks-tl-reel.dragging {
	cursor: grabbing;
}

.geeks-tl-reel.vertical {
	flex-direction: column;
	width: 34px;
	height: auto;
	-webkit-mask-image: linear-gradient( 180deg, transparent, #000 12%, #000 88%, transparent );
	mask-image: linear-gradient( 180deg, transparent, #000 12%, #000 88%, transparent );
}

.geeks-tl-reel.vertical .gtl-t {
	writing-mode: vertical-rl;
	text-orientation: mixed;
}

.geeks-tl .gtl-t {
	font-size: 13px;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var( --gtl-soft );
	white-space: nowrap;
	cursor: pointer;
	will-change: transform, opacity;
}

.geeks-tl .gtl-t.on {
	color: var( --gtl-ink );
}

/* ---------- the translator's copy ---------- */
.geeks-tl-i18n {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset( 50% );
	white-space: nowrap;
	pointer-events: none;
}

.geeks-tl-empty {
	padding: 2em;
	text-align: center;
	color: #6b7276;
	border: 1px dashed rgba( 47, 52, 55, .28 );
	border-radius: 8px;
}

@media ( prefers-reduced-motion: reduce ) {

	.geeks-tl .gtl-mark,
	.geeks-tl .gtl-disc,
	.geeks-tl-pill,
	.geeks-tl .gtl-t {
		transition: none !important;
	}
}
