diff --git a/index.html b/index.html index d2bcdf9..2147e4c 100644 --- a/index.html +++ b/index.html @@ -3,218 +3,234 @@ - ByteGeist Cloud + + ByteGeist — Self-hosted infrastructure + -
+
-

Self-Hosted Infrastructure Platform

-

Infrastructure, security, monitoring, and automation built from scratch.

-

- ByteGeist Cloud is a self-hosted infrastructure platform built on Ubuntu, Docker, - Authentik, Grafana, Loki, Prometheus, CrowdSec, Gitea, and Woodpecker CI. +

Home lab / ongoing project

+

A production-minded cloud, running on my own hardware.

+

+ ByteGeist is where I test infrastructure decisions for real: identity, observability, + deployments, security, and recovery. It currently runs 26 Docker containers on Ubuntu.

-
- ByteGeist cyber ghost logo -
+
+ ByteGeist operations dashboard in Grafana +
+ Live operations view + Grafana / Prometheus / cAdvisor +
+
-
-
26Containers
-
20+Services
-
11Protected Apps
-
SSOAuthentik
-
CI/CDWoodpecker
+
+
0126 containersdeployed with Compose
+
02Central identitySSO and 2FA via Authentik
+
03Full observabilitymetrics, logs, and uptime
+
04Documented recoverytested restore procedures
-
-
-

Architecture Overview

-

Designed like a small production platform.

-

- Traffic flows through Nginx Proxy Manager, access is controlled through Authentik, - and the environment is monitored, logged, secured, backed up, and documented. -

- ByteGeist architecture diagram -
- -
-

About ByteGeist

-

Built to prove real infrastructure skills.

-
    -
  • Centralized SSO and 2FA
  • -
  • Monitoring and log aggregation
  • -
  • Automated CI/CD pipelines
  • -
  • Threat detection and response
  • -
  • Backups and disaster recovery
  • -
  • Technical documentation
  • -
-
-
- -
-

Operations

-

Monitoring & Logging

-
-
- Grafana operations dashboard -

Infrastructure Monitoring

-

Grafana dashboards show uptime, CPU, memory, disk, container metrics, and system health.

-
- -
- Grafana logs dashboard -

Centralized Logging

-

Loki and Grafana provide searchable logs for containers and infrastructure troubleshooting.

-
-
-
- -
-

Security

-

Identity & Threat Protection

-
-
- Authentik application portal -

Centralized Identity

-

Authentik provides SSO, OAuth2, OIDC, Forward Auth, access control, and 2FA.

-
- -
- CrowdSec decisions list -

Threat Detection & Response

-

CrowdSec detects malicious SSH activity and creates firewall decisions to block attackers.

-
-
-
- -
-

Development

-

Git Hosting & CI/CD

-
-
- Gitea dashboard -

Self-Hosted Git

-

Gitea hosts infrastructure code, documentation, project files, and portfolio development.

-
- -
- Woodpecker successful pipeline -

CI/CD Automation

-

Woodpecker runs automated pipelines connected to Gitea repositories.

-
-
-
- -
-

Platform

-

Service Portal

-
-
- Dashy overview -

Operations Portal

-

Dashy provides quick access to infrastructure, observability, and development services.

-
- -
- Dashy services -

Service Catalog

-

Organized access to security tools, utilities, file management, and public sites.

-
-
-
- -
-

Recovery

-

Documentation & Disaster Recovery

-
- Disaster recovery documentation +
+
+

01 / Architecture

-

Recovery Planning

+

One server, treated like a platform.

- ByteGeist includes documented restore procedures, backup validation, service inventory, - DNS information, recovery priorities, and full environment rebuild planning. + Public traffic terminates at Nginx Proxy Manager. Authentik handles identity before + requests reach protected services. Metrics, logs, CI, and backups remain separate + concerns, so each can fail or change without taking the whole stack with it.

-
-
-

Technical Challenges

-

Problems Solved

-
-
-

OIDC Integration

-

