/* ============================================================
   dobsinsky.dev — zone file
   palette: ink #0C0C0E · cobalt #152A52 · oxide #5E2218 · bone #DFD9CB
   accent:  signal #FFB000 (amber terminal)
   type:    Archivo Black (display) · IBM Plex Mono (everything else)
   ============================================================ */

:root {
  --ink: #0C0C0E;
  --cobalt: #152A52;
  --oxide: #5E2218;
  --bone: #DFD9CB;
  --signal: #FFB000;
  --fg: #EDEAE2;
  --fg-dim: #8d8a82;
  --line: rgba(237, 234, 226, 0.25);
  --mono: 'IBM Plex Mono', ui-monospace, monospace;
  --display: 'Archivo Black', sans-serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  font-family: var(--mono);
  background: var(--ink);
  color: var(--fg);
  line-height: 1.55;
  overflow-x: hidden;
}

a { color: var(--signal); text-decoration: none; border-bottom: 1px solid currentColor; }
a:hover { background: var(--signal); color: var(--ink); }

::selection { background: var(--signal); color: var(--ink); }

:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }

/* ---------- status bar ---------- */
.statusbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.5rem 1.25rem;
  font-size: 0.75rem; letter-spacing: 0.04em;
  background: rgba(12, 12, 14, 0.85);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line);
}
.statusbar__zone { font-weight: 700; }
.statusbar__sep { color: var(--fg-dim); }
.statusbar__item { color: var(--fg-dim); }
#conn-status[data-state="trusted"] { color: var(--signal); font-weight: 700; }
/* CrowdSec counter. Stays hidden unless the API answers, so the bar never
   shows a stale or dead number. */
.statusbar__shield { color: var(--fg); cursor: help; white-space: nowrap; }
@media (max-width: 640px) { .statusbar__shield, #crowdsec-sep { display: none; } }
.statusbar__nav { margin-left: auto; display: flex; gap: 1rem; }
.statusbar__nav a { border-bottom: none; color: var(--fg); }
.statusbar__nav a:hover { color: var(--ink); }
/* Off-site link, so it reads differently from the in-page anchors beside it. */
.statusbar__nav-out { color: var(--signal) !important; font-weight: 700; }

/* ---------- sections ---------- */
.sec {
  min-height: 100vh;
  padding: 7rem 6vw 5rem;
  position: relative;
  border-bottom: 3px solid var(--fg);
}

.rec__header {
  font-family: var(--mono);
  font-weight: 400;
  font-size: clamp(1rem, 2vw, 1.35rem);
  margin-bottom: 3rem;
  text-transform: lowercase;
  border-bottom: 1px solid var(--line);
  padding-bottom: 0.75rem;
}
.rec__type { color: var(--signal); font-weight: 700; margin-right: 1rem; }
.rec__badge {
  float: right; font-size: 0.7rem; font-weight: 700;
  background: var(--signal); color: var(--ink);
  padding: 0.15rem 0.6rem;
}

.dig-line { font-size: 0.85rem; color: var(--signal); margin-bottom: 0.35rem; }
.dig-line--dim { color: var(--fg-dim); }

/* ---------- hero ---------- */
.sec--hero { display: flex; flex-direction: column; justify-content: center; }

.hero__name {
  font-family: var(--display);
  /* Tuned to the LONGER row: "DOBŠÍNSKÝ" is 9 glyphs of Archivo Black, which
     averages ~0.78em of advance each — about 7em — and the offset row gives up
     another 4vw to its indent. At the old 13vw it ran off the right edge on any
     normal laptop. Keep this in step with the indent below if the name changes. */
  font-size: clamp(2.25rem, 10.5vw, 9.5rem);
  /* 0.92 cropped the háček on Š and the acutes on Í and Ý: those glyphs reach
     above cap height, so the line box has to be taller than 1em, not shorter.
     Czech diacritics are the reason this cannot be tightened back up. */
  line-height: 1.04;
  letter-spacing: -0.02em;
  margin: 2rem 0;
  perspective: 800px;
}
.hero__row { display: block; white-space: nowrap; will-change: transform; }
.hero__row--offset { margin-left: clamp(0.5rem, 4vw, 3.5rem); color: var(--signal); }

.hero__soa { max-width: 46rem; border-left: 3px solid var(--signal); padding-left: 1.25rem; }
.soa__line { display: flex; gap: 1.5rem; padding: 0.3rem 0; font-size: 0.95rem; }
.soa__key { color: var(--fg-dim); min-width: 4ch; font-weight: 700; }

.hero__greeting {
  margin-top: 2rem; font-size: 1.2rem; color: var(--signal);
  border: 1px dashed var(--signal); padding: 0.75rem 1rem; max-width: max-content;
}

.hero__scrollhint {
  position: absolute; bottom: 2rem; left: 6vw;
  color: var(--fg-dim); font-size: 0.8rem;
  animation: bob 2.4s ease-in-out infinite;
}
@keyframes bob { 50% { transform: translateY(6px); } }

/* ---------- grids & panels ---------- */
.grid { display: grid; gap: 2rem; }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr)); }

