/* ==========================================================================
   Shell, floating rail, control dock, footer.

   Full-bleed layout. Readability is protected at the text element, not the
   container — prose keeps the measure while tables, photography and code use
   the full width via .bleed.

   The rail and dock are floating surfaces: rounded, shadowed to the right and
   bottom, detached from the document flow.
   ========================================================================== */

/* Utility ------------------------------------------------------------------ */

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

.screen-reader-text:focus {
	position: static !important;
	width: auto;
	height: auto;
	clip-path: none;
	margin: 0;
}

/* Shell -------------------------------------------------------------------- */

.shell {
	/* Sticky footer: the shell always fills the viewport, the content column
	   takes the slack, and the footer is pushed to the bottom on short pages
	   while flowing normally on long ones. */
	display: flex;
	flex-direction: column;
	min-height: 100vh;
	min-height: 100svh;
	/* No horizontal padding: containers must be able to run edge to edge so
	   their backgrounds and transitions span the full viewport. The inset that
	   clears the rail lives on the content inside each container instead. */
	padding-inline: 0;
	padding-block: 0;
}

.column {
	flex: 1 0 auto;
	padding-block: var(--section-gap);
	padding-inline: var(--content-inset) var(--gutter);
	min-width: 0;
}

/* Elementor pages: the wrapper must NOT inset, or top-level containers can
   never reach the viewport edges and a full-width background is impossible.
   The inset moves onto the containers themselves — see below.

   padding-block-start is zeroed too: the sections own their vertical rhythm,
   and the wrapper's --section-gap was pushing the first panel down the page. */
.elementor-page .column,
.elementor-page .column > .site-main {
	padding-inline: 0;
	padding-block-start: 0;
}

/* Every top-level Elementor container runs edge to edge and insets its own
   content. `.e-parent` is the top-level marker; nested containers are left
   alone so they inherit their parent's inset rather than doubling it. */
.elementor-page .e-con.e-parent {
	padding-inline: var(--content-inset) var(--gutter);
	/* The Elementor kit caps containers at 1140px via --container-max-width.
	   Overriding max-width alone is not enough — anything else consuming that
	   variable would still cap — so the variable itself is neutralised here. */
	--container-max-width: 100%;
	--content-width: 100%;
	max-width: none;
	width: 100%;
}

@media (max-width: 61.99em) {
	.elementor-page .e-con.e-parent { padding-inline: var(--gutter); }
}
.column > .site-main { max-width: 100%; }

.colophon {
	/* margin-block-start:auto is what pins the footer to the bottom on short
	   pages — the flex parent hands it all the leftover space. */
	margin-block-start: auto;
	padding-inline: var(--content-inset) var(--gutter);
	/* The dock is fixed at the bottom centre on wide screens and would
	   otherwise sit on top of the last footer row. Reserve its height. */
	padding-block: var(--s-7) calc(var(--s-9) + 3rem);
	border-top: 1px solid var(--rule);
}

/* Prose keeps the measure. Wide content opts out. */
.column p,
.column li,
.column blockquote { max-width: var(--measure); }

.bleed,
.bleed p,
.bleed li { max-width: none; }

/* Rail — floating, vertically centred ------------------------------------- */

.rail {
	position: fixed;
	inset-inline-start: var(--s-4);
	inset-block-start: 50%;
	transform: translateY(-50%);
	z-index: 60;
}

.rail__inner {
	display: flex;
	flex-direction: column;
	gap: var(--s-5);
	width: var(--rail-collapsed);
	max-height: calc(100vh - var(--s-8));
	padding: var(--s-5) var(--s-4);
	background: var(--surface);
	border: 1px solid var(--rule);
	border-radius: var(--radius);
	box-shadow: var(--shadow-rb);
	overflow: hidden;
	transition:
		width var(--motion-base) var(--ease),
		background-color var(--motion-base) var(--ease),
		box-shadow var(--motion-base) var(--ease);
}

.rail__inner:hover,
.rail__inner:focus-within { width: var(--rail-expanded); }

/* Identity ----------------------------------------------------------------- */

.rail__identity {
	display: flex;
	align-items: center;
	margin: 0;
	max-width: none;
}

.rail__name {
	font-family: var(--font-display);
	font-weight: 600;
	font-size: var(--body-sm);
	letter-spacing: var(--track-label);
	text-transform: uppercase;
	color: var(--ink);
	white-space: nowrap;
	writing-mode: vertical-rl;
	transform: rotate(180deg);
}

