/* =====================================================================
   LURETH GRAIL — style.css
   Eine Datei für die ganze Seite. Palette, Schriften und Atmosphäre sind
   1:1 aus dem alten WordPress-Theme übernommen (theme.json / main.css).
   ===================================================================== */

:root {
	--bg:        #090908;
	--text:      #ded3b6;
	--dim:       #9a9078;
	--dimmer:    #6b6350;
	/* Nur für Abschnitts-Überschriften: heller als --dim, damit sie sich
	   trotz der kleinen, weit gesperrten Schrift gut abheben. */
	--label:     #bcb298;
	--snow:      #e4ddd0;
	--red:       #7a1018;
	--red-b:     #c04030;
	--border:    #3d3928;
	/* Projektfarben. Violett stammt aus der alten Palette (theme.json),
	   Grün ist neu und bewusst genauso entsättigt wie die anderen. */
	--purple-b:  #8f6fb3;
	--green-b:   #5f9c62;
	--yellow-b:  #e2b93f;
	--orange-b:  #c86a24;
	/* Eigener Ton für die Social-Buttons, bewusst kein Alias auf --red-b:
	   die Optik stammt von phlox-ness.carrd.co und soll so bleiben. */
	--social:    #ff4747;

	--f-title: 'Avojaloin', Impact, sans-serif;
	--f-body:  'JMH Typewriter', 'Courier New', monospace;

	--pad:     clamp(1.25rem, 5vw, 3.5rem);
	--content: 780px;
}

/* Avojaloin ist auf GROSSBUCHSTABEN + Ziffern + Satzzeichen reduziert
   (45 KB -> 18 KB). Sie wird nur für Versal-Schriftzüge benutzt; wer hier
   Kleinbuchstaben setzt, bekommt Impact als Fallback. */
@font-face {
	font-family: 'Avojaloin';
	src: url('fonts/avojaloin-400.woff2') format('woff2');
	font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
	font-family: 'JMH Typewriter';
	src: url('fonts/jmh-typewriter-400.woff2') format('woff2');
	font-weight: 400; font-style: normal; font-display: swap;
}
/* Der fette Schnitt trägt nur, was auf der Seite wirklich fett gesetzt wird:
   Ziffern (Log-Daten), Großbuchstaben (Überschriften, Bild-Datum) und etwas
   Satzzeichen — 114 KB -> 14 KB. Kleinbuchstaben fehlen bewusst: sie kämen auf
   30 KB, und fett gesetzt wird auf dieser Seite nichts in Kleinschreibung.
   Wer das ändert, muss den Schnitt neu beschneiden, sonst ahmt der Browser
   die fette Schrift nach und es sieht in einer Zeile uneinheitlich aus. */
@font-face {
	font-family: 'JMH Typewriter';
	src: url('fonts/jmh-typewriter-700.woff2') format('woff2');
	font-weight: 700; font-style: normal; font-display: swap;
	unicode-range: U+0020, U+002D, U+002E, U+0030-0039, U+0041-005A, U+00C4, U+00D6, U+00DC;
}

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

body {
	margin: 0;
	background: var(--bg);
	color: var(--text);
	font: 400 13px/1.7 var(--f-body);
	letter-spacing: 0.01em;
	overflow-x: hidden;
	-webkit-text-size-adjust: 100%;
}
a { color: inherit; text-decoration: none; }
::selection { background: #3d0608; color: var(--snow); }
:focus-visible { outline: 1px solid var(--red-b); outline-offset: 3px; }

/* ----------------------------------------------------------------------
   ATMOSPHÄRE — Schnee-Canvas, Korn, Vignette, Cursor
   Korn und Vignette sind reines CSS (Daten-URI, kein zusätzlicher Request).
---------------------------------------------------------------------- */
#snow { position: fixed; inset: 0; z-index: 1; pointer-events: none; }

/* 116 % statt 300 %: die Keyframes verschieben nur um ±2 % der EIGENEN
   Größe, mehr Fläche kostet nur Compositing. numOctaves 2 — Oktave 3+ liegt
   unter einem Pixel und ist bei opacity 0.04 unsichtbar. */
#grain {
	position: fixed; inset: -8%; width: 116%; height: 116%;
	z-index: 2; pointer-events: none; opacity: 0.04;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
	animation: grain-shift 0.14s steps(1) infinite;
}
@keyframes grain-shift {
	0%   { transform: translate(0, 0); }
	25%  { transform: translate(-2%, -1%); }
	50%  { transform: translate(1%, 2%); }
	75%  { transform: translate(2%, -1%); }
	100% { transform: translate(-1%, 2%); }
}
#vignette {
	position: fixed; inset: 0; z-index: 3; pointer-events: none;
	background: radial-gradient(ellipse at 50% 35%, transparent 45%, rgba(4,4,3,0.5) 72%, rgba(4,4,3,0.9) 100%);
}