.panel {
  /* Same var-driven transform as .project — attachTilt writes these and no
     longer touches style.transform, so the stylesheet has to compose it here or
     the panels would not tilt at all. */
  --rx: 0deg; --ry: 0deg; --lift: 0;

  border: 2px solid var(--fg);
  padding: 1.75rem;
  background: rgba(0, 0, 0, 0.25);
  transform-style: preserve-3d;
  will-change: transform;
  transform: rotateX(var(--rx)) rotateY(var(--ry))
             translateZ(calc(var(--lift) * 8px));
  transition: transform .14s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .panel { transform: none; transition: none; }
}
.panel__label {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.15em;
  color: var(--signal); margin-bottom: 1rem;
}
.panel__body { font-size: 0.95rem; }
.panel--terminal { background: #050505; }
.now__output { font-size: 0.82rem; white-space: pre-wrap; color: #c9ffd8; min-height: 8rem; }
.panel__foot { margin-top: 1rem; font-size: 0.75rem; color: var(--fg-dim); }

/* ---------- marquee ---------- */
.stack__marquee {
  margin-top: 4rem; overflow: hidden;
  border-top: 2px solid var(--fg); border-bottom: 2px solid var(--fg);
  padding: 0.7rem 0;
}
.stack__track {
  display: flex; gap: 2.5rem; width: max-content;
  animation: marquee 28s linear infinite;
  font-family: var(--display); font-size: 1.1rem;
}
@keyframes marquee { to { transform: translateX(-50%); } }

/* ---------- projects ---------- */
.projects { display: grid; gap: 2rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 21rem), 1fr)); perspective: 1200px; }

.project {
  /* Defaults matter: attachTilt only sets these on pointer interaction, and on
     a touch device it never runs at all. */
  --rx: 0deg; --ry: 0deg;
  --mx: 50%; --my: 50%;
  --bx: 50%; --by: 50%;
  /* Unitless, and they must have a value here: the variants feed them to calc()
     inside hue-rotate() and conic-gradient(), and an unresolved var makes the
     whole declaration invalid rather than just falling back. */
  --hx: 50; --hy: 50;
  --lift: 0;

  position: relative;
  border: 2px solid var(--fg);
  padding: 1.5rem;
  background: rgba(0, 0, 0, 0.3);
  transform-style: preserve-3d;
  will-change: transform;
  display: flex; flex-direction: column; gap: 0.8rem;

  /* CSS owns the transform so nothing races an inline style. .projects already
     establishes the perspective, so these are plain rotations. */
  transform: rotateX(var(--rx)) rotateY(var(--ry))
             translateZ(calc(var(--lift) * 10px));
  transition: transform .14s ease-out, border-color .25s ease;
}
.project:hover { border-color: var(--signal); }

/* ---------- holographic foil (featured cards only) ---------- */
/* Two stacked layers, the way a foil card actually reads: a rainbow diffraction
   pattern that slides against the tilt, and a specular highlight that tracks
   the cursor. isolation:isolate keeps the blend modes inside the card instead
   of letting them punch through to the page background. */