Resolved redirect URI mismatches, user provisioning, and authorization issues.

-
-
-

Forward Auth

-

Integrated Authentik Forward Auth with reverse proxy protected services.

-
-
-

Observability

-

Built centralized metrics and logging using Grafana, Prometheus, Loki, and Alloy.

-
-
-

Security Hardening

-

Implemented SSH key-only login, Fail2Ban, CrowdSec, UFW, TLS, and SSO.

-
+
+ Detailed ByteGeist infrastructure architecture diagram +
Current architecture — updated as services are added or retired.
+
+ +
+
+ Access +

Authenticate once

+

OIDC where supported; forward auth for services that do not speak it natively.

+
+
+ Operations +

Make failures visible

+

Host and container metrics, centralized logs, and independent uptime checks.

+
+
+ Recovery +

Assume the host is replaceable

+

Configuration lives in Git. Persistent data is backed up with rebuild notes.

+
-
-

Core Skills Demonstrated

-
- Linux Administration - Docker & Compose - Reverse Proxies - Networking - TLS / SSL - DNS Management - Identity Management - SSO / OAuth2 / OIDC - Monitoring & Observability - Centralized Logging - CI/CD Automation - Security Hardening - Threat Detection & Response - Backup Strategy - Disaster Recovery - Technical Documentation +
+
+

02 / Selected systems

+
+

The parts I spend time on.

+

Not a catalog of logos—a record of what is running, why it exists, and what I learned building it.

+
+ +
+
+

01

+ +

Useful signals before pretty dashboards.

+

+ Prometheus collects host and container metrics while Loki receives logs through + Grafana Alloy. The dashboards answer practical questions: what is down, what changed, + and which container is consuming the host. +

+
    +
  • Grafana
  • Prometheus
  • Loki
  • Alloy
  • +
+
+
+ Grafana infrastructure metrics dashboard + Centralized container logs in Grafana +
+
+ +
+
+

02

+ +

One identity layer, fewer exposed edges.

+

+ Authentik provides SSO, OIDC, forward auth, and 2FA across eleven applications. + CrowdSec watches hostile traffic and creates firewall decisions alongside key-only SSH, + UFW, and TLS. +

+
    +
  • Authentik
  • CrowdSec
  • UFW
  • Let's Encrypt
  • +
+
+
+ Applications protected by Authentik + CrowdSec security decisions +
+
+ +
+
+

03

+ +

Code and delivery stay in-house.

+

+ Gitea stores infrastructure code and documentation. Woodpecker runs repository-linked + pipelines, including the build and deployment path for this site. +

+
    +
  • Gitea
  • Woodpecker CI
  • Docker
  • Git
  • +
+
+
+ Self-hosted Gitea dashboard + Successful Woodpecker CI pipeline +
+
+ +
+
+

04

+ +

The boring work is part of the system.

+

+ Dashy keeps service access legible. The wiki records inventory, DNS, restore order, + backup validation, and the steps required to rebuild the environment from a clean host. +

+
    +
  • Dashy
  • Wiki.js
  • Restic
  • Runbooks
  • +
+
+
+ ByteGeist internal service portal + ByteGeist disaster recovery runbook +
+
+
+ +
+
+

03 / Lessons

+
+

What actually took work.

+
+
+ +
+
+ 01 +

OIDC is mostly careful bookkeeping.

+

Redirect URIs, claims, group mapping, and user provisioning all need to agree across both ends.

+
+
+ 02 +

Backups are unfinished until restore is documented.

+

I track data location, dependencies, restore order, DNS, certificates, and validation—not just archive jobs.

+
+
+ 03 +

Observability has to support a decision.

+

A smaller dashboard that explains an incident is more useful than a wall of metrics nobody checks.

+
+
+ 04 +

Convenience can weaken the boundary.

+

Every exposed service has a cost. SSO, least privilege, and fewer public endpoints keep that cost explicit.

+
+
+
+ +
+

Project notes

+

This environment is still changing.

+