/* Cursor: Position läuft über die eigenständige `translate`-Property statt
   left/top (kein Layout pro Mausbewegung). `transform-origin: 0 0` ist
   Pflicht — die Properties greifen in der Reihenfolge translate → scale →
   transform, sonst skaliert `scale` das -50 %-Zentrieren mit. */
#cursor-ring, #cursor-dot { display: none; }
@media (pointer: fine) {
	body, a, button { cursor: none; }
	#cursor-ring, #cursor-dot {
		display: block; position: fixed; left: 0; top: 0;
		z-index: 9999; pointer-events: none;
		transform: translate(-50%, -50%); transform-origin: 0 0;
	}
	#cursor-ring {
		width: 18px; height: 18px; scale: 1;
		border: 1px solid var(--red-b); border-radius: 50%;
		mix-blend-mode: difference;
		transition: scale 0.1s ease, width 0.18s ease, height 0.18s ease, border-color 0.18s ease;
	}
	#cursor-ring.down { scale: 0.75; }
	#cursor-dot { width: 3px; height: 3px; border-radius: 50%; background: var(--snow); }
	body.hovering #cursor-ring { width: 30px; height: 30px; border-color: var(--snow); }
}

/* ----------------------------------------------------------------------
   SEITENRAHMEN
---------------------------------------------------------------------- */
main {
	position: relative; z-index: 10;
	max-width: var(--content);
	margin: 0 auto;
	padding: 0 var(--pad) clamp(9rem, 34vh, 20rem);   /* großzügiger Auslauf unten,
	   mit rem-Untergrenze: reines vh wäre auf sehr flachen Fenstern fast nichts */
}
section { margin-top: clamp(3.25rem, 8vw, 5rem); }

.eyebrow {
	margin: 0 0 1.4rem;
	font-size: 10px; font-weight: 700; letter-spacing: 0.45em;
	color: var(--label); text-transform: uppercase;
}
.eyebrow::before { content: "—"; color: var(--red-b); margin-right: 0.75em; }

/* ----------------------------------------------------------------------
   HERO — Schriftzug + Social. Sitzt bewusst weit oben.
---------------------------------------------------------------------- */
.hero { position: relative; padding: clamp(3rem, 7vh, 5.5rem) 0 0; }
/* Textur randlos über die volle Fensterbreite, hinter dem Schriftzug.
   Bei opacity 0.16 auf fast schwarzem Grund ist Detail ohnehin unsichtbar —
   deshalb reichen 700 px Bildbreite (39 KB). */