.project--holo { overflow: hidden; isolation: isolate; }

.project__foil, .project__glare {
  position: absolute; inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s ease;
  border-radius: inherit;
}
.project--holo:hover .project__foil { opacity: .55; }
.project--holo:hover .project__glare { opacity: 1; }

/* Shared specular highlight. Variants override it where the look needs
   something other than a round hotspot. */
.project__glare {
  background: radial-gradient(
    circle at var(--mx) var(--my),
    rgba(255, 255, 255, .30),
    rgba(255, 255, 255, .07) 32%,
    transparent 62%
  );
  mix-blend-mode: overlay;
}

/* ---- 1. HOLOGRAPHIC (Balatro) ------------------------------------------- */
/* Magenta-dominant with cyan and gold streaks, not a full rainbow — that is
   what separates Balatro's Holographic from its Polychrome. The hue rotates
   with pointer x, because a hologram changes colour with viewing angle; a fixed
   gradient just looks like a sticker. Faintly present at rest and drifting,
   since these cards are always holographic in-game, not only on hover. */
.project--fx-holo .project__foil {
  background: repeating-linear-gradient(
    102deg,
    rgba(255, 92, 190, .70) 0%,
    rgba(255, 170, 235, .40) 4%,
    rgba(130, 225, 255, .55) 9%,
    rgba(255, 232, 150, .45) 14%,
    rgba(196, 120, 255, .60) 19%,
    rgba(255, 92, 190, .70) 25%
  );
  background-size: 190% 190%;
  background-position: var(--bx) var(--by);
  mix-blend-mode: color-dodge;
  filter: saturate(1.9) contrast(1.15) hue-rotate(calc((var(--hx) - 50) * 1.4deg)) blur(1.5px);
  opacity: .22;
  animation: holo-drift 7s ease-in-out infinite;
}
.project--fx-holo:hover .project__foil {
  opacity: .62;
  animation: none;                 /* pointer takes over from the idle drift */
}
@keyframes holo-drift {
  0%, 100% { background-position: 22% 32%; }
  50%      { background-position: 78% 68%; }
}

/* ---- 2. FOIL ------------------------------------------------------------ */
/* Cool, narrow, mirror-like. Screen rather than color-dodge keeps it metallic
   instead of neon. */
.project--fx-foil .project__foil {
  background: repeating-linear-gradient(
    74deg,
    rgba(120, 190, 255, 0) 0 3%,
    rgba(205, 235, 255, .80) 5%,
    rgba(70, 130, 225, .35) 8%,
    rgba(120, 190, 255, 0) 11% 17%
  );
  background-size: 165% 165%;
  background-position: var(--bx) var(--by);
  mix-blend-mode: screen;
  filter: contrast(1.35) blur(.5px);
}
.project--fx-foil:hover .project__foil { opacity: .8; }

/* ---- 3. POLYCHROME ----------------------------------------------------- */
/* Oil slick: a wide conic sweep, heavily blurred so there are no visible bands,
   rotating with the pointer. */
.project--fx-poly .project__foil {
  background: conic-gradient(
    from calc(var(--hx) * 3.6deg) at var(--mx) var(--my),
    #ff5ea8, #ffd166, #4ade80, #38bdf8, #a855f7, #ff5ea8
  );
  mix-blend-mode: color-dodge;
  filter: blur(18px) saturate(1.35);
}
.project--fx-poly:hover .project__foil { opacity: .45; }

/* ---- 4. CRT ------------------------------------------------------------- */
/* Scanlines plus a phosphor band that follows the cursor. The one variant that
   belongs to this site's own terminal idiom rather than to a card game. */
.project--fx-crt .project__foil {
  background: repeating-linear-gradient(
    0deg,
    rgba(120, 255, 200, .16) 0 1px,
    transparent 1px 3px
  );
  mix-blend-mode: screen;
}
.project--fx-crt:hover .project__foil { opacity: .9; }
.project--fx-crt .project__glare {
  background: radial-gradient(
    130% 16% at 50% var(--my),
    rgba(170, 255, 220, .38),
    transparent 70%
  );
  mix-blend-mode: screen;
}