a.rail__name { text-decoration: none; }
a.rail__name:hover { text-decoration: underline; text-underline-offset: 0.18em; }

.rail__inner:hover .rail__name,
.rail__inner:focus-within .rail__name {
	writing-mode: horizontal-tb;
	transform: none;
}

/* Axis — instrument ticks -------------------------------------------------- */

/* The rail is height-capped and overflow-hidden so the width animation does
   not leak. That silently swallowed nav entries once the list grew past the
   available height, so the axis scrolls on its own instead. */
.rail__axis {
	min-height: 0;
	flex: 1 1 auto;
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: thin;
}

.rail__axis::-webkit-scrollbar { width: 3px; }
.rail__axis::-webkit-scrollbar-thumb { background: var(--rule); border-radius: 3px; }

.rail__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--s-3);
}

.rail__list + .rail__list {
	margin-top: var(--s-4);
	padding-top: var(--s-4);
	border-top: 1px solid var(--rule);
}

.rail__entry { max-width: none; }

.rail__entry a {
	display: flex;
	align-items: center;
	gap: var(--s-3);
	text-decoration: none;
	color: var(--muted);
	white-space: nowrap;
}

.rail__entry a:hover { color: var(--ink); }

.rail__tick {
	flex: 0 0 auto;
	width: 1rem;
	height: 1px;
	background: currentColor;
	transition:
		width var(--motion-base) var(--ease),
		height var(--motion-base) var(--ease),
		background-color var(--motion-base) var(--ease);
}

.rail__label {
	/* Lowercase in the menu. .label uppercases globally, so the rail opts out
	   rather than the utility class changing everywhere. */
	text-transform: lowercase;
	opacity: 0;
	transition: opacity var(--motion-fast) var(--ease);
}

.rail__inner:hover .rail__label,
.rail__inner:focus-within .rail__label { opacity: 1; }

/* Current section. The tick changes size as well as colour, so colour is
   never the only signal. */
.rail__entry a[aria-current] .rail__tick {
	width: 1.75rem;
	height: 3px;
	background: var(--signal);
}

.rail__entry a[aria-current] { color: var(--ink); }

/* Live monitor ------------------------------------------------------------- */

.rail__monitor {
	padding-top: var(--s-4);
	border-top: 1px solid var(--rule);
}

.rail__monitor-head {
	margin: 0 0 var(--s-2);
	max-width: none;
	opacity: 0;
	white-space: nowrap;
	transition: opacity var(--motion-fast) var(--ease);
}

.rail__monitor-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--s-2);
}

.rail__monitor-item {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	max-width: none;
	white-space: nowrap;
	color: var(--muted);
}

.rail__monitor-dot {
	flex: 0 0 7px;
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--muted);
	transition: background-color var(--motion-base) var(--ease);
}

/* Unknown until the endpoint answers — never a false "up". */
.rail__monitor-item.is-up .rail__monitor-dot { background: var(--signal); }
.rail__monitor-item.is-down .rail__monitor-dot { background: var(--alert); }

.rail__monitor-item.is-up .rail__monitor-dot {
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--signal) 22%, transparent);
}

.rail__monitor-label,
.rail__monitor-value {
	opacity: 0;
	transition: opacity var(--motion-fast) var(--ease);
}

.rail__monitor-value { margin-inline-start: auto; color: var(--ink); }

.rail__inner:hover .rail__monitor-head,
.rail__inner:hover .rail__monitor-label,
.rail__inner:hover .rail__monitor-value,
.rail__inner:focus-within .rail__monitor-head,
.rail__inner:focus-within .rail__monitor-label,
.rail__inner:focus-within .rail__monitor-value { opacity: 1; }

/* Control dock — floating, bottom centre ----------------------------------- */

.dock {
	position: fixed;
	inset-block-end: var(--s-5);
	inset-inline-start: 50%;
	transform: translateX(-50%);
	z-index: 70;
	display: flex;
	align-items: center;
	gap: var(--s-4);
	padding: var(--s-3) var(--s-5);
	background: var(--surface);
	border: 1px solid var(--rule);
	border-radius: var(--radius);
	box-shadow: var(--shadow-rb);
	font-family: var(--font-data);
	font-size: var(--mono-data);
}

.dock__btn {
	display: flex;
	align-items: center;
	gap: var(--s-2);
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	color: var(--muted);
	font: inherit;
	cursor: pointer;
	white-space: nowrap;
}