.hero::before {
	content: ""; position: absolute; z-index: -1;
	top: 0; left: 50%; width: 100vw; height: 300%;
	transform: translateX(-50%);
	/* Y-Position `top` = 0 %: das Bild ist bei `cover` viel höher als der
	   Kasten, die obere Kante liegt also an der Oberkante an und der ganze
	   Rest der Figur hängt nach UNTEN raus — so weit nach unten, wie es geht.
	   (100 % wäre das Gegenteil: Figur nach oben geschoben.) */
	background: url('img/shear-black-4.webp') 20% top / cover no-repeat;
	opacity: 0.16;
	/* Zwei Stellschrauben für „wie weit reicht das Bild nach unten":
	   `height` (wie hoch der Kasten ist) und der Ausblendpunkt (die 85 %).
	   Beide hoch = Bild läuft weit die Seite hinunter und geht erst spät weg. */
	-webkit-mask-image: linear-gradient(to bottom, #000 85%, transparent 100%);
	mask-image: linear-gradient(to bottom, #000 85%, transparent 100%);
}

/* Immer nebeneinander, nicht nur auf schmalen Schirmen — .line ist deshalb
   grundsätzlich inline, kein gestapeltes Layout mehr im Spiel. Das Leerzeichen
   zwischen den beiden Spans im Markup wird dabei zum Wortabstand.
   9.3vw ist gemessen, nicht geraten: der Schriftzug ist rund 6,8× so breit wie
   seine Schriftgröße. Bei 9.3vw bleibt das auf jeder Breite unter der
   verfügbaren Innenbreite von main (Content-Spalte 780px minus Padding) —
   geprüft von 320px bis 1920px. white-space:nowrap verhindert, dass er trotz
   knappem Platz doch umbricht. */
.name {
	margin: 0;
	font: 400 clamp(2.2rem, 9.3vw, 6rem)/0.84 var(--f-title);
	letter-spacing: -0.01em;
	white-space: nowrap;
	user-select: none;
	text-align: center;
}
.name .line { display: inline; }
.name .outline {
	color: transparent;
	-webkit-text-stroke: 1px var(--dim); text-stroke: 1px var(--dim);
}
.name .red { color: var(--red-b); }

/* Schreibmaschine: reines CSS, ohne JS-Klasse — eine erst nach dem Laden
   gesetzte Klasse würde den fertigen Schriftzug kurz aufblitzen lassen.
   --d steht an einer Stelle, damit die zweite Zeile ihren Wagenrücklauf
   (+220 ms) nur einmal bekommt. */
.name .l { animation: type 130ms cubic-bezier(0.2, 0.9, 0.3, 1) both; }
.name .line .l { --d: calc(var(--i) * 55ms); animation-delay: var(--d); }
.name .outline .l { --d: calc(var(--i) * 55ms + 220ms); }
@keyframes type {
	0%   { opacity: 0; transform: translateY(0.06em) scale(1.06); }
	1%   { opacity: 1; }
	100% { opacity: 1; transform: none; }
}

/* Social: gleiche Optik wie bisher (transparentes Quadrat, dünner Rahmen,
   Rahmen + Icon wechseln beim Hover auf Weiß), nur größer. */
.social {
	display: flex; flex-wrap: wrap; justify-content: center;
	gap: 8px; margin-top: 2.2rem;
}
.social a {
	display: flex; align-items: center; justify-content: center;
	width: 58px; height: 58px;
	border: 1px solid var(--social); color: var(--social);
	transition: color 0.25s, border-color 0.25s;
}
.social a:hover, .social a:focus-visible { color: var(--snow); border-color: var(--snow); }
.social svg { width: 30px; height: 30px; }
/* Beschriftung bleibt für Screenreader erhalten, nur optisch versteckt —
   display:none nähme dem Link seinen zugänglichen Namen. */
.social span { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ----------------------------------------------------------------------
   UPDATES (Wochenlog)
---------------------------------------------------------------------- */
.log { margin-top: clamp(2rem, 5vw, 3.2rem); }
/* Der Kasten sitzt an der Liste, nicht an der Sektion: solange log.txt leer
   ist, gibt build.py gar keine <ol> aus — dann steht hier auch kein leerer
   gestrichelter Rahmen herum. */
.entries {
	margin: 0; padding: clamp(1.25rem, 3.5vw, 2.1rem); list-style: none;
	background: #000;
	border: 1px dashed var(--dimmer);
}
.entries > li + li { margin-top: 1.9rem; }
.date {
	margin: 0 0 0.45rem;
	font: 700 clamp(15px, 2.2vw, 17px)/1.4 var(--f-body);
	letter-spacing: 0.06em;
	color: var(--text);
}
/* Die drei neuesten Daten laufen von Rot auf die normale Textfarbe aus.
   Alles ab dem vierten bleibt bei var(--text) (Regel oben). Der Hex-Wert ist
   der Mittelwert der beiden Farben und dient als Fallback für Browser ohne
   color-mix. */
.entries > li:nth-child(1) .date { color: var(--red-b); }
.entries > li:nth-child(2) .date {
	color: #cf8a73;
	color: color-mix(in srgb, var(--red-b) 50%, var(--text));
}
.entries > li:nth-child(3) .date { color: var(--text); }
.entries ul { margin: 0; padding: 0; list-style: none; }
.entries ul li {
	position: relative; padding-left: 1.15rem;
	color: var(--dim);
}
.entries ul li::before {
	content: "·"; position: absolute; left: 0.25rem; color: var(--dimmer);
}
/* Abschnittskopf: Überschrift links, Archiv-Link rechts auf gleicher Höhe.
   `baseline` statt `center`, damit die beiden Schriften auf einer Linie sitzen
   und nicht bloß ihre Kästen mittig zueinander stehen. */
.section-head {
	display: flex; align-items: baseline; justify-content: space-between;
	gap: 1rem; margin-bottom: 1.4rem;
}
.section-head .eyebrow { margin-bottom: 0; }
.more {
	flex: none;
	font-size: 11px; letter-spacing: 0.3em; color: var(--dim);
	border-bottom: 1px solid var(--border); padding-bottom: 0.35rem;
	transition: color 0.2s, border-color 0.2s;
}
.more:hover, .more:focus-visible { color: var(--snow); border-color: var(--red-b); }

/* ----------------------------------------------------------------------
   PROJEKTE (Game Dev, Tools, Astrofotos)
---------------------------------------------------------------------- */
.projects { margin: 0; padding: 0; list-style: none; }
.projects li { border-top: 1px solid var(--border); }
.projects li:last-child { border-bottom: 1px solid var(--border); }
.projects .row {
	display: block;            /* Name oben, Beschreibung darunter */
	padding: 1.05rem 0;
}
.p-name {
	font-family: var(--f-title); font-size: clamp(1.4rem, 4vw, 1.75rem);
	line-height: 1.1; letter-spacing: 0.03em; color: var(--text);
	text-transform: uppercase;   /* Avojaloin ist nur als Versalsatz geladen */
	transition: color 0.2s;
}
.p-name { display: block; }
.p-desc { display: block; margin-top: 0.35rem; color: var(--dim); font-size: 12px; }
/* Der Pfeil hängt am Namen statt an der Zeile — sonst stünde er seit dem
   Umbau auf Block-Layout unter der Beschreibung in einer eigenen Zeile.
   Eigene Schriftfamilie, weil die beschnittenen Hausschriften kein U+2192
   enthalten. */
.p-name::after {
	content: "→"; display: inline-block; margin-left: 0.45em;
	font-family: system-ui, sans-serif; font-size: 0.6em;
	color: var(--red-b);
	opacity: 0; transform: translateX(-4px);
	transition: opacity 0.2s, transform 0.2s;
}
a.row:hover .p-name::after, a.row:focus-visible .p-name::after {
	opacity: 1; transform: none;
}

/* Farbcodierung der Projektnamen. Was keine Klasse hat (Nachthimmel), bleibt
   auf var(--text) aus .p-name. */
.c-red    { color: var(--red-b); }
.c-purple { color: var(--purple-b); }
.c-green  { color: var(--green-b); }
/* „LEECHFLOWER" läuft von Gelb nach Orange. background-clip: text braucht
   color: transparent — deshalb kein einfaches `color` wie bei den anderen. */
.c-leech {
	background: linear-gradient(95deg, var(--yellow-b), var(--orange-b));
	-webkit-background-clip: text; background-clip: text;
	color: transparent;
}
/* Beim Hover wird aufgehellt statt umgefärbt: eine Farbumstellung würde die
   Codierung zunichtemachen, und beim Verlauf greift `color` ohnehin nicht. */
a.row:hover .p-name, a.row:focus-visible .p-name { filter: brightness(1.3); }
a.row:hover::after, a.row:focus-visible::after { opacity: 1; transform: none; }

/* Kontakt: bewusst NICHT .p-name — das ist auf Versalien gestellt, und
   Avojaloin hat gar keine Kleinbuchstaben. Eine E-Mail in Großbuchstaben wäre
   schwer zu lesen und zum Abtippen unbrauchbar. */
.mail {
	font-family: var(--f-body); font-size: clamp(14px, 3.2vw, 18px);
	letter-spacing: 0.02em; color: var(--purple-b);
	transition: color 0.2s;
}
a.row:hover .mail, a.row:focus-visible .mail { filter: brightness(1.25); }

/* ----------------------------------------------------------------------
   ZEICHNUNGEN — Raster + Vollbild-Ansicht
---------------------------------------------------------------------- */
/* auto-fill + minmax: die Spaltenzahl ergibt sich aus der Breite, ganz ohne
   Media Queries. Auf der Startseite (780 px) sind das drei Spalten, auf dem
   Handy eine. */
.gallery {
	display: grid; gap: clamp(0.6rem, 2vw, 1rem);
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
	margin: 0; padding: 0; list-style: none;
}
.gallery figure { margin: 0; }
.gallery img {
	display: block; width: 100%; height: auto;
	aspect-ratio: 1 / 1; object-fit: cover;
	background: #000;
	border: 1px solid var(--border);
	cursor: zoom-in;
	transition: border-color 0.2s, opacity 0.2s;
}
.gallery img:hover { border-color: var(--red-b); opacity: 0.88; }
.gallery figcaption {
	margin-top: 0.45rem;
	font-size: 10px; font-weight: 700; letter-spacing: 0.16em; color: var(--dim);
	text-align: center; text-transform: uppercase;
}

/* Vollbild: liegt über allem, auch über dem Korn (z-index 2) und der
   Vignette (3). `dialog` bringt Escape-zum-Schließen von Haus aus mit. */
#lightbox {
	position: fixed; inset: 0; z-index: 10000;
	/* Größe kommt ausschließlich aus inset: 0 — bewusst NICHT aus 100vw/100vh.
	   `width/height: auto` überschreibt das `fit-content` der Browser-Vorgabe
	   für <dialog>, damit die Ränder die Größe bestimmen. vw/vh wären hier
	   zweimal falsch: 100vw ignoriert eine sichtbare Scrollleiste (waagerechtes
	   Überlaufen), und 100vh meint auf Mobilgeräten die Höhe OHNE die
	   ein- und ausfahrende Adressleiste. */
	width: auto; height: auto; max-width: none; max-height: none;
	margin: 0; padding: 0; border: 0;
	background: transparent;
	overflow: hidden;
}
/* Nur ein Hauch Abdunklung — die Seite dahinter soll sichtbar bleiben. */
#lightbox::backdrop { background: rgba(4, 4, 3, 0.3); }
/* Der Rahmen füllt das Fenster und ist selbst die Klickfläche zum Schließen —
   deshalb reicht ein Klick irgendwo neben das Bild. */
#lightbox .lb-stage {
	display: flex; align-items: center; justify-content: center;
	width: 100%; height: 100%;
	padding: clamp(1rem, 4vw, 3rem);
	cursor: zoom-out;
}
#lightbox img {
	max-width: 100%; max-height: 100%;
	width: auto; height: auto;
	object-fit: contain;
	cursor: default;      /* auf dem Bild selbst schließt es NICHT */
	border: 1px solid var(--border);
}
/* Abstand zur Ecke statt bündig; eigener dunkler Grund, weil der Hintergrund
   jetzt kaum abgedunkelt ist und das ✕ sonst auf hellen Bildern verschwindet.
   Quadrat mit dünnem Rahmen — dieselbe Sprache wie die Social-Buttons. */