/* ---- 5. GLASS ---------------------------------------------------------- */
/* Restrained: one diagonal light sweep and a lit top edge. The option that does
   not announce itself, for comparison against the loud four. */
.project--fx-glass .project__foil {
  background:
    linear-gradient(
      calc((var(--hx) - 50) * 0.9deg + 115deg),
      transparent 35%,
      rgba(255, 255, 255, .16) 48%,
      rgba(255, 255, 255, .04) 55%,
      transparent 68%
    );
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22);
  mix-blend-mode: screen;
}
.project--fx-glass:hover .project__foil { opacity: 1; }

/* The tilt and the foil are both motion. Leave the border cue, drop the rest. */
@media (prefers-reduced-motion: reduce) {
  .project { transition: border-color .25s ease; transform: none; }
  .project__foil, .project__glare { display: none; }
}
.project__name { font-family: var(--display); font-size: 1.25rem; line-height: 1.15; }
.project__stack { font-size: 0.7rem; color: var(--signal); letter-spacing: 0.08em; }
.project__desc { font-size: 0.88rem; color: var(--fg); flex: 1; }
.project__link { align-self: flex-start; font-size: 0.8rem; }
.project--archive { opacity: 0.85; border-style: dashed; }

.archive-toggle {
  margin-top: 3rem;
  font-family: var(--mono); font-size: 0.9rem;
  background: none; border: 2px dashed var(--fg); color: var(--fg);
  padding: 0.8rem 1.4rem; cursor: pointer;
}
.archive-toggle:hover { background: var(--fg); color: var(--ink); }
.projects--archive { margin-top: 2rem; }

/* ---------- SRV / homelab ---------- */
/* Every section is now `ink`, so this block reads on dark. It used to sit on
   `bone` and hard-coded ink text and rgba(12,12,14,…) borders — on a dark
   background that was ink-on-ink, i.e. invisible. Everything here goes through
   --fg / --signal instead, so it survives a theme change too. */
.sec--srv .rec__header { border-color: rgba(255,255,255,0.22); }
.sec--srv .dig-line { color: var(--signal); }
.sec--srv .panel, .sec--srv .stat {
  border-color: rgba(255,255,255,0.22); background: rgba(255,255,255,0.04);
}

.grid--stats { grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); margin-bottom: 3rem; }
.stat { border: 2px solid rgba(255,255,255,0.22); padding: 1.25rem; }
.stat__label { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.12em; color: var(--fg-dim); }
.stat__value { font-family: var(--display); font-size: 2.2rem; margin: 0.4rem 0; }
.stat__bar { height: 8px; background: rgba(255,255,255,0.12); }
.stat__bar i { display: block; height: 100%; background: var(--signal); }
.stat__bar i.warn { background: #ff6b3d; }
.stat__sub { font-size: 0.72rem; margin-top: 0.4rem; color: var(--fg-dim); }

.grid--services { grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr)); gap: 0.75rem; }
.svc {
  border: 2px solid rgba(255,255,255,0.22); color: var(--fg);
  padding: 0.9rem 1rem; font-size: 0.85rem; font-weight: 700;
  display: flex; justify-content: space-between;
}
.svc:hover { background: var(--signal); color: var(--ink); }
.svc small { font-weight: 400; opacity: 0.65; }

.visitors { margin-top: 3rem; font-size: 0.85rem; }
.visitors summary { cursor: pointer; font-weight: 700; }
.visitor {
  display: flex; flex-wrap: wrap; gap: 1rem; align-items: center;
  border-bottom: 1px solid rgba(255,255,255,0.18); padding: 0.6rem 0;
}
.visitor__vid { font-size: 0.72rem; opacity: 0.6; }
.visitor input {
  font-family: var(--mono); font-size: 0.8rem;
  border: 1px solid rgba(255,255,255,0.3); background: transparent; color: var(--fg);
  padding: 0.25rem 0.5rem;
}
.visitor button {
  font-family: var(--mono); border: 2px solid rgba(255,255,255,0.3); background: none;
  color: var(--fg); padding: 0.25rem 0.7rem; cursor: pointer;
}
.visitor button:hover { background: var(--signal); color: var(--ink); }