+ I use ByteGeist to practice the parts of infrastructure work that tutorials tend to skip: + upgrades, failure modes, access policy, noisy logs, and recovery. +

+ Open the documentation ↗
- \ No newline at end of file + diff --git a/style.css b/style.css index a299de1..8a9c42c 100644 --- a/style.css +++ b/style.css @@ -1,17 +1,16 @@ :root { - --bg: #05060b; - --bg-soft: #0a0d16; - --panel: rgba(14, 18, 30, 0.82); - --panel-strong: rgba(18, 15, 30, 0.94); - --border: rgba(159, 96, 255, 0.24); - --border-bright: rgba(159, 96, 255, 0.55); - --text: #f4f1ff; - --muted: #bcb3d8; - --purple: #a46cff; - --purple-2: #7c3cff; - --green: #61ffb1; - --blue: #4aa3ff; - --orange: #ff7a3d; + --bg: #0b0c0e; + --surface: #111316; + --surface-2: #17191d; + --line: #292c31; + --line-light: #3a3e45; + --text: #edede8; + --muted: #a4a6aa; + --dim: #6f7379; + --accent: #a986e8; + --accent-soft: #c6adef; + --green: #81b89b; + --max: 1180px; } * { @@ -24,451 +23,666 @@ html { body { margin: 0; - font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; - background: - radial-gradient(circle at top left, rgba(124, 60, 255, 0.22), transparent 28rem), - radial-gradient(circle at 80% 20%, rgba(74, 163, 255, 0.09), transparent 24rem), - linear-gradient(180deg, #05060b 0%, #080713 45%, #05060b 100%); color: var(--text); + background: var(--bg); + font-family: "Segoe UI", Arial, sans-serif; + font-size: 16px; line-height: 1.6; } -body::before { - content: ""; - position: fixed; - inset: 0; - pointer-events: none; - background-image: - linear-gradient(rgba(164, 108, 255, 0.035) 1px, transparent 1px), - linear-gradient(90deg, rgba(164, 108, 255, 0.035) 1px, transparent 1px); - background-size: 42px 42px; - mask-image: linear-gradient(to bottom, black, transparent 75%); -} - a { color: inherit; text-decoration: none; } img { + display: block; max-width: 100%; } .site-header { - position: sticky; - top: 0; - z-index: 50; + width: min(calc(100% - 48px), var(--max)); height: 76px; - padding: 0 42px; + margin: 0 auto; display: flex; align-items: center; - gap: 32px; - border-bottom: 1px solid rgba(159, 96, 255, 0.16); - background: rgba(5, 6, 11, 0.82); - backdrop-filter: blur(18px); + border-bottom: 1px solid var(--line); } .brand { display: flex; align-items: center; - gap: 14px; + gap: 10px; margin-right: auto; - font-weight: 900; + font-size: 0.84rem; + font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; } .brand img { - width: 42px; - height: 42px; - object-fit: contain; + width: 32px; + height: 32px; + object-fit: cover; + object-position: 50% 25%; + border-radius: 50%; } .site-header nav { display: flex; - gap: 26px; - font-size: 0.78rem; - font-weight: 800; - text-transform: uppercase; - letter-spacing: 0.08em; - color: #ded6ff; + gap: 28px; + margin-right: 32px; + color: var(--muted); + font-size: 0.86rem; } -.site-header nav a:hover { - color: var(--green); +.site-header nav a, +.text-link { + transition: color 160ms ease; } -.nav-button { - border: 1px solid var(--border-bright); - padding: 10px 16px; - border-radius: 8px; - color: #eee7ff; - font-size: 0.8rem; - font-weight: 800; - text-transform: uppercase; +.site-header nav a:hover, +.text-link:hover { + color: var(--text); +} + +.text-link { + display: inline-flex; + align-items: center; + gap: 8px; + color: var(--accent-soft); + font-weight: 600; +} + +.text-link span { + font-size: 0.9em; +} + +.header-link { + font-size: 0.86rem; } .hero { - max-width: 1320px; + width: min(calc(100% - 48px), var(--max)); + min-height: 690px; margin: 0 auto; - min-height: calc(100vh - 76px); - padding: 82px 42px 36px; + padding: 92px 0 84px; display: grid; - grid-template-columns: 0.9fr 1.1fr; - gap: 48px; + grid-template-columns: minmax(0, 0.88fr) minmax(450px, 1.12fr); + gap: 74px; align-items: center; } -.eyebrow { - color: var(--green); - font-size: 0.78rem; - font-weight: 900; - letter-spacing: 0.22em; +.kicker, +.section-index, +.story-label { + margin: 0 0 20px; + color: var(--dim); + font: 600 0.72rem/1.2 Consolas, "Courier New", monospace; + letter-spacing: 0.12em; text-transform: uppercase; } -.hero h1, -.section h2, -.split-section h2, -.skills h2 { +.kicker { + display: flex; + align-items: center; + gap: 10px; + color: var(--green); +} + +.kicker span { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--green); + box-shadow: 0 0 0 4px rgba(129, 184, 155, 0.1); +} + +h1, +h2, +h3, +p { + overflow-wrap: break-word; +} + +h1, +h2 { margin: 0; - line-height: 0.98; - letter-spacing: -0.05em; + font-weight: 540; + letter-spacing: -0.045em; } -.hero h1 { - font-size: clamp(3rem, 6vw, 5.8rem); - max-width: 720px; -} - -.hero h1 span { - color: var(--purple); - text-shadow: 0 0 28px rgba(164, 108, 255, 0.4); -} - -.hero-copy p:not(.eyebrow) { - color: #d4cdec; +h1 { max-width: 660px; - font-size: 1.05rem; + font-size: clamp(3.1rem, 5.5vw, 5.25rem); + line-height: 0.98; +} + +.hero-intro { + max-width: 620px; + margin: 30px 0 0; + color: var(--muted); + font-size: 1.12rem; } .actions { display: flex; - gap: 16px; - margin-top: 32px; - flex-wrap: wrap; + align-items: center; + gap: 26px; + margin-top: 38px; } .button { - border-radius: 9px; - padding: 14px 20px; - font-weight: 900; - font-size: 0.86rem; - text-transform: uppercase; - letter-spacing: 0.04em; + display: inline-flex; + align-items: center; + gap: 8px; + padding: 12px 17px; + border: 1px solid var(--line-light); + border-radius: 3px; + font-size: 0.88rem; + font-weight: 650; + transition: background 160ms ease, border-color 160ms ease; } -.primary { - background: linear-gradient(135deg, var(--purple), var(--purple-2)); - box-shadow: 0 0 30px rgba(164, 108, 255, 0.25); +.button:hover { + border-color: #5a5f68; } -.secondary { - border: 1px solid var(--border-bright); - background: rgba(255, 255, 255, 0.02); +.button.primary { + color: #111014; + border-color: var(--accent-soft); + background: var(--accent-soft); } -.hero-art { +.button.primary:hover { + background: #d2bdf2; +} + +.hero-visual, +.architecture-figure { + margin: 0; +} + +.hero-visual { position: relative; - display: flex; - justify-content: center; + padding: 10px; + border: 1px solid var(--line); + background: #08090b; } -.hero-art img { - max-height: 620px; - object-fit: contain; - filter: drop-shadow(0 0 42px rgba(164, 108, 255, 0.35)); -} - -.hero-art::after { +.hero-visual::before { content: ""; position: absolute; - inset: 10%; - border-radius: 50%; - background: rgba(164, 108, 255, 0.12); - filter: blur(90px); + width: 72%; + height: 55%; + top: -24px; + right: -22px; z-index: -1; + background: #16121d; } -.stats { - max-width: 1240px; - margin: 0 auto 18px; - padding: 0 42px; - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 0; - border: 1px solid var(--border); - border-radius: 18px; - background: rgba(12, 15, 27, 0.82); - overflow: hidden; +.hero-visual img { + width: 100%; + height: auto; + filter: saturate(0.78); } -.stats div { - padding: 24px; - border-right: 1px solid rgba(159, 96, 255, 0.16); +.hero-visual figcaption, +.architecture-figure figcaption { + display: flex; + justify-content: space-between; + gap: 20px; + padding-top: 10px; + color: var(--dim); + font: 0.7rem/1.4 Consolas, "Courier New", monospace; } -.stats div:last-child { - border-right: none; -} - -.stats strong { - display: block; - font-size: 2rem; - color: var(--text); -} - -.stats span { +.hero-visual figcaption span { color: var(--muted); } -.split-section, -.section { - max-width: 1240px; +.project-facts { + width: min(calc(100% - 48px), var(--max)); margin: 0 auto; - padding: 72px 42px; -} - -.split-section { - display: grid; - grid-template-columns: 1.45fr 0.9fr; - gap: 24px; -} - -.panel, -.card, -.feature-card, -.challenge-grid div { - background: - linear-gradient(180deg, rgba(255, 255, 255, 0.025), rgba(255, 255, 255, 0.005)), - var(--panel); - border: 1px solid var(--border); - border-radius: 18px; - box-shadow: 0 24px 80px rgba(0, 0, 0, 0.3); -} - -.panel { - padding: 28px; -} - -.panel h2, -.section h2, -.skills h2 { - font-size: clamp(2.2rem, 4vw, 4rem); -} - -.panel p, -.section > p, -.card p, -.feature-card p, -.challenge-grid p, -.panel li { - color: var(--muted); -} - -.panel img { - margin-top: 22px; - border-radius: 14px; - border: 1px solid rgba(159, 96, 255, 0.2); -} - -.about ul { - list-style: none; - padding: 0; - margin: 28px 0 0; -} - -.about li { - margin: 14px 0; - padding-left: 30px; - position: relative; -} - -.about li::before { - content: "✓"; - position: absolute; - left: 0; - color: var(--green); -} - -.section > p { - max-width: 820px; - margin-bottom: 28px; -} - -.card-grid { - display: grid; - gap: 22px; -} - -.card-grid.two { - grid-template-columns: repeat(2, 1fr); -} - -.card { - overflow: hidden; -} - -.card img { - width: 100%; - height: 265px; - object-fit: cover; - object-position: top; - border-bottom: 1px solid rgba(159, 96, 255, 0.18); -} - -.card h3 { - color: var(--purple); - font-size: 1.45rem; - padding: 22px 24px 0; - margin: 0; -} - -.card p { - padding: 8px 24px 24px; - margin: 0; -} - -.feature-card { - overflow: hidden; -} - -.feature-card img { - width: 100%; - display: block; -} - -.feature-card div { - padding: 28px; -} - -.feature-card h3 { - color: var(--purple); - font-size: 1.6rem; - margin: 0 0 8px; -} - -.challenge-grid { display: grid; grid-template-columns: repeat(4, 1fr); - gap: 18px; - margin-top: 26px; + border-top: 1px solid var(--line); + border-bottom: 1px solid var(--line); } -.challenge-grid div { - padding: 22px; +.project-facts div { + min-height: 128px; + padding: 24px 24px 22px 0; + border-right: 1px solid var(--line); } -.challenge-grid h3 { - color: var(--green); - margin-top: 0; +.project-facts div:not(:first-child) { + padding-left: 24px; } -.skills { - max-width: 1240px; +.project-facts div:last-child { + border-right: 0; +} + +.project-facts span, +.story-number, +.lesson-list span { + display: block; + color: var(--accent); + font: 0.72rem/1 Consolas, "Courier New", monospace; +} + +.project-facts strong { + display: block; + margin-top: 14px; + font-size: 1rem; + font-weight: 600; +} + +.project-facts small { + display: block; + color: var(--dim); + font-size: 0.8rem; +} + +.section-rule { + width: min(calc(100% - 48px), var(--max)); margin: 0 auto; - padding: 72px 42px 92px; + padding: 118px 0; + border-bottom: 1px solid var(--line); +} + +.section-heading { + display: grid; + grid-template-columns: 0.38fr 1fr; + gap: 60px; + margin-bottom: 62px; +} + +.section-heading.compact { + margin-bottom: 80px; +} + +.section-heading h2, +.closing h2 { + max-width: 760px; + font-size: clamp(2.5rem, 4.6vw, 4.4rem); + line-height: 1.02; +} + +.section-heading > div > p { + max-width: 690px; + margin: 24px 0 0; + color: var(--muted); + font-size: 1.04rem; +} + +.architecture-figure { + padding: 10px; + border: 1px solid var(--line); + background: #08090b; +} + +.architecture-figure img { + width: 100%; +} + +.principles { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1px; + margin-top: 56px; + background: var(--line); +} + +.principles article { + min-height: 230px; + padding: 28px; + background: var(--bg); +} + +.principles span { + color: var(--accent); + font: 0.7rem Consolas, "Courier New", monospace; + text-transform: uppercase; +} + +.principles h3, +.system-story h3, +.lesson-list h3 { + margin: 30px 0 10px; + font-size: 1.35rem; + font-weight: 580; + letter-spacing: -0.025em; + line-height: 1.2; +} + +.principles p, +.system-story p, +.lesson-list p, +.closing > p:not(.section-index), +footer p { + color: var(--muted); +} + +.principles p { + margin: 0; + font-size: 0.93rem; +} + +.system-story { + display: grid; + grid-template-columns: 0.7fr 1.3fr; + gap: 76px; + align-items: center; + padding: 72px 0; + border-top: 1px solid var(--line); +} + +.system-story.reverse { + grid-template-columns: 1.3fr 0.7fr; +} + +.system-story.reverse .story-copy { + order: 2; +} + +.system-story.reverse .story-images { + order: 1; +} + +.story-label { + margin: 28px 0 16px; + color: var(--green); +} + +.system-story h3 { + margin: 0 0 22px; + font-size: clamp(1.8rem, 3vw, 2.75rem); +} + +.system-story p:not(.story-number, .story-label) { + margin: 0; +} + +.system-story ul { + display: flex; + flex-wrap: wrap; + gap: 8px 18px; + margin: 28px 0 0; + padding: 0; + list-style: none; + color: var(--dim); + font: 0.72rem Consolas, "Courier New", monospace; +} + +.system-story li::before { + content: "+ "; + color: var(--accent); +} + +.story-images { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + min-width: 0; + align-items: start; +} + +.story-images img { + width: 100%; + height: auto; + object-fit: contain; + border: 1px solid var(--line); + background: #08090b; + filter: saturate(0.72); +} + +.lesson-list { + border-top: 1px solid var(--line); +} + +.lesson-list article { + display: grid; + grid-template-columns: 70px 0.85fr 1fr; + gap: 30px; + align-items: baseline; + padding: 31px 0; + border-bottom: 1px solid var(--line); +} + +.lesson-list h3, +.lesson-list p { + margin: 0; +} + +.lesson-list h3 { + font-size: 1.15rem; +} + +.lesson-list p { + font-size: 0.92rem; +} + +.closing { + width: min(calc(100% - 48px), 820px); + margin: 0 auto; + padding: 130px 0; text-align: center; } -.skills-list { - margin-top: 28px; - display: flex; - justify-content: center; - flex-wrap: wrap; - gap: 12px; +.closing h2 { + margin: 0 auto; } -.skills-list span { - border: 1px solid var(--border); - color: #ded6ff; - background: rgba(18, 15, 30, 0.8); - padding: 10px 16px; - border-radius: 999px; - font-weight: 800; - font-size: 0.9rem; +.closing > p:not(.section-index) { + max-width: 650px; + margin: 28px auto 34px; + font-size: 1.06rem; } footer { - border-top: 1px solid rgba(159, 96, 255, 0.16); - padding: 34px 42px; - text-align: center; - color: var(--muted); + width: min(calc(100% - 48px), var(--max)); + margin: 0 auto; + padding: 30px 0 38px; + display: grid; + grid-template-columns: 1fr auto auto; + gap: 32px; + align-items: center; + border-top: 1px solid var(--line); + font-size: 0.78rem; } -footer span { - color: var(--purple); - font-weight: 900; +footer .brand { + margin: 0; +} + +footer .brand img { + width: 26px; + height: 26px; } footer p { - margin: 6px 0 0; + margin: 0; } -@media (max-width: 1050px) { - .site-header { - position: static; - height: auto; - padding: 22px; - flex-wrap: wrap; - } - - .site-header nav { - order: 3; - width: 100%; - flex-wrap: wrap; - } - - .hero, - .split-section, - .card-grid.two, - .challenge-grid { +@media (max-width: 900px) { + .hero { + min-height: auto; grid-template-columns: 1fr; + gap: 70px; } - .stats { + .hero-copy { + max-width: 720px; + } + + .project-facts { grid-template-columns: repeat(2, 1fr); } - .stats div { - border-bottom: 1px solid rgba(159, 96, 255, 0.16); + .project-facts div:nth-child(2) { + border-right: 0; + } + + .project-facts div:nth-child(-n + 2) { + border-bottom: 1px solid var(--line); + } + + .section-heading { + grid-template-columns: 1fr; + gap: 12px; + } + + .principles { + grid-template-columns: 1fr; + } + + .principles article { + min-height: 0; + } + + .system-story, + .system-story.reverse { + grid-template-columns: 1fr; + gap: 38px; + } + + .system-story.reverse .story-copy, + .system-story.reverse .story-images { + order: initial; + } + + .story-copy { + max-width: 650px; + } + + footer { + grid-template-columns: 1fr; + gap: 10px; } } @media (max-width: 650px) { + .site-header, .hero, - .split-section, - .section, - .skills { - padding-left: 20px; - padding-right: 20px; + .project-facts, + .section-rule, + footer { + width: min(calc(100% - 32px), var(--max)); } - .stats { + .site-header { + height: 66px; + } + + .site-header nav { + display: none; + } + + .header-link { + font-size: 0.78rem; + } + + .hero { + padding: 68px 0 64px; + } + + h1 { + font-size: clamp(2.75rem, 14vw, 4rem); + } + + .hero-intro { + font-size: 1rem; + } + + .actions { + align-items: flex-start; + flex-direction: column; + } + + .hero-visual { + padding: 6px; + } + + .hero-visual::before { + display: none; + } + + .hero-visual figcaption { + display: block; + } + + .hero-visual figcaption span { + display: block; + } + + .project-facts { grid-template-columns: 1fr; - padding: 0 20px; - border: none; - background: transparent; - gap: 12px; } - .stats div { - border: 1px solid var(--border); - border-radius: 14px; - background: var(--panel); + .project-facts div, + .project-facts div:not(:first-child) { + min-height: 0; + padding: 20px 0; + border-right: 0; + border-bottom: 1px solid var(--line); } - .card img { - height: 210px; + .project-facts div:last-child { + border-bottom: 0; } -} \ No newline at end of file + + .section-rule { + padding: 82px 0; + } + + .section-heading, + .section-heading.compact { + margin-bottom: 48px; + } + + .section-heading h2, + .closing h2 { + font-size: 2.55rem; + } + + .architecture-figure { + padding: 5px; + overflow-x: auto; + } + + .architecture-figure img { + min-width: 780px; + } + + .principles { + margin-top: 38px; + } + + .system-story { + padding: 54px 0; + } + + .story-images { + grid-template-columns: 1fr; + } + + .story-images img { + height: auto; + } + + .lesson-list article { + grid-template-columns: 40px 1fr; + gap: 14px; + } + + .lesson-list p { + grid-column: 2; + } + + .closing { + width: calc(100% - 32px); + padding: 90px 0; + } +}