#lightbox .lb-close {
	position: absolute;
	top: clamp(0.75rem, 2.5vw, 1.75rem);
	right: clamp(0.75rem, 2.5vw, 1.75rem);
	display: flex; align-items: center; justify-content: center;
	width: 40px; height: 40px; padding: 0;
	background: rgba(9, 9, 8, 0.72);
	border: 1px solid var(--red-b);
	font: 400 17px/1 var(--f-body); color: var(--red-b);
	cursor: pointer;
	transition: color 0.2s, border-color 0.2s, background-color 0.2s;
}
#lightbox .lb-close:hover, #lightbox .lb-close:focus-visible {
	color: var(--snow); border-color: var(--red-b); background: rgba(9, 9, 8, 0.9);
}
/* Solange das Vollbild offen ist, soll die Seite dahinter nicht mitscrollen.
   Bewusst über :has() am Dialog selbst statt über eine per JS gesetzte Klasse:
   das kann nicht auseinanderlaufen. Beim Schließen per Escape feuert das
   `close`-Ereignis nicht überall zuverlässig (hier gemessen: gar nicht) — eine
   JS-Klasse wäre dann hängen geblieben und die Seite dauerhaft gesperrt. */
body:has(#lightbox[open]) { overflow: hidden; }

/* ----------------------------------------------------------------------
   UNTERSEITEN (Archiv, Astrofotos)
---------------------------------------------------------------------- */
.page-head { padding: clamp(2.5rem, 6vh, 4rem) 0 0; }
.page-title {
	margin: 0.9rem 0 0;
	font: 400 clamp(2.6rem, 9vw, 4.5rem)/0.9 var(--f-title);
	letter-spacing: 0.01em; color: var(--text);
	text-transform: uppercase;   /* Avojaloin ist nur als Versalsatz geladen */
}
.back {
	display: inline-block;
	font-size: 10px; letter-spacing: 0.35em; color: var(--dimmer);
	transition: color 0.2s;
}
.back:hover, .back:focus-visible { color: var(--snow); }

figure { margin: 0 0 clamp(2.5rem, 6vw, 4rem); }
figure img {
	display: block; width: 100%; height: auto;
	border: 1px solid var(--border);
}
figcaption {
	margin-top: 0.7rem;
	font-size: 11px; letter-spacing: 0.25em; color: var(--dim);
}

/* ----------------------------------------------------------------------
   BARRIEREFREIHEIT
---------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
	#grain { animation: none; }
	.name .l { animation: none; opacity: 1; transform: none; }
	* { transition-duration: 0.01ms !important; }
}