/* ---------- MX / contact ---------- */
.sec--mx { display: flex; flex-direction: column; justify-content: center; }
.mx__addr {
  font-family: var(--display);
  font-size: clamp(1.6rem, 6vw, 4.5rem);
  border-bottom: 4px solid var(--signal);
  align-self: flex-start;
  word-break: break-all;
}
.mx__at { color: var(--signal); }
.mx__copy {
  margin-top: 1.5rem; align-self: flex-start;
  font-family: var(--mono); font-size: 0.85rem;
  background: none; border: 2px solid var(--fg); color: var(--fg);
  padding: 0.6rem 1.2rem; cursor: pointer;
}
.mx__copy:hover { background: var(--signal); border-color: var(--signal); color: var(--ink); }
.mx__links { display: flex; gap: 2rem; margin-top: 2.5rem; font-size: 0.9rem; }

.footer { margin-top: 5rem; font-size: 0.75rem; color: var(--fg-dim); }
.footer__vid { opacity: 0.5; }

/* ---------- motion safety ---------- */
@media (prefers-reduced-motion: reduce) {
  .stack__track { animation: none; }
  .hero__scrollhint { animation: none; }
  html { scroll-behavior: auto; }
  * { transition: none !important; }
}

@media (max-width: 640px) {
  .sec { padding: 6rem 5vw 4rem; }
  .statusbar { font-size: 0.68rem; gap: 0.4rem; }
}

/* ---------- terminal overlay ---------- */
.statusbar__shell {
  font-family: var(--mono); font-size: 0.72rem; font-weight: 700;
  background: none; border: 1px solid var(--signal); color: var(--signal);
  padding: 0.15rem 0.6rem; cursor: pointer; margin-left: 1rem;
}
.statusbar__shell:hover { background: var(--signal); color: var(--ink); }

.term[hidden] { display: none; }

.term {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(0, 0, 0, 0.6);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 8vh 4vw;
}
.term__win {
  width: min(60rem, 100%);
  background: #050505;
  border: 2px solid var(--signal);
  box-shadow: 10px 10px 0 rgba(0,0,0,0.45);
  transform: translateY(-30px); opacity: 0;
  transition: transform 0.25s ease, opacity 0.25s ease;
  display: flex; flex-direction: column;
  max-height: 80vh;
}
.term--open .term__win { transform: translateY(0); opacity: 1; }

.term__bar {
  display: flex; justify-content: space-between; align-items: center;
  border-bottom: 1px solid var(--signal);
  padding: 0.4rem 0.9rem; font-size: 0.72rem; color: var(--signal);
}
.term__close {
  font-family: var(--mono); background: none; border: none;
  color: var(--signal); cursor: pointer; font-size: 0.9rem;
}
.term__close:hover { color: var(--fg); }