.dock__btn:hover { color: var(--ink); }

.dock__dot {
	width: 7px;
	height: 7px;
	flex: 0 0 7px;
	border: 1px solid var(--signal);
	border-radius: 50%;
}

:root[data-theme="dark"] .dock__dot { background: var(--signal); }

@media (prefers-color-scheme: dark) {
	:root:not([data-theme="light"]) .dock__dot { background: var(--signal); }
}

.dock__sep {
	width: 1px;
	height: 1rem;
	background: var(--rule);
}

.dock__lang { display: flex; align-items: center; gap: var(--s-2); }

.dock__lang-item { color: var(--muted); text-decoration: none; }
.dock__lang-item:hover { color: var(--ink); }
.dock__lang-item.is-active { color: var(--signal); }

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

.colophon__contact {
	list-style: none;
	margin: 0 0 var(--s-5);
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-5);
}

.colophon__contact li { max-width: none; }

.colophon__meta {
	margin: 0;
	max-width: none;
	display: flex;
	align-items: center;
	gap: var(--s-2);
}

.colophon__bolt { font-size: 1em; line-height: 1; }

/* Touch, wide viewport ------------------------------------------------------
   No hover means no way to expand, so the rail rests open. */

@media (hover: none) and (min-width: 62em) {
	:root { --content-inset: calc(var(--rail-expanded) + var(--gutter)); }
	.rail__inner { width: var(--rail-expanded); }
	.rail__name { writing-mode: horizontal-tb; transform: none; }
	.rail__label,
	.rail__monitor-head,
	.rail__monitor-label,
	.rail__monitor-value { opacity: 1; }
}

/* Narrow ------------------------------------------------------------------- */
/* The floating rail becomes a sticky bar; the dock stays where it is. */

@media (max-width: 61.99em) {

	/* Rail becomes a top bar, so nothing has to be cleared on the left. */
	:root { --content-inset: var(--gutter); }

	.rail {
		position: sticky;
		inset-block-start: var(--s-2);
		inset-inline-start: auto;
		transform: none;
		padding-inline: var(--gutter);
	}

	.rail__inner {
		flex-direction: row;
		align-items: center;
		justify-content: space-between;
		gap: var(--s-4);
		width: auto;
		max-height: none;
		min-height: var(--rail-bar-h);
		padding: var(--s-2) var(--s-4);
		overflow-x: auto;
	}

	.rail__inner:hover,
	.rail__inner:focus-within { width: auto; }

	.rail__name { writing-mode: horizontal-tb; transform: none; }

	/* Section list does not fit one line; the readout replaces it and every
	   anchor stays reachable from the page body. */
	.rail__axis { display: none; }

	.rail__monitor {
		padding-top: 0;
		border-top: 0;
		margin-inline-start: auto;
	}

	.rail__monitor-head { display: none; }
	.rail__monitor-list { flex-direction: row; gap: var(--s-3); }
	.rail__monitor-label { display: none; }
	.rail__monitor-value { opacity: 1; margin-inline-start: 0; }
	.rail__monitor-item { gap: var(--s-1); }

	/* The dock stops floating and takes its place in the document, directly
	   above the footer. Fixed position on a small screen costs vertical space
	   the page cannot spare, and it stacked with the cookie notice and the CLI. */
	.dock {
		position: static;
		transform: none;
		inset: auto;
		width: 100%;
		justify-content: center;
		margin-block: var(--s-6) 0;
		padding: var(--s-3) var(--s-4);
	}

	/* Nothing fixed is left at the bottom, so the reserved space goes away. */
	.colophon { padding-block-end: var(--s-7); }

	:target,
	[id] { scroll-margin-top: calc(var(--rail-bar-h) + var(--s-5)); }
}

/* Nav tree — folders and files ---------------------------------------------
   The rail is a file listing. Icons are folder/file only: unambiguous, and
   the one place an icon says something a word would not say faster. */

.ico {
	flex: 0 0 auto;
	opacity: 0.75;
	transition: opacity var(--motion-fast) var(--ease), color var(--motion-base) var(--ease);
}

.rail__entry a:hover .ico { opacity: 1; }
.rail__entry--dir > a .ico { color: var(--signal); }
.rail__entry a[aria-current] .ico { color: var(--signal); opacity: 1; }

/* Nested children indent under their folder, with a guide line. */
.rail__list--depth-1 {
	margin-block-start: var(--s-3);
	margin-inline-start: 0;
	padding-inline-start: 0;
	gap: var(--s-3);
}

/* Collapsed, only the icons show — so the tree reads as a column of glyphs
   and the indent must not push children out of the 4rem rail. */
.rail__inner:not(:hover):not(:focus-within) .rail__list--depth-1 {
	margin-inline-start: 0;
	padding-inline-start: 0;
	border-inline-start: 0;
}

.rail__entry--dir > a .rail__label { color: var(--ink); }

/* Tree connectors ----------------------------------------------------------
   Drawn with real characters rather than borders, because this is a tree
   listing and the glyphs are the notation. */

.rail__branch {
	flex: 0 0 auto;
	color: var(--rule);
	font-family: var(--font-data);
	font-size: var(--mono-data);
	line-height: 1;
	letter-spacing: 0;
	opacity: 0;
	transition: opacity var(--motion-fast) var(--ease);
}

.rail__inner:hover .rail__branch,
.rail__inner:focus-within .rail__branch { opacity: 1; }

/* The vertical run of the tree, drawn once per nested list. */
.rail__list--depth-1 { position: relative; }

.rail__inner:hover .rail__list--depth-1,
.rail__inner:focus-within .rail__list--depth-1 { border-inline-start-color: var(--rule); }

.rail__entry a { gap: var(--s-2); }

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

/* Five entries with icons wrap on narrow viewports; keep the rows readable
   rather than letting them collapse into one dense line. */
.colophon__contact { row-gap: var(--s-3); }

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

.ico--profile { opacity: 0.7; transition: opacity var(--motion-fast) var(--ease); }
.colophon__contact a:hover .ico--profile { opacity: 1; }

.colophon__contact a {
	display: inline-flex;
	align-items: center;
	gap: var(--s-2);
}

/* Path readout -------------------------------------------------------------
   Shows the current location as a path — /ueber-mich/arbeit — with a prompt
   prefix so it reads as a location rather than a label. Populated by rail.js;
   ships [hidden] so it never appears empty. */

.rail__current {
	display: flex;
	align-items: baseline;
	gap: 0.6ch;
	margin: 0;
	max-width: none;
	min-width: 0;
	color: var(--muted);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	padding-block-start: var(--s-3);
	border-top: 1px solid var(--rule);
	opacity: 0;
	transition: opacity var(--motion-fast) var(--ease);
}

.rail__current::before {
	content: "$>";
	flex: 0 0 auto;
	color: var(--signal);
	font-family: var(--font-data);
}

/* Collapsed rail is 4rem wide — a path cannot fit, so it only appears once the
   rail is open. */
.rail__inner:hover .rail__current,
.rail__inner:focus-within .rail__current { opacity: 1; }

@media (hover: none) and (min-width: 62em) {
	.rail__current { opacity: 1; }
}

@media (max-width: 61.99em) {
	/* On the mobile bar the readout is the only navigation feedback there is,
	   so it is always visible and carries no divider. */
	.rail__current {
		opacity: 1;
		padding-block-start: 0;
		border-top: 0;
		flex: 1 1 auto;
		justify-content: flex-end;
	}
}

/* Marvy / WebGL background canvases -----------------------------------------
   Marvy sets position:absolute, top, width, height and z-index inline on its
   canvas but never sets `left`. An absolutely positioned box with no inline
   offset keeps its static position — which here is after the container's left
   padding, so the animation starts where the text starts instead of at the
   container edge, and width:100% then overflows the right.

   Setting the inline offset is enough; width and height are already correct
   relative to the padding box. No !important needed because Marvy does not
   set `left` at all. */
.elementor-page .e-con > canvas,
.elementor-page .e-con-inner > canvas {
	left: 0;
	right: 0;
	inset-inline: 0;
}

/* The canvas ships z-index 1, which would sit over the copy. Content wins. */
.elementor-page .e-con > .elementor-element,
.elementor-page .e-con > .e-con-inner {
	position: relative;
	z-index: 2;
}

/* Legal row ----------------------------------------------------------------
   Below the contact links, quieter than them: obligations, not invitations. */

.colophon__legal {
	list-style: none;
	margin: 0 0 var(--s-5);
	padding: var(--s-4) 0 0;
	border-top: 1px solid var(--rule);
	display: flex;
	flex-wrap: wrap;
	gap: var(--s-2) var(--s-5);
}

.colophon__legal li { max-width: none; }
.colophon__legal a { color: var(--muted); }
.colophon__legal a:hover { color: var(--ink); }