.term__out {
  padding: 0.9rem; overflow-y: auto; flex: 1;
  font-size: 0.82rem; min-height: 14rem;
}
.term__line { white-space: pre-wrap; color: var(--fg); }
.term__line a { color: var(--signal); }
.term__prompt { color: var(--signal); }
.term__dim { color: var(--fg-dim); }
.term__accent { color: var(--signal); }
.term__err { color: #ff5544; }

.term__inputrow {
  display: flex; gap: 0.6rem; align-items: center;
  border-top: 1px solid rgba(255, 176, 0, 0.3);
  padding: 0.6rem 0.9rem;
}
.term__ps1 { color: var(--signal); font-size: 0.82rem; white-space: nowrap; }
.term__input {
  flex: 1; font-family: var(--mono); font-size: 0.85rem;
  background: none; border: none; color: var(--fg); caret-color: var(--signal);
}
.term__input:focus { outline: none; }

@media (max-width: 640px) {
  .term { padding: 4vh 2vw; }
  .term__ps1 { display: none; }
}

/* ---------- editor in terminal ---------- */
.ted { border: 1px solid var(--signal); margin: 0.5rem 0; }
.ted__bar {
  display: flex; justify-content: space-between;
  font-size: 0.7rem; color: var(--ink);
  background: var(--signal); padding: 0.2rem 0.6rem;
}
.ted__area {
  width: 100%; min-height: 11rem; resize: vertical;
  font-family: var(--mono); font-size: 0.82rem;
  background: transparent; color: var(--fg);
  border: none; padding: 0.6rem;
}
.ted__area:focus { outline: none; }

/* ---------- themes ----------
   theme switching redefines the core vars; app.js reads section
   colors from these at scroll time, so everything follows. */
[data-theme="magma"] {
  --ink: #0D0506;
  --cobalt: #38090B;
  --oxide: #6E1A00;
  --bone: #F2C29B;
  --signal: #FF4D00;
  --fg: #F6E3D7;
  --fg-dim: #9c7f72;
  --line: rgba(246, 227, 215, 0.25);
}
[data-theme="mocha"] {
  --ink: #11111B;
  --cobalt: #1E1E2E;
  --oxide: #313244;
  --bone: #F5E0DC;
  --signal: #CBA6F7;
  --fg: #CDD6F4;
  --fg-dim: #7f849c;
  --line: rgba(205, 214, 244, 0.25);
}
[data-theme="latte"] {
  --ink: #EFF1F5;
  --cobalt: #DCE0E8;
  --oxide: #E6D6C8;
  --bone: #4C4F69;
  --signal: #FE640B;
  --fg: #4C4F69;
  --fg-dim: #8c8fa1;
  --line: rgba(76, 79, 105, 0.3);
}
/* light theme: dark chrome elements need flipping */
[data-theme="latte"] .statusbar { background: rgba(239, 241, 245, 0.85); }
[data-theme="latte"] .panel, [data-theme="latte"] .project { background: rgba(76, 79, 105, 0.06); }
[data-theme="latte"] .panel--terminal, [data-theme="latte"] .term__win { background: #E6E9EF; }
[data-theme="latte"] .now__output { color: #2a6b3f; }
[data-theme="latte"] .term__win { box-shadow: 10px 10px 0 rgba(76,79,105,0.25); }
/* in latte the SRV section bg (--bone) is dark, so flip its text */
[data-theme="latte"] .sec--srv { color: #EFF1F5; }
[data-theme="latte"] .sec--srv .panel, [data-theme="latte"] .sec--srv .stat,
[data-theme="latte"] .sec--srv .svc, [data-theme="latte"] .sec--srv .visitor input,
[data-theme="latte"] .sec--srv .visitor button { border-color: #EFF1F5; color: #EFF1F5; background: transparent; }

/* scroll lock while terminal is open */
body.term-open { overflow: hidden; }

/* ---------- more themes ---------- */
[data-theme="dracula"] {
  --ink: #191a24; --cobalt: #282a36; --oxide: #44475a; --bone: #f8f8f2;
  --signal: #ff79c6; --fg: #f8f8f2; --fg-dim: #6272a4; --line: rgba(248,248,242,.25);
}
[data-theme="gruvbox"] {
  --ink: #1d2021; --cobalt: #3c3836; --oxide: #af3a03; --bone: #fbf1c7;
  --signal: #fabd2f; --fg: #ebdbb2; --fg-dim: #928374; --line: rgba(235,219,178,.25);
}
[data-theme="nord"] {
  --ink: #2e3440; --cobalt: #3b4252; --oxide: #4c566a; --bone: #eceff4;
  --signal: #88c0d0; --fg: #e5e9f0; --fg-dim: #7b88a1; --line: rgba(229,233,240,.25);
}

/* ---------- slots foreclosure ---------- */
.wasted {
  position: fixed; inset: 0; z-index: 999;
  background: #000; color: #c33;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-family: var(--display); font-size: clamp(1.6rem, 5vw, 3.5rem);
  gap: 1rem; text-align: center; padding: 2rem;
}
.wasted__sub { font-family: var(--mono); font-size: 0.95rem; color: #777; }
