/* ==========================================================================
   Variables
   ========================================================================== */
:root {
  --bg: #0a0d0a;
  --bg-elevated: #10140f;
  --bg-card: #12160f;
  --border: #212a1e;
  --border-hover: #2f3d29;

  --green: #3ddc7a;
  --green-strong: #2bc466;
  --green-dim: #1c7a45;
  --green-glow: rgba(61, 220, 122, 0.35);
  --green-rgb: 61, 220, 122;

  --accent: #ffb545;
  --accent-rgb: 255, 181, 69;
  --accent-glow: radial-gradient(circle, rgba(255, 181, 69, 0.65) 0%, rgba(255, 181, 69, 0.25) 45%, rgba(255, 181, 69, 0) 75%);

  --text: #e7ece6;
  --text-dim: #a9b3a5;
  --text-faint: #6f796c;

  --radius: 14px;
  --radius-sm: 8px;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --transition: 320ms var(--ease);
  --transition-fast: 150ms var(--ease);

  --max-width: 1120px;
}

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

html {
  scroll-behavior: smooth;
}

html, body {
  scrollbar-width: thin;
  scrollbar-color: var(--green-dim) var(--bg-elevated);
}

::-webkit-scrollbar {
  width: 12px;
  height: 12px;
}

::-webkit-scrollbar-track {
  background: var(--bg-elevated);
}

::-webkit-scrollbar-thumb {
  background-color: var(--green-dim);
  border-radius: 8px;
  border: 3px solid var(--bg-elevated);
}

::-webkit-scrollbar-thumb:hover {
  background-color: var(--green);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3, p, ul, blockquote {
  margin: 0;
}

ul {
  padding: 0;
  list-style: none;
}

a {
  color: inherit;
  text-decoration: none;
}

/* Native link/element drag-and-drop stalls pointermove (and can leave
   :hover "stuck") while the drag gesture is active — this UI has no use
   for dragging links or cards, so opt out entirely. */
a,
.card,
.contact-link {
  -webkit-user-drag: none;
}

img, svg {
  display: block;
  max-width: 100%;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

code {
  font-family: 'Cascadia Code', Consolas, monospace;
  font-size: 0.9em;
  background: rgba(61, 220, 122, 0.08);
  color: var(--green);
  padding: 0.1em 0.4em;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--green);
  color: #06110a;
  padding: 0.75rem 1.25rem;
  border-radius: 0 0 var(--radius-sm) 0;
  z-index: 1000;
  font-weight: 600;
}

.skip-link:focus {
  left: 0;
}

/* ==========================================================================
   Nav
   ========================================================================== */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: rgba(10, 13, 10, 0.72);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}

.nav__inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 1rem 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.nav__logo {
  font-weight: 700;
  letter-spacing: 0.02em;
  font-size: 1.05rem;
  color: var(--text);
}

.nav__links {
  display: flex;
  gap: 2rem;
}

.nav__links a {
  color: var(--text-dim);
  font-size: 0.95rem;
  transition: color var(--transition);
  position: relative;
}

.nav__links a::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -4px;
  width: 0;
  height: 1px;
  background: var(--green);
  transition: width var(--transition);
}

.nav__links a:hover,
.nav__links a:focus-visible {
  color: var(--text);
}

.nav__links a:hover::after,
.nav__links a:focus-visible::after {
  width: 100%;
}

.nav__toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: 0.5rem;
  border-radius: var(--radius-sm);
  transition: background-color var(--transition-fast), transform var(--transition-fast);
}

.nav__toggle:hover,
.nav__toggle:focus-visible {
  background: rgba(61, 220, 122, 0.12);
  transform: scale(1.08);
}

.nav__toggle span {
  width: 22px;
  height: 2px;
  background: var(--text);
  transition: transform var(--transition), opacity var(--transition), background-color var(--transition-fast);
}

.nav__toggle:hover span,
.nav__toggle:focus-visible span {
  background: var(--green);
}

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.85rem 1.75rem;
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 0.95rem;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast);
  border: 1px solid transparent;
}

.btn--primary {
  background: var(--green);
  color: #06110a;
}

.btn--primary:hover,
.btn--primary:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 8px 30px var(--green-glow);
  background: var(--green-strong);
}

.btn--ghost {
  border-color: var(--border-hover);
  color: var(--text);
}

.btn--ghost:hover,
.btn--ghost:focus-visible {
  border-color: var(--green);
  color: var(--green);
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(61, 220, 122, 0.15);
}

/* ==========================================================================
   Cursor spotlight (global, mouse-only)
   ========================================================================== */
.cursor-spotlight {
  position: fixed;
  top: 0;
  left: 0;
  width: 640px;
  height: 640px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--green-glow), transparent 70%);
  filter: blur(15px);
  pointer-events: none;
  z-index: 1;
  opacity: 0;
  transform: translate3d(-9999px, -9999px, 0) translate(-50%, -50%);
  transition: opacity 220ms var(--ease);
  will-change: transform, opacity;
}

.cursor-spotlight.is-active {
  opacity: 1;
}

@media (pointer: coarse) {
  .cursor-spotlight {
    display: none;
  }
}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  position: relative;
  z-index: 2;
  min-height: 100vh;
  /* On mobile, 100vh is the browser's *maximum* viewport (address bar
     hidden) — with it collapsed on first paint, the actually-visible area
     is shorter, which is exactly the kind of gap that lets fixed content
     (the nav) and in-flow content disagree about where they sit relative
     to each other. 100dvh tracks the real visible viewport; vh stays as
     the fallback for browsers that don't support dvh. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 8rem 1.5rem 4rem;
  overflow: hidden;
  isolation: isolate;
}

.hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: radial-gradient(ellipse at top right, rgba(255, 181, 69, 0.08), transparent 50%);
}

.hero__glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* On fine-pointer devices the global .cursor-spotlight layer handles the
   glow (including inside hero), so this element only renders anything on
   touch devices, where it drives the autonomous drift animation instead. */
@media (pointer: coarse) {
  .hero__glow {
    background-image: radial-gradient(circle, var(--green-glow), transparent 45%);
    background-repeat: no-repeat;
    background-size: 1100px 1100px;
    background-position: 30% 30%;
    will-change: background-position;
    animation: heroGlowDrift 10s ease-in-out infinite;
  }
}

@keyframes heroGlowDrift {
  0% { background-position: 30% 30%; }
  50% { background-position: 68% 52%; }
  100% { background-position: 30% 30%; }
}

.hero__bg::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(61, 220, 122, 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(61, 220, 122, 0.06) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: radial-gradient(circle at 50% 40%, black, transparent 75%);
  -webkit-mask-image: radial-gradient(circle at 50% 40%, black, transparent 75%);
}

.hero__content {
  position: relative;
  z-index: 1;
  max-width: 960px;
  /* margin-left/right: auto (horizontal centering, via the shorthand
     below) plus margin-top/bottom: auto — the latter pair vertically
     centers this block *within whatever space .hero__scroll-hint doesn't
     need*, instead of .hero__content claiming the full height and the
     hint overlapping it. Flex distributes the container's free space
     evenly between these two auto margins, so growing content (long
     description wrapping to more lines on narrow screens, short
     viewports) only ever shrinks that shared space — it can never push
     .hero__content into the hint, which sits after it in normal flow. */
  margin: auto;
  width: 100%;
  display: flex;
  align-items: center;
  gap: clamp(2rem, 5vw, 4rem);
}

.hero__avatar {
  flex-shrink: 0;
}

.hero__avatar-img {
  display: block;
  width: clamp(140px, 18vw, 220px);
  height: clamp(140px, 18vw, 220px);
  object-fit: cover;
  border-radius: 50%;
  border: 3px solid var(--green-dim);
  transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}

.hero__avatar-img:hover {
  transform: scale(1.03);
  border-color: var(--accent);
  box-shadow: 0 0 48px var(--accent-glow);
}

.hero__text {
  flex: 1;
  min-width: 0;
}

.hero__greeting {
  color: var(--green);
  font-size: 1.15rem;
  font-weight: 600;
  margin-bottom: 1rem;
  letter-spacing: 0.02em;
}

.hero__name {
  font-size: clamp(2.4rem, 6vw, 4rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  margin-bottom: 0.75rem;
}

.hero__role {
  font-size: clamp(1.1rem, 2.5vw, 1.4rem);
  color: var(--text-dim);
  margin-bottom: 1.75rem;
}

.hero__desc {
  font-size: 1.05rem;
  color: var(--text-dim);
  max-width: 640px;
  margin-bottom: 2.5rem;
}

.hero__desc .accent {
  color: var(--accent);
  /*font-weight: 600;*/
}

.hero__cta {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

.hero__scroll-hint {
  /* Normal flow, not absolutely pinned to the viewport bottom: on
     narrow/short screens (long wrapped .hero__desc eating most of the
     vertical space), an absolute bottom:2rem offset would sit the hint
     on top of .hero__cta's buttons regardless of how much room content
     actually needed. As a flex sibling after .hero__content it always
     renders below it, so it can only ever move — never overlap. */
  flex-shrink: 0;
  margin-top: 2rem;
  width: 24px;
  height: 40px;
  border: 1.5px solid var(--border-hover);
  border-radius: 14px;
  z-index: 1;
}

.hero__scroll-hint span {
  display: block;
  width: 4px;
  height: 8px;
  background: var(--green);
  border-radius: 2px;
  margin: 6px auto 0;
  animation: scrollDot 1.8s ease infinite;
}

@keyframes scrollDot {
  0% { transform: translateY(0); opacity: 1; }
  70% { transform: translateY(14px); opacity: 0; }
  100% { transform: translateY(0); opacity: 0; }
}

/* ==========================================================================
   Reveal-on-scroll
   ========================================================================== */
.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 700ms var(--ease), transform 700ms var(--ease);
  /* Without a promoted compositor layer, mobile GPUs can rasterize this
     transform+opacity transition against a stale tile mid-scroll, which
     reads as the block flickering/vanishing instead of smoothly lifting
     into place. will-change asks the browser to promote the layer before
     the transition starts, not after. */
  will-change: opacity, transform;
}

.reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

/* ==========================================================================
   Sections (shared)
   ========================================================================== */
.section {
  position: relative;
  padding: 6rem 1.5rem;
  overflow: hidden;
}

/* Soft fade instead of a hard rule between sections — a hint of a seam,
   not a border. */
.section::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--border) 25%, var(--border) 75%, transparent);
  filter: blur(1px);
  opacity: 0.55;
  pointer-events: none;
}

/* Faint grid texture behind each section, softened via blur so it reads as
   an ambient glow/texture rather than a crisp ruled grid (same motif as
   hero's grid, but muted the way .nav mutes content behind it with blur). */
.section::before {
  content: '';
  position: absolute;
  inset: -10%;
  z-index: -1;
  background-image:
    linear-gradient(rgba(61, 220, 122, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(61, 220, 122, 0.05) 1px, transparent 1px);
  background-size: 64px 64px;
  filter: blur(2.5px);
  opacity: 0.65;
  mask-image: radial-gradient(ellipse 65% 55% at 50% 40%, black, transparent 85%);
  -webkit-mask-image: radial-gradient(ellipse 65% 55% at 50% 40%, black, transparent 85%);
  pointer-events: none;
}

.section__inner {
  position: relative;
  z-index: 1;
  max-width: var(--max-width);
  margin: 0 auto;
}

/* ==========================================================================
   Section background orbs (scroll parallax)
   ========================================================================== */
.section-orbs {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* .orb is a plain positioning host — JS applies the scroll-parallax
   translate3d() to it directly. The actual visible blob (color, blur,
   breathing pulse) lives on ::after so the CSS animation's transform
   never fights with JS's inline transform on the same property. */
.orb {
  position: absolute;
  pointer-events: none;
  will-change: transform;
}

.orb::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  filter: blur(90px);
  animation: orbPulse 10s ease-in-out infinite;
}

@keyframes orbPulse {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.12);
  }
}

.orb--green::after {
  background: var(--green);
  opacity: 0.16;
}

.orb--accent::after {
  background: var(--accent);
  opacity: 0.13;
}

/* Per-section placement + size — each section gets its own corner/scale
   instead of the same green-top-left / accent-bottom-right pair repeated
   five times. Pulse timing is staggered per orb so they never sync up. */
#experience .orb--green {
  top: -15%;
  right: -10%;
  left: auto;
  width: 380px;
  height: 380px;
}

#experience .orb--green::after {
  animation-duration: 9s;
}

#experience .orb--accent {
  bottom: -10%;
  left: -8%;
  width: 280px;
  height: 280px;
}

#experience .orb--accent::after {
  animation-duration: 12s;
  animation-delay: 1.5s;
}

#projects .orb--green {
  top: -18%;
  left: -10%;
  width: 480px;
  height: 480px;
}

#projects .orb--green::after {
  animation-duration: 13s;
  animation-delay: 0.6s;
}

#projects .orb--accent {
  top: 35%;
  right: -8%;
  bottom: auto;
  width: 260px;
  height: 260px;
}

#projects .orb--accent::after {
  animation-duration: 8.5s;
}

#interests .orb--green {
  bottom: -14%;
  left: -6%;
  top: auto;
  width: 360px;
  height: 360px;
}

#interests .orb--green::after {
  animation-duration: 10.5s;
  animation-delay: 2s;
}

#interests .orb--accent {
  top: -10%;
  right: -6%;
  width: 240px;
  height: 240px;
}

#interests .orb--accent::after {
  animation-duration: 14s;
}

#books .orb--green {
  top: -10%;
  left: 6%;
  width: 240px;
  height: 240px;
}

#books .orb--green::after {
  animation-duration: 11s;
  animation-delay: 1s;
}

#books .orb--accent {
  bottom: -18%;
  right: -10%;
  width: 440px;
  height: 440px;
}

#books .orb--accent::after {
  animation-duration: 9.5s;
  animation-delay: 2.5s;
}

#contacts .orb--green {
  bottom: -20%;
  left: 30%;
  top: auto;
  width: 460px;
  height: 460px;
}

#contacts .orb--green::after {
  animation-duration: 12.5s;
  animation-delay: 0.4s;
}

#contacts .orb--accent {
  top: -10%;
  left: -8%;
  right: auto;
  width: 260px;
  height: 260px;
}

#contacts .orb--accent::after {
  animation-duration: 8s;
  animation-delay: 1.8s;
}

.section__title {
  font-size: clamp(1.8rem, 4vw, 2.4rem);
  font-weight: 800;
  margin-bottom: 0.75rem;
}

.section__subtitle {
  color: var(--text-dim);
  font-size: 1.05rem;
  margin-bottom: 3rem;
  max-width: 640px;
}

.section__more {
  margin-top: 2.5rem;
  color: var(--text-dim);
}

.section__more a {
  color: var(--accent);
  border-bottom: 1px solid transparent;
  transition: border-color var(--transition), color var(--transition);
}

.section__more a:hover,
.section__more a:focus-visible {
  border-color: var(--accent);
}

/* ==========================================================================
   Stats
   ========================================================================== */
.stats {
  position: relative;
  overflow: hidden;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 1.5rem 5rem;
}

.stats__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.5rem;
}

.stat {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 2rem 1.5rem;
  text-align: center;
  transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
  /* .stat sits inside .stats.reveal, which gets its own transform during
     the scroll-reveal transition. Without its own promoted layer, a card
     here can get rasterized against the parent's stale transform matrix
     mid-transition on some mobile GPUs, landing visibly offset from its
     siblings until the next full repaint — see #3. */
  will-change: transform;
}

.stat:hover {
  border-color: var(--border-hover);
  transform: translateY(-4px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}

.stat__number {
  display: block;
  font-size: clamp(2rem, 4vw, 2.75rem);
  font-weight: 800;
  color: var(--green);
  margin-bottom: 0.5rem;
}

.stat__label {
  color: var(--text-dim);
  font-size: 0.9rem;
}

/* ==========================================================================
   Chart — horizontal bar chart block (see blocks.py's render_chart). Bar
   width is relative to the block's own max value, not a fixed scale.
   ========================================================================== */
.chart__list {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

.chart__row {
  display: grid;
  grid-template-columns: minmax(110px, 200px) 1fr auto;
  align-items: center;
  gap: 1rem;
}

.chart__label {
  color: var(--text-dim);
  font-size: 0.92rem;
}

.chart__track {
  position: relative;
  height: 10px;
  border-radius: 999px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  overflow: hidden;
}

.chart__fill {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--green-dim), var(--green));
  transition: width 900ms var(--ease);
}

.chart__value {
  font-weight: 700;
  color: var(--green);
  min-width: 2.5ch;
  text-align: right;
}

@media (max-width: 640px) {
  .chart__row {
    grid-template-columns: 1fr auto;
    grid-template-areas: "label value" "track track";
    row-gap: 0.4rem;
  }

  .chart__label {
    grid-area: label;
  }

  .chart__value {
    grid-area: value;
  }

  .chart__track {
    grid-area: track;
  }
}

/* ==========================================================================
   Table — generic data table block (see blocks.py's render_table). The
   horizontal-scroll shell is the mobile adaptation: columns keep their
   readable width and the table becomes a swipeable strip instead of
   squeezing cell text unreadably thin.
   ========================================================================== */
.table-block__scroll {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg-card);
}

.table-block__table {
  width: 100%;
  min-width: 480px;
  border-collapse: collapse;
  font-size: 0.92rem;
}

.table-block__table th,
.table-block__table td {
  padding: 0.85rem 1.1rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.table-block__table th {
  color: var(--accent);
  font-weight: 700;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--bg-elevated);
}

.table-block__table tbody tr:last-child td {
  border-bottom: none;
}

.table-block__table tbody tr {
  transition: background-color var(--transition-fast);
}

.table-block__table tbody tr:hover {
  background: rgba(var(--green-rgb), 0.06);
}

@media (max-width: 640px) {
  .table-block__table th,
  .table-block__table td {
    padding: 0.65rem 0.85rem;
    font-size: 0.85rem;
  }
}

/* ==========================================================================
   Timeline (experience)
   ========================================================================== */
.timeline {
  display: flex;
  flex-direction: column;
  gap: 2rem;
  position: relative;
  padding-left: 1.75rem;
  border-left: 1px solid var(--border);
}

.timeline__item {
  position: relative;
}

.timeline__heading {
  position: relative;
}

.timeline__marker {
  position: absolute;
  left: -2.28rem;
  top: 50%;
  transform: translateY(-50%);
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid var(--green);
  transition: background-color 600ms var(--ease), box-shadow 600ms var(--ease), transform 600ms var(--ease);
}

.timeline__item.is-visible .timeline__marker {
  background: var(--green);
  box-shadow: 0 0 16px 2px var(--green-glow);
  transform: translateY(-50%) scale(1.15);
}

.timeline__content h3 {
  font-size: 1.15rem;
  font-weight: 700;
  margin-bottom: 0.4rem;
}

.timeline__content p {
  color: var(--text-dim);
}

.todo {
  display: inline-block;
  color: var(--accent);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  margin-left: 0.4rem;
}

/* ==========================================================================
   Cards (projects)
   ========================================================================== */
.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
}

.card {
  position: relative;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.75rem;
  display: flex;
  flex-direction: column;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}

.card:hover,
.card:focus-within {
  transform: translateY(-6px);
  border-color: var(--green-dim);
  box-shadow: 0 16px 40px rgba(61, 220, 122, 0.12);
}

.card__title {
  font-size: 1.2rem;
  font-weight: 700;
  margin-bottom: 0.85rem;
}

.card__desc {
  color: var(--text-dim);
  font-size: 0.95rem;
  flex-grow: 1;
  margin-bottom: 1.25rem;
}

.card__stack {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

.tag {
  font-size: 0.78rem;
  color: var(--green);
  background: rgba(61, 220, 122, 0.08);
  border: 1px solid rgba(61, 220, 122, 0.2);
  border-radius: 999px;
  padding: 0.3rem 0.7rem;
}

.card__link {
  color: var(--accent);
  /*font-weight: 600;*/
  font-size: 0.95rem;
  align-self: flex-start;
  border-bottom: 1px solid transparent;
  transition: border-color var(--transition), color var(--transition);
}

.card__link:hover,
.card__link:focus-visible {
  border-color: var(--accent);
}

/* ==========================================================================
   Channel plug — a small clickable banner pointing at the Telegram-channel
   site (channel.html), same hover-lift language as .card above.
   ========================================================================== */
.channel-plug {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem 1.75rem;
  text-decoration: none;
  color: inherit;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}

.channel-plug:hover,
.channel-plug:focus-visible {
  transform: translateY(-4px);
  border-color: var(--green-dim);
  box-shadow: 0 16px 40px rgba(61, 220, 122, 0.12);
}

.channel-plug__title {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 0.35rem;
}

.channel-plug__desc {
  color: var(--text-dim);
  font-size: 0.92rem;
}

.channel-plug__cta {
  flex-shrink: 0;
  color: var(--accent);
  font-weight: 600;
  font-size: 0.95rem;
  white-space: nowrap;
}

/* ==========================================================================
   Interests (constructor "grid" block — see blocks.py's render_grid)
   Explicit 4-column grid: each tile's column/row span is a per-item field
   set from the constructor's layout menu (grid-column/grid-row, inlined by
   the server), not guessed from item count — so a "featured, bigger" tile
   is a deliberate choice, not something that reflows itself when a sibling
   tile is added or removed elsewhere in the grid.
   ========================================================================== */
.interests {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: minmax(160px, auto);
  gap: 1.5rem;
}

.interest {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.75rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
  transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
}

.interest:hover {
  border-color: var(--green-dim);
  transform: translateY(-4px);
  box-shadow: 0 12px 32px rgba(61, 220, 122, 0.1);
}

.interest h3 {
  color: var(--green);
  font-size: 1.3rem;
  font-weight: 700;
  margin-bottom: 0.6rem;
}

.interest p {
  color: var(--text-dim);
  font-size: 0.95rem;
}

/* --stat: a big accent number instead of the usual title+desc pair */
.interest__number {
  display: block;
  color: var(--green);
  font-size: 2.75rem;
  font-weight: 800;
  line-height: 1.1;
}

.interest__label {
  display: block;
  color: var(--text-dim);
  font-size: 0.95rem;
  margin-top: 0.4rem;
}

/* --link: the whole tile is an <a>, so it needs its own link styling
   instead of inheriting .interest's div defaults */
a.interest {
  text-decoration: none;
  color: inherit;
  position: relative;
}

a.interest:hover {
  border-color: var(--accent);
  box-shadow: 0 12px 32px rgba(255, 181, 69, 0.12);
}

.interest__arrow {
  position: absolute;
  top: 1.5rem;
  right: 1.5rem;
  color: var(--accent);
  font-size: 1.2rem;
  transition: transform var(--transition);
}

a.interest:hover .interest__arrow {
  transform: translateX(4px);
}

/* Below the point where a 4-column grid stops making sense, every tile
   drops to a single column and gives up its span — the constructor's
   layout choices are a desktop-only luxury. */
@media (max-width: 860px) {
  .interests {
    grid-template-columns: 1fr;
  }

  .interest {
    grid-column: auto !important;
    grid-row: auto !important;
  }
}

/* ==========================================================================
   Anime — Аниме block on the main page (see blocks.py's render_anime).
   Every card is a whole-tile <a> to /anime/{slug}, tinted by its own
   --a-accent/--a-accent-rgb custom properties (TZ-anime.md §6, computed
   client-side from the poster — see admin.js) with a fallback chain to the
   site's own --accent when a title has none set.
   ========================================================================== */
.anime-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 170px));
  justify-content: start;
  gap: 1.1rem;
}

.anime-card {
  --a-glow: rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.4);
  --a-soft: rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.16);
  position: relative;
  display: block;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--a-soft);
  background: var(--bg-card);
  color: inherit;
  text-decoration: none;
  aspect-ratio: 2 / 3;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}

.anime-card:hover,
.anime-card:focus-visible {
  transform: translateY(-6px);
  border-color: var(--a-accent, var(--accent));
  box-shadow: 0 16px 40px var(--a-glow);
}.anime-card--featured,
.game-card--featured {
  grid-column: span 2;
  grid-row: span 2;
}

.anime-card__poster {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background-size: cover;
  background-position: center;
}

.anime-card__poster--placeholder {
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-elevated) 10px, var(--bg-elevated) 20px);
}.anime-card__overlay,
.game-card__overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.85), transparent 55%);
  pointer-events: none;
}.anime-card__info,
.game-card__info {
  position: absolute;
  left: 0.9rem;
  right: 0.9rem;
  bottom: 0.8rem;
}

.anime-card__title {
  display: block;
  color: var(--text);
  font-weight: 700;
  font-size: 0.95rem;
  text-shadow: 0 0 12px var(--a-glow);
}.anime-card--featured .anime-card__title,
.game-card--featured .game-card__title {
  font-size: 1.3rem;
}

.anime-card__year {
  display: block;
  color: var(--a-accent, var(--accent));
  font-size: 0.78rem;
  font-weight: 600;
  margin-top: 0.15rem;
}

@media (max-width: 640px) {
  .anime-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 0.9rem;
  }

  .anime-card--featured {
    grid-column: span 2;
    grid-row: span 1;
  }
}

/* ==========================================================================
   Standalone /anime/genre/{slug} catalog page (TZ-anime-genre-pages.md) —
   own .anime-genre__* classes, not a reuse of .anime-card: the page has no
   per-title accent (see anime_genre_page_renderer.py's docstring), so every
   glow/border here falls straight back to the sitewide --accent instead of
   an --a-accent override.
   ========================================================================== */
.anime-genre__title {
  font-size: clamp(1.8rem, 4vw, 2.5rem);
  font-weight: 800;
  letter-spacing: -0.01em;
  text-shadow: 0 0 24px rgba(var(--accent-rgb), 0.4);
  padding-top: 2.5rem;
  margin: 0 0 1rem;
}

.anime-genre__desc {
  color: var(--text-dim);
  max-width: 640px;
  margin: 0 0 2rem;
}

.anime-genre__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 170px));
  justify-content: start;
  gap: 1.1rem;
  margin-bottom: 3rem;
}

.anime-genre__card {
  position: relative;
  display: block;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid rgba(var(--accent-rgb), 0.16);
  background: var(--bg-card);
  color: inherit;
  text-decoration: none;
  aspect-ratio: 2 / 3;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}

.anime-genre__card:hover,
.anime-genre__card:focus-visible {
  transform: translateY(-6px);
  border-color: var(--accent);
  box-shadow: 0 16px 40px rgba(var(--accent-rgb), 0.4);
}

.anime-genre__poster {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.anime-genre__poster--placeholder {
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-elevated) 10px, var(--bg-elevated) 20px);
}

.anime-genre__overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.85), transparent 55%);
  pointer-events: none;
}

.anime-genre__info {
  position: absolute;
  left: 0.9rem;
  right: 0.9rem;
  bottom: 0.8rem;
}

.anime-genre__name {
  display: block;
  color: var(--text);
  font-weight: 700;
  font-size: 0.95rem;
  text-shadow: 0 0 12px rgba(var(--accent-rgb), 0.4);
}

.anime-genre__year {
  display: block;
  color: var(--accent);
  font-size: 0.78rem;
  font-weight: 600;
  margin-top: 0.15rem;
}

@media (max-width: 640px) {
  .anime-genre__grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 0.9rem;
  }
}

/* ==========================================================================
   Books — Литература block on the main page. Every card is a whole-tile
   <a> to /books/{slug} (see blocks.py's render_books), so both variants
   get the same hover-lift + glow language as .card/.contact-link. Each
   card also carries its own --b-accent/--b-accent-rgb (accent_style(),
   prefix="b") extracted from that book's cover, same per-item tinting as
   .anime-card — falls back to the sitewide --green when a book has none.
   ========================================================================== */
.book-highlight {
  --b-glow: rgba(var(--b-accent-rgb, var(--green-rgb)), 0.35);
  display: flex;
  align-items: center;
  gap: 1.25rem;
  background: linear-gradient(135deg, rgba(61, 220, 122, 0.08), rgba(255, 181, 69, 0.04));
  border: 1px solid var(--green-dim);
  border-radius: var(--radius);
  padding: 1.5rem 1.75rem;
  margin: 0 0 2.5rem;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}

.book-highlight:hover,
.book-highlight:focus-visible {
  transform: translateY(-4px);
  border-color: var(--b-accent, var(--green));
  box-shadow: 0 16px 40px var(--b-glow);
}

.book-highlight__icon {
  flex-shrink: 0;
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1;
  color: var(--green);
  letter-spacing: 0.05em;
}

.book-highlight__title {
  font-size: 1.15rem;
  font-weight: 700;
  margin-bottom: 0.2rem;
}

.book-highlight__author {
  color: var(--text-dim);
  font-size: 0.9rem;
}

.book-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
}

.book-list__item {
  --b-soft: rgba(var(--b-accent-rgb, var(--green-rgb)), 0.16);
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.1rem 1.3rem;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}

.book-list__item:hover,
.book-list__item:focus-visible {
  transform: translateY(-4px);
  border-color: var(--b-accent, var(--green-dim));
  box-shadow: 0 12px 32px var(--b-soft);
}

.book-list__title {
  font-weight: 600;
}

.book-list__author {
  color: var(--text-dim);
  font-size: 0.9rem;
}

@media (max-width: 640px) {
  .book-list {
    grid-template-columns: 1fr;
  }
}

/* ==========================================================================
   Book page — the standalone /books/{slug} detail page (see
   book_page_renderer.py). A fixed one-off layout, not a constructor block,
   so it gets its own small class namespace instead of reusing .section/.nav.
   Tinted by --b-accent/--b-accent-rgb, set once on <html class="book-html">
   (accent_style() in blocks.py, prefix="b") so every descendant — and the
   root element's own scrollbar — just reference var(--b-accent, var(--green))
   through inheritance. Same mechanism as .anime-html/.anime-page, own
   variable names so a book's accent never bleeds into the anime section.
   No .cursor-spotlight on this template either — see book_page_renderer.py's
   docstring for why.
   ========================================================================== */
.book-html {
  scrollbar-color: var(--b-accent, var(--green)) var(--bg-elevated);
}

.book-html::-webkit-scrollbar-thumb {
  background-color: var(--b-accent, var(--green));
}

.book-body {
  --b-glow: rgba(var(--b-accent-rgb, var(--green-rgb)), 0.4);
  --b-soft: rgba(var(--b-accent-rgb, var(--green-rgb)), 0.16);
  --b-faint: rgba(var(--b-accent-rgb, var(--green-rgb)), 0.08);
  position: relative;
  min-height: 100vh;
  background-color: var(--bg);
  background-image: radial-gradient(circle at 1px 1px, var(--border) 1px, transparent 0);
  background-size: 26px 26px;
}

.book-body::before {
  content: "";
  position: fixed;
  inset: -10%;
  z-index: -1;
  pointer-events: none;
  filter: blur(120px);
  background:
    radial-gradient(circle at 18% 22%, var(--b-faint), transparent 60%),
    radial-gradient(circle at 82% 26%, var(--b-faint), transparent 60%),
    radial-gradient(circle at 26% 86%, var(--b-faint), transparent 60%),
    radial-gradient(circle at 80% 90%, var(--b-faint), transparent 60%);
}

.book-body ::selection {
  background: var(--b-soft);
}

/* Alternate ambient textures for .book-body's background-image (picked
   per book in the admin — see book_page_renderer.py's _body_class and
   entities.py's BG_PATTERNS). Each one fully replaces the base rule's dot
   grid rather than layering on top of it, so only one texture is ever
   painted at a time. Same set/values as .anime-body--pattern-* — same
   neutral, var(--border) not accent, texture idiom as the default above. */
.book-body--pattern-none {
  background-image: none;
}

.book-body--pattern-grid {
  background-image:
    linear-gradient(var(--border) 1px, transparent 1px),
    linear-gradient(90deg, var(--border) 1px, transparent 1px);
  background-size: 26px 26px;
}

.book-body--pattern-diagonal {
  background-image: repeating-linear-gradient(45deg, var(--border) 0 1px, transparent 1px 18px);
  background-size: auto;
}

.book-body--pattern-crosshatch {
  background-image:
    repeating-linear-gradient(45deg, var(--border) 0 1px, transparent 1px 18px),
    repeating-linear-gradient(-45deg, var(--border) 0 1px, transparent 1px 18px);
  background-size: auto;
}

.book-body--pattern-circles {
  background-image: radial-gradient(circle, transparent 0 9px, var(--border) 9px 10px, transparent 10px);
  background-size: 34px 34px;
}

.book-body--pattern-diamonds {
  background-image:
    linear-gradient(45deg, var(--border) 25%, transparent 25%, transparent 75%, var(--border) 75%),
    linear-gradient(45deg, var(--border) 25%, transparent 25%, transparent 75%, var(--border) 75%);
  background-size: 28px 28px;
  background-position: 0 0, 14px 14px;
}

.book-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 2rem;
  border-bottom: 1px solid var(--b-soft);
  background: linear-gradient(to bottom, var(--b-faint), transparent), rgba(10, 13, 10, 0.7);
  backdrop-filter: blur(8px);
  position: sticky;
  top: 0;
  z-index: 100;
}

.book-nav__inner {
  display: contents;
}

.book-nav__logo {
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  color: var(--b-accent, var(--green));
  text-shadow: 0 0 12px var(--b-glow);
}

.book-nav__back {
  color: var(--text-dim);
  font-size: 0.85rem;
  transition: color var(--transition-fast);
}

.book-nav__back:hover,
.book-nav__back:focus-visible {
  color: var(--b-accent, var(--green));
}

.book-page {
  position: relative;
  max-width: 720px;
  margin: 0 auto;
}

.book-hero {
  position: relative;
  height: 220px;
  display: flex;
  align-items: flex-end;
  padding: 2rem;
}

.book-hero__cover {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.book-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(10, 13, 10, 0) 0%, var(--bg) 95%);
}

.book-hero--placeholder {
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-elevated) 10px, var(--bg-elevated) 20px);
}

.book-hero__inner {
  position: relative;
  z-index: 1;
}

.book-hero__badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.book-hero__marker {
  color: var(--green);
  font-weight: 800;
  font-size: 1.2rem;
}

.book-hero__badge-label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text);
}

.book-hero__title {
  font-size: 2.2rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text);
  text-shadow: 0 0 24px var(--b-glow);
}

.book-content {
  padding: 2rem;
}

.book-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 2rem;
}

.book-meta__author {
  color: var(--text-dim);
  font-size: 1.05rem;
}

.book-meta__period {
  color: var(--text-faint);
  font-size: 0.85rem;
  margin-top: 0.2rem;
}

.book-rating {
  display: flex;
  gap: 5px;
}

.book-rating__dot {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 1.5px solid var(--border-hover);
}

.book-rating__dot--filled {
  background: var(--b-accent, var(--green));
  border-color: var(--b-accent, var(--green));
}

.book-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 2rem;
}

.book-tag {
  font-size: 0.78rem;
  color: var(--b-accent, var(--green));
  background: var(--b-faint, rgba(61, 220, 122, 0.08));
  border: 1px solid var(--b-soft, rgba(61, 220, 122, 0.2));
  border-radius: 999px;
  padding: 0.3rem 0.7rem;
}

.book-review {
  background: var(--bg-card);
  border: 1px solid var(--b-accent, var(--green));
  border-radius: var(--radius);
  padding: 1.6rem 1.75rem;
  box-shadow: 0 0 24px var(--b-soft);
  color: var(--text-dim);
  font-size: 0.95rem;
  line-height: 1.75;
  margin-bottom: 2.5rem;
}

.book-review p {
  margin-bottom: 1rem;
}

.book-review blockquote {
  margin: 1.25rem 0;
  padding: 0.9rem 1.25rem;
  border-left: 2px solid var(--b-accent, var(--green));
  background: rgba(255, 255, 255, 0.03);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-style: italic;
  color: var(--text);
}

.book-review ul,
.book-review ol {
  margin: 0 0 1rem 1.25rem;
  padding: 0;
  list-style: revert;
}

.book-quotes__heading {
  color: var(--text-faint);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 1rem;
}

.book-quotes__list {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.book-quote {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-left: 2px solid var(--b-accent, var(--green));
  border-radius: var(--radius);
  padding: 1.1rem 1.4rem;
  font-style: italic;
  color: var(--text);
  font-size: 0.95rem;
}

.book-404 {
  min-height: 50vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 1rem;
  padding: 3rem 1.5rem;
}

.book-404__code {
  font-size: 3rem;
  font-weight: 800;
  color: var(--green);
}

.book-404__title {
  font-size: 1.5rem;
  font-weight: 700;
}

.book-404__desc {
  color: var(--text-dim);
  max-width: 420px;
}

@media (max-width: 640px) {
  .book-nav {
    padding: 0.85rem 1.25rem;
  }

  .book-hero {
    height: 160px;
    padding: 1.25rem;
  }

  .book-hero__title {
    font-size: 1.5rem;
  }

  .book-content {
    padding: 1.25rem 1.25rem 2rem;
  }
}

/* ==========================================================================
   Anime page — the standalone /anime/{slug} detail page (see
   anime_page_renderer.py). Same one-off-template scope as .book-*, its own
   small class namespace. Tinted by --a-accent/--a-accent-rgb, set once on
   <html class="anime-html"> (accent_style() in blocks.py — see
   anime_page_renderer.py's docstring for why <html> and not <body>/
   .anime-page) so every descendant, and the root element's own scrollbar,
   just reference var(--a-accent, var(--accent)) via inheritance. No
   .cursor-spotlight on this template — see the same docstring for why.

   TZ-anime-games-shared-refactor.md §3: wherever an .anime-* rule and its
   .game-* counterpart (here and in the Game page / Block engine sections
   below) have byte-identical declarations, they're written as one grouped
   selector (`.anime-x,\n.game-x { ... }`) instead of two copies — kept as
   the existing .anime-* / .game-* class names (not renamed to a new shared
   .item-block* namespace) so this stays a pure CSS text dedup with zero
   changes to the Python renderers/admin.js that already emit those exact
   class strings. Rules that differ (hero's structurally different markup,
   --a-accent vs --g-accent custom properties, games-only achievements/
   stats/specs/hero-video/CRT/parallax) stay separate — see
   that TZ for the full inventory of which pairs matched byte-for-byte.
   ========================================================================== */
.anime-html {
  scrollbar-color: var(--a-accent, var(--accent)) var(--bg-elevated);
}

.anime-html::-webkit-scrollbar-thumb {
  background-color: var(--a-accent, var(--accent));
}

.anime-body {
  --a-glow: rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.4);
  --a-soft: rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.16);
  --a-faint: rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.08);
  position: relative;
  min-height: 100vh;
  background-color: var(--bg);
  /* Neutral dot grid — same idiom as the admin builder's own canvas
     backdrop (.a-canvas in admin.css) — instead of a colored wash: texture,
     not tint, so the background itself never competes for attention with
     the title's own neon glow (borders/tags/hero text-shadow below). */
  background-image: radial-gradient(circle at 1px 1px, var(--border) 1px, transparent 0);
  background-size: 26px 26px;
}

/* What actually ties the neutral dots to the title's neon: four large,
   heavily blurred accent-colored glows spread across all four quadrants of
   the viewport (not one spot in a corner, so there's no "left/center/right"
   zone), fixed so they stay put while the page scrolls. Sits between the
   dot grid (painted as part of .anime-body's own background) and the real
   content — z-index: -1 is enough to stay behind .anime-page's normal-flow
   content without touching that content's own stacking — reading as soft
   accent light falling across the texture rather than a colored panel. */
.anime-body::before {
  content: "";
  position: fixed;
  inset: -10%;
  z-index: -1;
  pointer-events: none;
  filter: blur(120px);
  background:
    radial-gradient(circle at 18% 22%, var(--a-faint), transparent 60%),
    radial-gradient(circle at 82% 26%, var(--a-faint), transparent 60%),
    radial-gradient(circle at 26% 86%, var(--a-faint), transparent 60%),
    radial-gradient(circle at 80% 90%, var(--a-faint), transparent 60%);
}

.anime-body ::selection {
  background: var(--a-soft);
}.anime-body--pattern-none,
.game-body--pattern-none {
  background-image: none;
}.anime-body--pattern-grid,
.game-body--pattern-grid {
  background-image:
    linear-gradient(var(--border) 1px, transparent 1px),
    linear-gradient(90deg, var(--border) 1px, transparent 1px);
  background-size: 26px 26px;
}.anime-body--pattern-diagonal,
.game-body--pattern-diagonal {
  background-image: repeating-linear-gradient(45deg, var(--border) 0 1px, transparent 1px 18px);
  background-size: auto;
}.anime-body--pattern-crosshatch,
.game-body--pattern-crosshatch {
  background-image:
    repeating-linear-gradient(45deg, var(--border) 0 1px, transparent 1px 18px),
    repeating-linear-gradient(-45deg, var(--border) 0 1px, transparent 1px 18px);
  background-size: auto;
}.anime-body--pattern-circles,
.game-body--pattern-circles {
  background-image: radial-gradient(circle, transparent 0 9px, var(--border) 9px 10px, transparent 10px);
  background-size: 34px 34px;
}.anime-body--pattern-diamonds,
.game-body--pattern-diamonds {
  background-image:
    linear-gradient(45deg, var(--border) 25%, transparent 25%, transparent 75%, var(--border) 75%),
    linear-gradient(45deg, var(--border) 25%, transparent 25%, transparent 75%, var(--border) 75%);
  background-size: 28px 28px;
  background-position: 0 0, 14px 14px;
}

.anime-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 2rem;
  border-bottom: 1px solid var(--a-soft);
  background: linear-gradient(to bottom, var(--a-faint), transparent), rgba(10, 13, 10, 0.7);
  backdrop-filter: blur(8px);
  position: sticky;
  top: 0;
  z-index: 100;
}.anime-nav__inner,
.game-nav__inner {
  display: contents;
}

.anime-nav__logo {
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  color: var(--a-accent, var(--accent));
  text-shadow: 0 0 12px var(--a-glow);
}.anime-nav__back,
.game-nav__back {
  color: var(--text-dim);
  font-size: 0.85rem;
  transition: color var(--transition-fast);
}

.anime-nav__back:hover,
.anime-nav__back:focus-visible {
  color: var(--a-accent, var(--accent));
}

.anime-page {
  position: relative;
  max-width: 1600px;
  margin: 0 auto;
  padding: 0 2rem 4rem;
}

/* Every anime page sits in the same 1600px-wide .anime-page and is a plain
   vertical stack of blocks now (TZ-anime-constructor.md) — the author
   composes hero/note/soundtrack/gallery/... in whatever order and
   combination they like via the constructor, so there's no fixed "tracks
   sit beside the note" layout to special-case anymore; every block (hero
   included) just stretches to the full width and stacks in DOM order,
   spaced by .anime-block's own margin-top (see below). */

.anime-hero {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 2rem;
  padding: 2.5rem 0 1.5rem;
}

.anime-hero__poster {
  border-radius: var(--radius);
  overflow: hidden;
  border: 2px solid var(--a-accent, var(--accent));
  box-shadow: 0 0 30px var(--a-glow), 0 20px 44px rgba(0, 0, 0, 0.5);
  aspect-ratio: 2 / 3;
}

.anime-hero__poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.anime-hero__poster--placeholder {
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-elevated) 10px, var(--bg-elevated) 20px);
}

.anime-hero__title {
  font-size: clamp(1.8rem, 4vw, 2.5rem);
  font-weight: 800;
  letter-spacing: -0.01em;
  text-shadow: 0 0 24px var(--a-glow);
  margin: 0 0 0.6rem;
}

/* Fallback <h1> when the author removed the hero block entirely
   (TZ-anime-constructor.md §3.1) — same weight/glow as .anime-hero__title
   so the page still reads as "an anime title page" without a poster. */
.anime-page__title {
  font-size: clamp(1.8rem, 4vw, 2.5rem);
  font-weight: 800;
  letter-spacing: -0.01em;
  text-shadow: 0 0 24px var(--a-glow);
  padding-top: 2.5rem;
  margin: 0;
}

.anime-meta {
  color: var(--a-accent, var(--accent));
  font-weight: 700;
  font-size: 1.05rem;
  margin: 0 0 1rem;
}

.anime-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.anime-tag {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 700;
  color: #06060a;
  background: var(--a-accent, var(--accent));
  border-radius: 999px;
  padding: 0.3rem 0.75rem;
  box-shadow: 0 0 14px var(--a-glow);
  transition: transform var(--transition), box-shadow var(--transition);
}

/* Tags link to /anime/genre/{slug} (TZ-anime-genre-pages.md §4) — a small
   lift + stronger glow marks them interactive, same language every other
   clickable element on the site uses. */
.anime-tag:hover,
.anime-tag:focus-visible {
  transform: translateY(-1px);
  box-shadow: 0 0 22px var(--a-glow);
}

/* Generic wrapper every anime block type (except hero, which keeps its own
   historical .anime-hero markup) renders through — anime_blocks.py's
   _block_shell. Owns the vertical rhythm between blocks (margin-top) so
   individual block types (.anime-note/.anime-gallery/.anime-tracks/...)
   don't each need their own top-spacing rule. */
.anime-block {
  position: relative;
  overflow: hidden;
  display: block;
  margin-top: 2.5rem;
}.anime-block__inner,
.game-block__inner {
  position: relative;
  z-index: 1;
}

.anime-block__title {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--a-accent, var(--accent));
  text-shadow: 0 0 10px var(--a-glow);
  margin: 0 0 1.2rem;
}.anime-block__subtitle,
.game-block__subtitle {
  color: var(--text-dim);
  font-size: 0.95rem;
  margin: -0.7rem 0 1.2rem;
}.anime-block-bg-anim,
.game-block-bg-anim {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  border-radius: inherit;
}.anime-block-bg-anim--drift-orbs::before,
.anime-block-bg-anim--drift-orbs::after,
.game-block-bg-anim--drift-orbs::before,
.game-block-bg-anim--drift-orbs::after {
  content: '';
  position: absolute;
  width: 42%;
  padding-bottom: 42%;
  border-radius: 50%;
  filter: blur(60px);
}

.anime-block-bg-anim--drift-orbs::before {
  left: 6%;
  top: 8%;
  background: radial-gradient(circle, rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.22), transparent 70%);
  animation: bgAnimDrift1 26s ease-in-out infinite;
}

.anime-block-bg-anim--drift-orbs::after {
  right: 8%;
  bottom: 6%;
  background: radial-gradient(circle, rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.16), transparent 70%);
  animation: bgAnimDrift2 32s ease-in-out infinite;
}

.anime-block-bg-anim--aurora {
  background: linear-gradient(115deg,
    transparent 20%,
    rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.14) 40%,
    rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.10) 55%,
    transparent 75%);
  background-size: 250% 250%;
  animation: bgAnimAurora 34s ease-in-out infinite;
}

.anime-block-bg-anim--grain-pulse {
  background: radial-gradient(ellipse 70% 60% at 50% 40%, rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.16), transparent 70%);
  animation: bgAnimPulse 14s ease-in-out infinite;
}

.anime-block-bg-anim--soft-grid {
  background-image:
    linear-gradient(rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.08) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.08) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(circle at 50% 50%, black, transparent 75%);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, black, transparent 75%);
  animation: bgAnimGridPan 40s linear infinite;
}

.anime-block-bg-anim--floating-particles::before,
.anime-block-bg-anim--floating-particles::after {
  content: '';
  position: absolute;
  inset: -20% 0;
  background-image: radial-gradient(circle, rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.35) 0 2px, transparent 3px);
  background-repeat: repeat;
  background-size: 90px 120px;
}.anime-block-bg-anim--floating-particles::before,
.game-block-bg-anim--floating-particles::before {
  background-position: 10px 20px;
  animation: bgAnimFloat 22s linear infinite;
}.anime-block-bg-anim--floating-particles::after,
.game-block-bg-anim--floating-particles::after {
  background-position: 60px 80px;
  opacity: 0.6;
  animation: bgAnimFloat 30s linear infinite reverse;
}

.anime-block-bg-anim--gradient-sweep {
  background: radial-gradient(ellipse 45% 70% at 50% 50%, rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.18), transparent 70%);
  background-repeat: no-repeat;
  animation: bgAnimSweep 28s ease-in-out infinite;
}

.anime-block-bg-anim--slow-rotate::before {
  content: '';
  position: absolute;
  inset: -30%;
  background: conic-gradient(from 0deg,
    rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.10),
    rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.06),
    rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.10));
  filter: blur(50px);
  animation: bgAnimRotate 60s linear infinite;
}

.anime-block-bg-anim--pulse-glow {
  background: radial-gradient(circle at 50% 45%, rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.22), transparent 60%);
  animation: bgAnimPulseGlow 9s ease-in-out infinite;
}

.anime-block-bg-anim--rising-bubbles {
  background-image: radial-gradient(circle, rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.22) 0 5px, transparent 6px);
  background-repeat: repeat;
  background-size: 70px 140px;
  animation: bgAnimRiseUp 20s linear infinite;
}

.anime-block-bg-anim--wave-lines {
  background-image: repeating-linear-gradient(
    -6deg,
    rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.10) 0px,
    rgba(var(--a-accent-rgb, var(--accent-rgb)), 0.10) 2px,
    transparent 2px,
    transparent 34px
  );
  background-size: 200% 200%;
  animation: bgAnimWave 24s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .anime-block-bg-anim,
  .anime-block-bg-anim::before,
  .anime-block-bg-anim::after {
    animation: none !important;
  }
}

.anime-note {
  background: var(--bg-card);
  border: 1px solid var(--a-accent, var(--accent));
  border-radius: var(--radius);
  padding: 1.6rem 1.75rem;
  box-shadow: 0 0 24px var(--a-soft);
}.anime-note p,
.game-note p {
  color: var(--text-dim);
  line-height: 1.75;
  margin: 0 0 1rem;
}.anime-note p:last-child,
.game-note p:last-child {
  margin-bottom: 0;
}

.anime-note blockquote {
  margin: 1.25rem 0;
  padding: 0.9rem 1.25rem;
  border-left: 2px solid var(--a-accent, var(--accent));
  background: rgba(255, 255, 255, 0.03);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-style: italic;
  color: var(--text);
}.anime-note ul,
.anime-note ol,
.game-note ul,
.game-note ol {
  margin: 0 0 1rem 1.25rem;
  padding: 0;
  list-style: revert;
  color: var(--text-dim);
}.anime-gallery,
.game-gallery {
  column-count: 3;
  column-gap: 0.9rem;
}.anime-gallery__item,
.game-gallery__item {
  display: block;
  break-inside: avoid;
  margin-bottom: 0.9rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--border);
  cursor: zoom-in;
  position: relative;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}

.anime-gallery__item:hover,
.anime-gallery__item:focus-visible {
  transform: scale(1.03);
  box-shadow: 0 14px 34px var(--a-soft);
}.anime-gallery__item img,
.game-gallery__item img {
  width: 100%;
  display: block;
}.anime-gallery__caption,
.game-gallery__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0.5rem 0.7rem;
  font-size: 0.75rem;
  color: var(--text);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
}

.anime-lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  background: radial-gradient(circle at 50% 50%, var(--a-faint), transparent 65%), rgba(5, 7, 5, 0.92);
  cursor: zoom-out;
}

/* Extra side padding on pointer:fine (mouse) devices reserves room for the
   prev/next arrows below, so the image's own max-width never grows into
   the space an arrow sits in — "never overlaps the image" is guaranteed
   structurally rather than by careful z-index/positioning. Touch devices
   get the plain padding above and swipe (script.js) instead of arrows. */
@media (pointer: fine) {
  .anime-lightbox {
    padding: 2rem 5.5rem;
  }
}.anime-lightbox[hidden],
.game-lightbox[hidden] {
  display: none;
}.anime-lightbox__img,
.game-lightbox__img {
  max-width: min(1100px, 92vw);
  max-height: 88vh;
  object-fit: contain;
  border-radius: var(--radius-sm);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
}

/* Crossfade between shots when flipping — script.js toggles is-changing
   around the src swap. Gated behind reduced-motion like the rest of the
   site's animation. */
@media (prefers-reduced-motion: no-preference) {
  .anime-lightbox__img {
    transition: opacity 0.18s ease;
  }

  .anime-lightbox__img.is-changing {
    opacity: 0;
  }
}

.anime-lightbox__arrow {
  display: none;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  border: 1px solid var(--a-accent, var(--accent));
  background: var(--bg-card);
  color: var(--a-accent, var(--accent));
  cursor: pointer;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}

/* Only pointer:fine gets arrows — touch pages the lightbox by swipe
   instead (script.js). Higher specificity than both rules above so a
   single-image gallery (script.js sets `hidden`) stays hidden regardless
   of pointer type. */
@media (pointer: fine) {
  .anime-lightbox__arrow {
    display: inline-flex;
  }
}.anime-lightbox__arrow[hidden],
.game-lightbox__arrow[hidden] {
  display: none;
}.anime-lightbox__arrow svg,
.game-lightbox__arrow svg {
  width: 1.2rem;
  height: 1.2rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.anime-lightbox__arrow:hover:not(:disabled),
.anime-lightbox__arrow:focus-visible:not(:disabled) {
  transform: translateY(-50%) scale(1.08);
  box-shadow: 0 0 20px var(--a-glow);
}.anime-lightbox__arrow:disabled,
.game-lightbox__arrow:disabled {
  opacity: 0.35;
  cursor: default;
}.anime-lightbox__arrow--prev,
.game-lightbox__arrow--prev {
  left: 1.25rem;
}.anime-lightbox__arrow--next,
.game-lightbox__arrow--next {
  right: 1.25rem;
}.anime-tracks,
.game-tracks {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}.anime-track,
.game-track {
  display: flex;
  align-items: center;
  gap: 1rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.9rem 1.1rem;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.anime-track.is-playing {
  border-color: var(--a-accent, var(--accent));
  box-shadow: 0 0 24px var(--a-soft);
}

.anime-track__play {
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 1px solid var(--a-accent, var(--accent));
  background: transparent;
  color: var(--a-accent, var(--accent));
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}

.anime-track__play:hover,
.anime-track__play:focus-visible {
  transform: scale(1.08);
  box-shadow: 0 0 18px var(--a-glow);
}

.anime-track.is-playing .anime-track__play {
  background: var(--a-accent, var(--accent));
  color: #06060a;
  box-shadow: 0 0 20px var(--a-glow);
}.anime-track__icon,
.game-track__icon {
  width: 1.05rem;
  height: 1.05rem;
  fill: currentColor;
}.anime-track__icon--pause,
.game-track__icon--pause {
  display: none;
}.anime-track.is-playing .anime-track__icon--play,
.game-track.is-playing .game-track__icon--play {
  display: none;
}.anime-track.is-playing .anime-track__icon--pause,
.game-track.is-playing .game-track__icon--pause {
  display: block;
}.anime-track__body,
.game-track__body {
  flex: 1;
  min-width: 0;
}.anime-track__title,
.game-track__title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 0.6rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}.anime-track__bar,
.game-track__bar {
  position: relative;
  height: 22px;
  padding: 8px 0;
  background-clip: content-box;
  border-radius: 999px;
  background-color: var(--bg-elevated);
  cursor: pointer;
  touch-action: none;
}

.anime-track__bar-fill {
  position: absolute;
  top: 8px;
  left: 0;
  height: 6px;
  border-radius: 999px;
  width: 0%;
  background: var(--a-accent, var(--accent));
  transition: width 0.1s linear, box-shadow var(--transition-fast);
}

.anime-track.is-playing .anime-track__bar-fill {
  box-shadow: 0 0 10px var(--a-glow);
}

.anime-track__bar-fill::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -5px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--a-accent, var(--accent));
  transform: translateY(-50%) scale(0);
  transition: transform var(--transition-fast);
}.anime-track__bar:hover .anime-track__bar-fill::after,
.anime-track.is-playing .anime-track__bar-fill::after,
.game-track__bar:hover .game-track__bar-fill::after,
.game-track.is-playing .game-track__bar-fill::after {
  transform: translateY(-50%) scale(1);
}.anime-track__time,
.game-track__time {
  display: flex;
  gap: 0.3rem;
  font-size: 0.75rem;
  color: var(--text-dim);
  margin-top: 0.5rem;
}.anime-track__time-sep,
.game-track__time-sep {
  opacity: 0.6;
}

/* ==========================================================================
   Games — Игры block on the main page (see blocks.py's render_games).
   Same whole-tile-card shape as .anime-card, own --g-accent/--g-accent-rgb
   custom properties (TZ-games.md, computed client-side from the cover —
   see admin.js) with a fallback chain to the site's own --accent when a
   game has none set.
   ========================================================================== */
.games-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 170px));
  justify-content: start;
  gap: 1.1rem;
}

.game-card {
  --g-glow: rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.4);
  --g-soft: rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.16);
  position: relative;
  display: block;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--g-soft);
  background: var(--bg-card);
  color: inherit;
  text-decoration: none;
  aspect-ratio: 3 / 4;
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}

.game-card:hover,
.game-card:focus-visible {
  transform: translateY(-6px);
  border-color: var(--g-accent, var(--accent));
  box-shadow: 0 16px 40px var(--g-glow);
}

.game-card__cover {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background-size: cover;
  background-position: center;
}

.game-card__cover--placeholder {
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-elevated) 10px, var(--bg-elevated) 20px);
}

.game-card__title {
  display: block;
  color: var(--text);
  font-weight: 700;
  font-size: 0.95rem;
  text-shadow: 0 0 12px var(--g-glow);
}

.game-card__platform {
  display: block;
  color: var(--g-accent, var(--accent));
  font-size: 0.78rem;
  font-weight: 600;
  margin-top: 0.15rem;
}

@media (max-width: 640px) {
  .games-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 0.9rem;
  }

  .game-card--featured {
    grid-column: span 2;
    grid-row: span 1;
  }
}

/* ==========================================================================
   Game page — the standalone /games/{slug} detail page (see
   game_page_renderer.py), now a free-order block list per
   TZ-games-constructor.md rather than a fixed section order. Own small
   class namespace, same one-off-template scope as .book-*/.anime-*.
   Tinted by --g-accent/--g-accent-rgb, set once on <html class="game-html">
   so every descendant — and the root element's own scrollbar — reference
   var(--g-accent, var(--accent)) via inheritance. No .cursor-spotlight on
   this template either, same reasoning as the book/anime pages. The
   gallery/lightbox and every other block type get their own `.game-*`
   rules further down (see "Block engine" below) — games stopped reusing
   anime's gallery DOM/CSS/lightbox id once the constructor landed (§3.2).
   ========================================================================== */
.game-html {
  scrollbar-color: var(--g-accent, var(--accent)) var(--bg-elevated);
}

.game-html::-webkit-scrollbar-thumb {
  background-color: var(--g-accent, var(--accent));
}

.game-body {
  --g-glow: rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.4);
  --g-soft: rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.16);
  --g-faint: rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.08);
  position: relative;
  min-height: 100vh;
  background-color: var(--bg);
  background-image: radial-gradient(circle at 1px 1px, var(--border) 1px, transparent 0);
  background-size: 26px 26px;
}

.game-body::before {
  content: "";
  position: fixed;
  inset: -10%;
  z-index: -1;
  pointer-events: none;
  filter: blur(120px);
  background:
    radial-gradient(circle at 18% 22%, var(--g-faint), transparent 60%),
    radial-gradient(circle at 82% 26%, var(--g-faint), transparent 60%),
    radial-gradient(circle at 26% 86%, var(--g-faint), transparent 60%),
    radial-gradient(circle at 80% 90%, var(--g-faint), transparent 60%);
}

.game-body ::selection {
  background: var(--g-soft);
}

.game-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 2rem;
  border-bottom: 1px solid var(--g-soft);
  background: linear-gradient(to bottom, var(--g-faint), transparent), rgba(10, 13, 10, 0.7);
  backdrop-filter: blur(8px);
  position: sticky;
  top: 0;
  z-index: 100;
}

.game-nav__logo {
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  color: var(--g-accent, var(--accent));
  text-shadow: 0 0 12px var(--g-glow);
}

.game-nav__back:hover,
.game-nav__back:focus-visible {
  color: var(--g-accent, var(--accent));
}

.game-page {
  position: relative;
  max-width: 720px;
  margin: 0 auto;
  padding-bottom: 4rem;
}

.game-hero {
  position: relative;
  min-height: 220px;
  display: flex;
  align-items: flex-end;
  padding: 2rem;
  overflow: hidden;
}

.game-hero__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.game-hero__cover,
.game-hero__bg-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.game-hero__bg-video {
  position: absolute;
  inset: 0;
}

.game-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(10, 13, 10, 0) 0%, var(--bg) 95%);
}

.game-hero--placeholder {
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-elevated) 10px, var(--bg-elevated) 20px);
}

.game-hero__inner {
  position: relative;
  z-index: 1;
}

/* Ретро/CRT-эффект (TZ-games-constructor.md §3.1) — статичные сканлайны
   (не анимация, остаются и при prefers-reduced-motion) плюс редкий,
   короткий глитч-импульс, который script.js включает через .is-glitching
   (движение — отключается под reduced-motion, см. script.js). */
.game-hero__crt {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.22) 0px,
    rgba(0, 0, 0, 0.22) 1px,
    transparent 1px,
    transparent 3px
  );
  mix-blend-mode: multiply;
}

.game-hero--crt.is-glitching .game-hero__bg {
  animation: game-hero-glitch 0.22s steps(2, jump-none);
}

.game-hero--crt.is-glitching .game-hero__inner {
  animation: game-hero-glitch-text 0.22s steps(2, jump-none);
}

@keyframes game-hero-glitch {
  0% { transform: translate3d(0, 0, 0); filter: none; }
  30% { transform: translate3d(-4px, 0, 0); filter: hue-rotate(8deg); }
  60% { transform: translate3d(3px, 0, 0); filter: hue-rotate(-6deg); }
  100% { transform: translate3d(0, 0, 0); filter: none; }
}

@keyframes game-hero-glitch-text {
  0% { transform: translate3d(0, 0, 0); }
  30% { transform: translate3d(2px, 0, 0); }
  60% { transform: translate3d(-2px, 0, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* Параллакс (TZ-games-constructor.md §3.1) — transform выставляется
   scrollY-driven кодом в script.js (initGameHeroParallax); will-change тут
   только подсказка браузеру, что слой будет двигаться. */
.game-hero--parallax .game-hero__bg,
.game-hero--parallax .game-hero__inner {
  will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
  .game-hero--crt.is-glitching .game-hero__bg,
  .game-hero--crt.is-glitching .game-hero__inner {
    animation: none;
  }
}

.game-hero__badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.game-hero__badge-label {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--g-accent, var(--accent));
}

.game-hero__title {
  font-size: 2.2rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text);
  text-shadow: 0 0 24px var(--g-glow);
}

.game-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--border);
  margin-bottom: 2rem;
}

.game-meta__platform {
  color: var(--text-dim);
  font-size: 1.05rem;
}

.game-meta__year {
  color: var(--text-faint);
  font-size: 0.85rem;
}

.game-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 2rem;
}

.game-tag {
  font-size: 0.78rem;
  color: var(--g-accent, var(--accent));
  background: var(--g-faint);
  border: 1px solid var(--g-soft);
  border-radius: 999px;
  padding: 0.3rem 0.7rem;
}

/* ==========================================================================
   Block engine (TZ-games-constructor.md) — mirrors the .anime-block*
   family class-for-class under its own `game-*` namespace (§2.1: block
   engines don't share CSS classes between title pages), tinted by the
   game's own --g-accent/--g-glow/--g-soft/--g-faint instead of anime's
   --a-*. .game-content/.game-review (the old fixed-section styling) are
   gone — every section is now its own `.game-block` (see game_blocks.py),
   starting with .game-note below.
   ========================================================================== */
.game-block {
  position: relative;
  overflow: hidden;
  display: block;
  margin-top: 2.5rem;
  padding: 0 2rem;
}

.game-block__title {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--g-accent, var(--accent));
  text-shadow: 0 0 10px var(--g-glow);
  margin: 0 0 1.2rem;
}

.game-block-bg-anim--drift-orbs::before {
  left: 6%;
  top: 8%;
  background: radial-gradient(circle, rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.22), transparent 70%);
  animation: bgAnimDrift1 26s ease-in-out infinite;
}

.game-block-bg-anim--drift-orbs::after {
  right: 8%;
  bottom: 6%;
  background: radial-gradient(circle, rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.16), transparent 70%);
  animation: bgAnimDrift2 32s ease-in-out infinite;
}

.game-block-bg-anim--aurora {
  background: linear-gradient(115deg,
    transparent 20%,
    rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.14) 40%,
    rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.10) 55%,
    transparent 75%);
  background-size: 250% 250%;
  animation: bgAnimAurora 34s ease-in-out infinite;
}

.game-block-bg-anim--grain-pulse {
  background: radial-gradient(ellipse 70% 60% at 50% 40%, rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.16), transparent 70%);
  animation: bgAnimPulse 14s ease-in-out infinite;
}

.game-block-bg-anim--soft-grid {
  background-image:
    linear-gradient(rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.08) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.08) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(circle at 50% 50%, black, transparent 75%);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, black, transparent 75%);
  animation: bgAnimGridPan 40s linear infinite;
}

.game-block-bg-anim--floating-particles::before,
.game-block-bg-anim--floating-particles::after {
  content: '';
  position: absolute;
  inset: -20% 0;
  background-image: radial-gradient(circle, rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.35) 0 2px, transparent 3px);
  background-repeat: repeat;
  background-size: 90px 120px;
}

.game-block-bg-anim--gradient-sweep {
  background: radial-gradient(ellipse 45% 70% at 50% 50%, rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.18), transparent 70%);
  background-repeat: no-repeat;
  animation: bgAnimSweep 28s ease-in-out infinite;
}

.game-block-bg-anim--slow-rotate::before {
  content: '';
  position: absolute;
  inset: -30%;
  background: conic-gradient(from 0deg,
    rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.10),
    rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.06),
    rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.10));
  filter: blur(50px);
  animation: bgAnimRotate 60s linear infinite;
}

.game-block-bg-anim--pulse-glow {
  background: radial-gradient(circle at 50% 45%, rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.22), transparent 60%);
  animation: bgAnimPulseGlow 9s ease-in-out infinite;
}

.game-block-bg-anim--rising-bubbles {
  background-image: radial-gradient(circle, rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.22) 0 5px, transparent 6px);
  background-repeat: repeat;
  background-size: 70px 140px;
  animation: bgAnimRiseUp 20s linear infinite;
}

.game-block-bg-anim--wave-lines {
  background-image: repeating-linear-gradient(
    -6deg,
    rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.10) 0px,
    rgba(var(--g-accent-rgb, var(--accent-rgb)), 0.10) 2px,
    transparent 2px,
    transparent 34px
  );
  background-size: 200% 200%;
  animation: bgAnimWave 24s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .game-block-bg-anim,
  .game-block-bg-anim::before,
  .game-block-bg-anim::after {
    animation: none !important;
  }
}

.game-note {
  background: var(--bg-card);
  border: 1px solid var(--g-accent, var(--accent));
  border-radius: var(--radius);
  padding: 1.6rem 1.75rem;
  box-shadow: 0 0 24px var(--g-soft);
}

.game-note blockquote {
  margin: 1.25rem 0;
  padding: 0.9rem 1.25rem;
  border-left: 2px solid var(--g-accent, var(--accent));
  background: rgba(255, 255, 255, 0.03);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-style: italic;
  color: var(--text);
}

.game-gallery__item:hover,
.game-gallery__item:focus-visible {
  transform: scale(1.03);
  box-shadow: 0 14px 34px var(--g-soft);
}

.game-lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  background: radial-gradient(circle at 50% 50%, var(--g-faint), transparent 65%), rgba(5, 7, 5, 0.92);
  cursor: zoom-out;
}

@media (pointer: fine) {
  .game-lightbox {
    padding: 2rem 5.5rem;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .game-lightbox__img {
    transition: opacity 0.18s ease;
  }

  .game-lightbox__img.is-changing {
    opacity: 0;
  }
}

.game-lightbox__arrow {
  display: none;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  border: 1px solid var(--g-accent, var(--accent));
  background: var(--bg-card);
  color: var(--g-accent, var(--accent));
  cursor: pointer;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}

@media (pointer: fine) {
  .game-lightbox__arrow {
    display: inline-flex;
  }
}

.game-lightbox__arrow:hover:not(:disabled),
.game-lightbox__arrow:focus-visible:not(:disabled) {
  transform: translateY(-50%) scale(1.08);
  box-shadow: 0 0 20px var(--g-glow);
}

.game-track.is-playing {
  border-color: var(--g-accent, var(--accent));
  box-shadow: 0 0 24px var(--g-soft);
}

.game-track__play {
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 1px solid var(--g-accent, var(--accent));
  background: transparent;
  color: var(--g-accent, var(--accent));
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}

.game-track__play:hover,
.game-track__play:focus-visible {
  transform: scale(1.08);
  box-shadow: 0 0 18px var(--g-glow);
}

.game-track.is-playing .game-track__play {
  background: var(--g-accent, var(--accent));
  color: #06060a;
  box-shadow: 0 0 20px var(--g-glow);
}

.game-track__bar-fill {
  position: absolute;
  top: 8px;
  left: 0;
  height: 6px;
  border-radius: 999px;
  width: 0%;
  background: var(--g-accent, var(--accent));
  transition: width 0.1s linear, box-shadow var(--transition-fast);
}

.game-track.is-playing .game-track__bar-fill {
  box-shadow: 0 0 10px var(--g-glow);
}

.game-track__bar-fill::after {
  content: "";
  position: absolute;
  top: 50%;
  right: -5px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--g-accent, var(--accent));
  transform: translateY(-50%) scale(0);
  transition: transform var(--transition-fast);
}

@media (pointer: coarse) {
  .game-lane__track {
    scroll-snap-type: x mandatory;
    cursor: default;
    touch-action: pan-y pinch-zoom;
  }
}

.game-lane__progress-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 20%;
  background: var(--g-accent, var(--accent));
  border-radius: inherit;
}

.game-timeline-item__marker {
  position: absolute;
  top: 0.7rem;
  left: 0;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--bg-elevated);
  border: 2px solid var(--g-accent, var(--accent));
  z-index: 1;
}

.game-timeline-item--done .game-timeline-item__marker {
  background: var(--g-accent, var(--accent));
  box-shadow: 0 0 10px var(--g-glow);
}

.game-timeline-item--current .game-timeline-item__marker {
  box-shadow: 0 0 0 4px var(--g-soft);
}

.game-timeline-item__date {
  color: var(--g-accent, var(--accent));
  font-size: 0.75rem;
  font-weight: 700;
  margin-bottom: 0.3rem;
}

.game-related-item:hover,
.game-related-item:focus-visible {
  transform: translateY(-4px);
  border-color: var(--g-accent, var(--accent));
  box-shadow: 0 14px 30px var(--g-soft);
}

.game-related-item__cover {
  aspect-ratio: 2 / 3;
  background: var(--bg-elevated);
}

.game-related-item__cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.game-related-item__cover--placeholder {
  width: 100%;
  height: 100%;
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-elevated) 10px, var(--bg-elevated) 20px);
}

.game-related-item__platform {
  color: var(--g-accent, var(--accent));
  font-size: 0.75rem;
  font-weight: 700;
}

.game-characters__line {
  stroke: var(--g-accent, var(--accent));
  stroke-width: 1.5;
  opacity: 0.4;
}

.game-characters__card:hover,
.game-characters__card:focus-visible {
  border-color: var(--g-accent, var(--accent));
  box-shadow: 0 10px 24px var(--g-soft);
  transform: translateY(-3px);
}

.game-characters__photo {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  overflow: hidden;
  margin: 0 auto 0.6rem;
  background: var(--bg-elevated);
  border: 2px solid var(--g-accent, var(--accent));
}

.game-characters__role {
  color: var(--g-accent, var(--accent));
  font-size: 0.72rem;
  font-weight: 700;
  margin-top: 0.25rem;
}

@media (prefers-reduced-motion: reduce) {
  .game-characters__detail {
    transition: none;
  }
}

.game-spoiler__toggle {
  background: var(--bg-card);
  border: 1px solid var(--g-accent, var(--accent));
  color: var(--g-accent, var(--accent));
  border-radius: 999px;
  padding: 0.55rem 1.1rem;
  font-weight: 700;
  font-size: 0.85rem;
  cursor: pointer;
  transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}

.game-spoiler__toggle:hover,
.game-spoiler__toggle:focus-visible {
  box-shadow: 0 0 16px var(--g-soft);
  transform: translateY(-2px);
}

.game-spoiler.is-open .game-spoiler__toggle {
  border-color: var(--g-accent, var(--accent));
  box-shadow: 0 0 12px var(--g-soft);
}

@media (prefers-reduced-motion: reduce) {
  .game-spoiler__content {
    transition: none;
  }
}

/* ---- quote (own .game-quote, distinct from blocks.py's/anime's) ---- */
.game-quote {
  margin: 0;
  padding: 1.75rem 2rem;
  border-left: 3px solid var(--g-accent, var(--accent));
  background: var(--bg-card);
  border-radius: 0 var(--radius) var(--radius) 0;
}

.game-quote__author {
  color: var(--g-accent, var(--accent));
  font-weight: 700;
  font-size: 0.85rem;
}

.game-links__pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1.1rem;
  border-radius: 999px;
  border: 1px solid var(--g-accent, var(--accent));
  color: var(--g-accent, var(--accent));
  font-weight: 700;
  font-size: 0.85rem;
  background: var(--bg-card);
  transition: box-shadow var(--transition-fast), transform var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}

.game-links__pill:hover,
.game-links__pill:focus-visible {
  background: var(--g-accent, var(--accent));
  color: #06060a;
  box-shadow: 0 0 16px var(--g-glow);
  transform: translateY(-2px);
}

.game-rating__fill {
  fill: none;
  stroke: var(--g-accent, var(--accent));
  stroke-width: 8;
  stroke-linecap: round;
  stroke-dasharray: 326.726;
  stroke-dashoffset: 326.726;
  transition: stroke-dashoffset 1.1s ease;
  filter: drop-shadow(0 0 6px var(--g-glow));
}

.game-rating__value {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.9rem;
  font-weight: 800;
  color: var(--text);
  text-shadow: 0 0 16px var(--g-glow);
}

@media (prefers-reduced-motion: reduce) {
  .game-rating__fill {
    transition: none;
  }
}

.game-compare__divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 3px;
  background: var(--g-accent, var(--accent));
  box-shadow: 0 0 12px var(--g-glow);
  cursor: ew-resize;
  transform: translateX(-50%);
  touch-action: none;
}

.game-compare__handle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--g-accent, var(--accent));
  box-shadow: 0 0 16px var(--g-glow);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ==========================================================================
   Games-only block types (TZ-games-constructor.md §3.13-§3.15) — no anime
   equivalent.
   ========================================================================== */

/* ---- achievements: trophy grid, tri-state unlocked/locked/neutral pin ---- */
.game-achievements {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1rem;
  margin-top: 1.25rem;
}

.game-achievement {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.9rem 1rem;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast), opacity var(--transition-fast);
}

.game-achievement__icon {
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-elevated);
}

.game-achievement__icon img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.game-achievement__icon--placeholder {
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 6px, var(--bg-elevated) 6px, var(--bg-elevated) 12px);
}

.game-achievement__body {
  min-width: 0;
}

.game-achievement__name {
  font-weight: 700;
  font-size: 0.92rem;
}

.game-achievement__desc {
  color: var(--text-dim);
  font-size: 0.8rem;
  line-height: 1.5;
  margin: 0.2rem 0 0;
}

/* Unlocked — same accent-glow pin every other interactive card on the site
   uses; locked — muted, not blocked (this is a manual showcase, not a real
   tracker, see TZ §7); unset (neither modifier) renders neutrally. */
.game-achievement--unlocked {
  border-color: var(--g-accent, var(--accent));
  box-shadow: 0 0 20px var(--g-soft);
}

.game-achievement--locked {
  opacity: 0.55;
}

/* ---- stats: free-form label/value pairs as big accent numbers ---- */
.game-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 1.5rem;
  margin-top: 1.25rem;
  text-align: center;
}

.game-stat__value {
  font-size: 2.1rem;
  font-weight: 800;
  color: var(--g-accent, var(--accent));
  text-shadow: 0 0 16px var(--g-glow);
}

.game-stat__label {
  color: var(--text-dim);
  font-size: 0.82rem;
  margin-top: 0.3rem;
}

/* ---- specs: two independent minimum/recommended columns ---- */
.game-specs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.25rem;
  margin-top: 1.25rem;
}

.game-specs__column {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem 1.4rem;
}

.game-specs__column-title {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--g-accent, var(--accent));
  margin: 0 0 0.9rem;
}

.game-specs__rows {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}

.game-specs__row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 0.55rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.85rem;
}

.game-specs__row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.game-specs__row-label {
  color: var(--text-dim);
}

.game-specs__row-value {
  color: var(--text);
  font-weight: 600;
  text-align: right;
}

.game-404__code {
  font-size: 3rem;
  font-weight: 800;
  color: var(--accent);
}

@media (max-width: 640px) {
  .game-nav {
    padding: 0.85rem 1.25rem;
  }

  .game-hero {
    min-height: 160px;
    padding: 1.25rem;
  }

  .game-hero__title {
    font-size: 1.5rem;
  }

  .game-block {
    padding: 0 1.25rem;
  }

  .game-gallery {
    column-count: 2;
    column-gap: 0.6rem;
  }

  .game-track {
    padding: 0.75rem 0.85rem;
    gap: 0.75rem;
  }

  .game-track__play {
    width: 2.4rem;
    height: 2.4rem;
  }

  .game-track__title {
    font-size: 0.88rem;
  }

  .game-timeline-item {
    flex-basis: 200px;
  }
}.anime-lane,
.game-lane {
  position: relative;
  margin-top: 1.25rem;
}.anime-lane__track,
.game-lane__track {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 0.5rem 0.15rem 0.75rem;
  cursor: grab;
  touch-action: pan-y;
}.anime-lane__track::-webkit-scrollbar,
.game-lane__track::-webkit-scrollbar {
  display: none;
}.anime-lane__track.is-dragging,
.game-lane__track.is-dragging {
  cursor: grabbing;
  scroll-behavior: auto;
}

@media (pointer: coarse) {
  .anime-lane__track {
    scroll-snap-type: x mandatory;
    cursor: default;
    touch-action: pan-y pinch-zoom;
  }
}.anime-lane__progress,
.game-lane__progress {
  position: relative;
  height: 3px;
  background: var(--border);
  border-radius: 999px;
  overflow: hidden;
}

.anime-lane__progress-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 20%;
  background: var(--a-accent, var(--accent));
  border-radius: inherit;
}

/* Per-card reveal-on-lane-scroll — off entirely under reduced-motion (cards
   are visible immediately; only the drag/swipe navigation remains, see
   TZ-anime-constructor.md §3.3). */
@media (prefers-reduced-motion: no-preference) {
  [data-lane-item] {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 0.5s ease, transform 0.5s ease;
  }

  [data-lane-item].is-lane-visible {
    opacity: 1;
    transform: translateY(0);
  }
}.anime-timeline-item,
.game-timeline-item {
  position: relative;
  flex: 0 0 240px;
  scroll-snap-align: start;
  padding-top: 2.1rem;
  display: flex;
  flex-direction: column;
}

.anime-timeline-item__marker {
  position: absolute;
  top: 0.7rem;
  left: 0;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--bg-elevated);
  border: 2px solid var(--a-accent, var(--accent));
  z-index: 1;
}

.anime-timeline-item--done .anime-timeline-item__marker {
  background: var(--a-accent, var(--accent));
  box-shadow: 0 0 10px var(--a-glow);
}

.anime-timeline-item--current .anime-timeline-item__marker {
  box-shadow: 0 0 0 4px var(--a-soft);
}.anime-timeline-item--planned .anime-timeline-item__marker,
.game-timeline-item--planned .game-timeline-item__marker {
  opacity: 0.45;
}.anime-timeline-item::after,
.game-timeline-item::after {
  content: "";
  position: absolute;
  top: calc(0.7rem + 6px);
  left: 14px;
  right: -1.25rem;
  height: 2px;
  background: var(--border);
}.anime-timeline-item:last-child::after,
.game-timeline-item:last-child::after {
  display: none;
}.anime-timeline-item__image,
.game-timeline-item__image {
  border-radius: var(--radius-sm);
  overflow: hidden;
  margin-bottom: 0.7rem;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--border);
}.anime-timeline-item__image img,
.game-timeline-item__image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}.anime-timeline-item__body,
.game-timeline-item__body {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.9rem 1rem;
  flex: 1;
}

.anime-timeline-item__date {
  color: var(--a-accent, var(--accent));
  font-size: 0.75rem;
  font-weight: 700;
  margin-bottom: 0.3rem;
}.anime-timeline-item__label,
.game-timeline-item__label {
  font-weight: 700;
  margin-bottom: 0.3rem;
}.anime-timeline-item__desc,
.game-timeline-item__desc {
  color: var(--text-dim);
  font-size: 0.85rem;
  line-height: 1.55;
}.anime-related-item,
.game-related-item {
  flex: 0 0 160px;
  scroll-snap-align: start;
  display: block;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--bg-card);
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
}

.anime-related-item:hover,
.anime-related-item:focus-visible {
  transform: translateY(-4px);
  border-color: var(--a-accent, var(--accent));
  box-shadow: 0 14px 30px var(--a-soft);
}

.anime-related-item__poster {
  aspect-ratio: 2 / 3;
  background: var(--bg-elevated);
}

.anime-related-item__poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.anime-related-item__poster--placeholder {
  width: 100%;
  height: 100%;
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 10px, var(--bg-elevated) 10px, var(--bg-elevated) 20px);
}.anime-related-item__info,
.game-related-item__info {
  padding: 0.6rem 0.7rem;
}.anime-related-item__title,
.game-related-item__title {
  display: block;
  font-weight: 700;
  font-size: 0.85rem;
  margin-bottom: 0.2rem;
}

.anime-related-item__year {
  color: var(--a-accent, var(--accent));
  font-size: 0.75rem;
  font-weight: 700;
}.anime-characters,
.game-characters {
  position: relative;
  margin-top: 1.25rem;
}.anime-characters__lines,
.game-characters__lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
  overflow: visible;
}

.anime-characters__line {
  stroke: var(--a-accent, var(--accent));
  stroke-width: 1.5;
  opacity: 0.4;
}.anime-characters__grid,
.game-characters__grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 1.1rem;
}.anime-characters__grid.is-scheme-layout,
.game-characters__grid.is-scheme-layout {
  display: block;
  min-height: 480px;
}.anime-characters__grid.is-scheme-layout .anime-characters__card,
.game-characters__grid.is-scheme-layout .game-characters__card {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 130px;
}.anime-characters__grid.is-scheme-layout .anime-characters__card:hover,
.anime-characters__grid.is-scheme-layout .anime-characters__card:focus-visible,
.game-characters__grid.is-scheme-layout .game-characters__card:hover,
.game-characters__grid.is-scheme-layout .game-characters__card:focus-visible {
  transform: translate(-50%, -50%) translateY(-3px);
}.anime-characters__card,
.game-characters__card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.8rem;
  text-align: center;
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast);
}.anime-characters__card[role="button"],
.game-characters__card[role="button"] {
  cursor: pointer;
}

.anime-characters__card:hover,
.anime-characters__card:focus-visible {
  border-color: var(--a-accent, var(--accent));
  box-shadow: 0 10px 24px var(--a-soft);
  transform: translateY(-3px);
}

.anime-characters__photo {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  overflow: hidden;
  margin: 0 auto 0.6rem;
  background: var(--bg-elevated);
  border: 2px solid var(--a-accent, var(--accent));
}.anime-characters__photo img,
.game-characters__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}.anime-characters__photo--placeholder,
.game-characters__photo--placeholder {
  background-image: repeating-linear-gradient(135deg, var(--bg-card), var(--bg-card) 6px, var(--bg-elevated) 6px, var(--bg-elevated) 12px);
}.anime-characters__name,
.game-characters__name {
  font-weight: 700;
  font-size: 0.9rem;
}

.anime-characters__role {
  color: var(--a-accent, var(--accent));
  font-size: 0.72rem;
  font-weight: 700;
  margin-top: 0.25rem;
}.anime-characters__detail,
.game-characters__detail {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.35s ease;
  text-align: left;
  font-size: 0.8rem;
  color: var(--text-dim);
}.anime-characters__detail-inner,
.game-characters__detail-inner {
  overflow: hidden;
}.anime-characters__card.is-expanded .anime-characters__detail,
.game-characters__card.is-expanded .game-characters__detail {
  grid-template-rows: 1fr;
  margin-top: 0.6rem;
}.anime-characters__va,
.game-characters__va {
  font-weight: 600;
  color: var(--text);
  margin-bottom: 0.3rem;
}.anime-characters__detail p,
.game-characters__detail p {
  margin: 0;
  line-height: 1.5;
}

@media (prefers-reduced-motion: reduce) {
  .anime-characters__detail {
    transition: none;
  }
}.anime-spoiler,
.game-spoiler {
  margin-top: 1rem;
}

.anime-spoiler__toggle {
  background: var(--bg-card);
  border: 1px solid var(--a-accent, var(--accent));
  color: var(--a-accent, var(--accent));
  border-radius: 999px;
  padding: 0.55rem 1.1rem;
  font-weight: 700;
  font-size: 0.85rem;
  cursor: pointer;
  transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}

.anime-spoiler__toggle:hover,
.anime-spoiler__toggle:focus-visible {
  box-shadow: 0 0 16px var(--a-soft);
  transform: translateY(-2px);
}.anime-spoiler__content,
.game-spoiler__content {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.4s ease;
}.anime-spoiler__content-inner,
.game-spoiler__content-inner {
  overflow: hidden;
}.anime-spoiler.is-open .anime-spoiler__content,
.game-spoiler.is-open .game-spoiler__content {
  grid-template-rows: 1fr;
  margin-top: 1rem;
}

.anime-spoiler.is-open .anime-spoiler__toggle {
  border-color: var(--a-accent, var(--accent));
  box-shadow: 0 0 12px var(--a-soft);
}

@media (prefers-reduced-motion: reduce) {
  .anime-spoiler__content {
    transition: none;
  }
}

/* ==========================================================================
   Quote — anime-scoped (TZ-anime-constructor.md §3.8), deliberately its own
   type/CSS distinct from blocks.py's index/channel .quote-block per the
   TZ's §0 decision.
   ========================================================================== */
.anime-quote {
  margin: 0;
  padding: 1.75rem 2rem;
  border-left: 3px solid var(--a-accent, var(--accent));
  background: var(--bg-card);
  border-radius: 0 var(--radius) var(--radius) 0;
}.anime-quote__text,
.game-quote__text {
  font-size: 1.3rem;
  font-weight: 700;
  font-style: italic;
  color: var(--text);
  margin: 0 0 0.6rem;
  line-height: 1.5;
}

.anime-quote__author {
  color: var(--a-accent, var(--accent));
  font-weight: 700;
  font-size: 0.85rem;
}.anime-quote__author::before,
.game-quote__author::before {
  content: "— ";
}.anime-links,
.game-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 1rem;
}

.anime-links__pill {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1.1rem;
  border-radius: 999px;
  border: 1px solid var(--a-accent, var(--accent));
  color: var(--a-accent, var(--accent));
  font-weight: 700;
  font-size: 0.85rem;
  background: var(--bg-card);
  transition: box-shadow var(--transition-fast), transform var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}

.anime-links__pill:hover,
.anime-links__pill:focus-visible {
  background: var(--a-accent, var(--accent));
  color: #06060a;
  box-shadow: 0 0 16px var(--a-glow);
  transform: translateY(-2px);
}.anime-links__icon,
.game-links__icon {
  width: 1rem;
  height: 1rem;
  flex: none;
}.anime-rating,
.game-rating {
  position: relative;
  width: 150px;
  height: 150px;
  margin-top: 1rem;
}.anime-rating__svg,
.game-rating__svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}.anime-rating__track,
.game-rating__track {
  fill: none;
  stroke: var(--border);
  stroke-width: 8;
}

.anime-rating__fill {
  fill: none;
  stroke: var(--a-accent, var(--accent));
  stroke-width: 8;
  stroke-linecap: round;
  stroke-dasharray: 326.726;
  stroke-dashoffset: 326.726;
  transition: stroke-dashoffset 1.1s ease;
  filter: drop-shadow(0 0 6px var(--a-glow));
}

.anime-rating__value {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.9rem;
  font-weight: 800;
  color: var(--text);
  text-shadow: 0 0 16px var(--a-glow);
}

@media (prefers-reduced-motion: reduce) {
  .anime-rating__fill {
    transition: none;
  }
}.anime-compare,
.game-compare {
  position: relative;
  margin-top: 1rem;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  user-select: none;
}.anime-compare__image,
.game-compare__image {
  position: absolute;
  inset: 0;
}.anime-compare__image img,
.game-compare__image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  pointer-events: none;
}.anime-compare__image--after,
.game-compare__image--after {
  clip-path: inset(0 0 0 50%);
}.anime-compare__caption,
.game-compare__caption {
  position: absolute;
  bottom: 0.6rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
}.anime-compare__caption--before,
.game-compare__caption--before {
  left: 0.6rem;
}.anime-compare__caption--after,
.game-compare__caption--after {
  right: 0.6rem;
}

.anime-compare__divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 3px;
  background: var(--a-accent, var(--accent));
  box-shadow: 0 0 12px var(--a-glow);
  cursor: ew-resize;
  transform: translateX(-50%);
  touch-action: none;
}

.anime-compare__handle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--a-accent, var(--accent));
  box-shadow: 0 0 16px var(--a-glow);
  display: flex;
  align-items: center;
  justify-content: center;
}.anime-compare__handle svg,
.game-compare__handle svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: #06060a;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}.anime-video,
.game-video {
  margin-top: 1rem;
}.anime-video__player,
.anime-video__iframe,
.game-video__player,
.game-video__iframe {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: #000;
}.anime-video__caption,
.game-video__caption {
  margin-top: 0.6rem;
  color: var(--text-dim);
  font-size: 0.85rem;
}.anime-404,
.game-404 {
  min-height: 50vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 1rem;
  padding: 3rem 1.5rem;
}

.anime-404__code {
  font-size: 3rem;
  font-weight: 800;
  color: var(--green);
}.anime-404__title,
.game-404__title {
  font-size: 1.5rem;
  font-weight: 700;
}.anime-404__desc,
.game-404__desc {
  color: var(--text-dim);
  max-width: 420px;
}

@media (max-width: 780px) {
  .anime-hero {
    grid-template-columns: 1fr;
  }

  .anime-hero__poster {
    /* Not max-width: a grid item with aspect-ratio and auto margins but no
       definite width resolves its fit-content size to 0 in Chromium
       instead of stretching — width: min(...) gives it one. */
    width: min(260px, 100%);
    margin: 0 auto;
  }

  .anime-gallery {
    column-count: 2;
  }
}

@media (max-width: 640px) {
  .anime-nav {
    padding: 0.85rem 1.25rem;
  }

  .anime-page {
    padding: 0 1.25rem 3rem;
  }

  .anime-hero__title {
    font-size: 1.6rem;
  }

  .anime-gallery {
    column-count: 2;
    column-gap: 0.6rem;
  }

  .anime-track {
    padding: 0.75rem 0.85rem;
    gap: 0.75rem;
  }

  .anime-track__play {
    width: 2.4rem;
    height: 2.4rem;
  }

  .anime-track__title {
    font-size: 0.88rem;
  }

  .anime-timeline-item {
    flex-basis: 200px;
  }
}

/* ==========================================================================
   Contacts
   ========================================================================== */
.contacts {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.25rem;
  margin-bottom: 3rem;
}

.contact-link {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  transition: transform var(--transition), border-color var(--transition), box-shadow var(--transition);
}

.contact-link:hover,
.contact-link:focus-visible {
  transform: translateY(-4px);
  border-color: var(--green);
  box-shadow: 0 12px 32px var(--green-glow);
}

.contact-link__icon {
  width: 26px;
  height: 26px;
  margin-bottom: 0.2rem;
  color: var(--green);
  transition: color var(--transition);
}

.contact-link__icon svg {
  display: block;
  width: 100%;
  height: 100%;
}

.contact-link:hover .contact-link__icon,
.contact-link:focus-visible .contact-link__icon {
  color: var(--accent);
}

.contact-link--featured .contact-link__icon {
  width: 30px;
  height: 30px;
}

.contact-link__label {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}

.contact-link__value {
  font-weight: 600;
  color: var(--text);
  word-break: break-word;
}

.contact-link--featured {
  grid-column: 1 / -1;
  padding: 1.75rem;
  background: linear-gradient(120deg, rgba(61, 220, 122, 0.14), rgba(255, 181, 69, 0.05));
  border-color: var(--green-dim);
}

.contact-link--featured .contact-link__label {
  color: var(--text-dim);
  text-transform: none;
  letter-spacing: normal;
  font-size: 0.95rem;
}

.contact-link--featured .contact-link__value {
  font-size: 1.4rem;
}

.farewell {
  text-align: center;
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--green);
}

/* ==========================================================================
   Per-section spacing rhythm — avoids every section reading as the same
   block repeated five times.
   ========================================================================== */
#experience {
  padding-top: 5rem;
  padding-bottom: 7rem;
}

#projects {
  padding-top: 7rem;
  padding-bottom: 7rem;
}

#interests {
  padding-top: 6rem;
  padding-bottom: 5rem;
}

#books {
  padding-top: 5rem;
  padding-bottom: 6rem;
}

#contacts {
  padding-top: 7rem;
  padding-bottom: 8rem;
}

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 900px) {
  .cards {
    grid-template-columns: 1fr;
  }

  .contacts {
    grid-template-columns: 1fr;
  }

  .stats__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 700px) {
  .nav__links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    background: var(--bg);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 12px 24px rgba(0, 0, 0, 0.5);
    padding: 1rem 1.5rem 1.5rem;
    gap: 1rem;
    transform: translateY(-8px);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition), transform var(--transition);
  }

  .nav__links.is-open {
    transform: translateY(0);
    opacity: 1;
    pointer-events: auto;
  }

  .nav__toggle {
    display: flex;
  }

  .nav__toggle.is-open span:nth-child(1) {
    transform: translateY(7px) rotate(45deg);
  }

  .nav__toggle.is-open span:nth-child(2) {
    opacity: 0;
  }

  .nav__toggle.is-open span:nth-child(3) {
    transform: translateY(-7px) rotate(-45deg);
  }

  .hero {
    padding-top: 7rem;
  }

  .hero__content {
    flex-direction: column;
    text-align: center;
  }

  .hero__cta {
    flex-direction: column;
    align-items: stretch;
  }

  .section {
    padding: 4rem 1.25rem;
  }

  .channel-plug {
    flex-direction: column;
    align-items: flex-start;
  }
}

@media (max-width: 480px) {
  .stats__grid {
    grid-template-columns: 1fr;
  }

  .nav__inner {
    padding: 1rem;
  }
}

/* ==========================================================================
   Reduced motion
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .reveal,
  .btn,
  .card,
  .stat,
  .interest,
  .contact-link,
  .nav__links a::after {
    transition: none !important;
  }

  .reveal {
    opacity: 1;
    transform: none;
  }

  .hero__scroll-hint span {
    animation: none;
  }

  .hero__glow {
    animation: none !important;
  }

  .orb {
    transition: none !important;
  }

  .orb::after {
    animation: none !important;
  }

  .cursor-spotlight {
    display: none;
  }
}

/* ==========================================================================
   Quote block (constructor-added block type)
   ========================================================================== */
.quote-block {
  max-width: 560px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.75rem 2rem;
  text-align: center;
}

.quote-block__text {
  font-size: 1.1rem;
  font-style: italic;
  margin-bottom: 0.75rem;
  line-height: 1.6;
}

.quote-block__author {
  color: var(--accent);
  font-size: 0.85rem;
  font-weight: 600;
  font-style: normal;
}

/* ==========================================================================
   Text block (constructor-added block type)
   ========================================================================== */
.text-block {
  color: var(--text-dim);
  font-size: 0.98rem;
  line-height: 1.75;
  max-width: 640px;
}

/* ==========================================================================
   Block background animations (constructor "Анимация фона" field — see
   blocks.py's _bg_animation_html / admin.js's BG_ANIMATIONS). Ten calm,
   loop-safe presets, opt-in per block, off by default. Sits at z-index: 0
   behind each section's content layer (.section__inner / .hero__content /
   .stats__grid, all z-index: 1) and clips to its own box via `overflow:
   hidden`, so it never needs the host section's own overflow touched.
   ========================================================================== */
.bg-anim {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  border-radius: inherit;
}

/* 1. Drift orbs — two soft blurred circles wandering independently */
.bg-anim--drift-orbs::before,
.bg-anim--drift-orbs::after {
  content: '';
  position: absolute;
  width: 42%;
  padding-bottom: 42%;
  border-radius: 50%;
  filter: blur(60px);
}

.bg-anim--drift-orbs::before {
  left: 6%;
  top: 8%;
  background: radial-gradient(circle, rgba(var(--green-rgb), 0.22), transparent 70%);
  animation: bgAnimDrift1 26s ease-in-out infinite;
}

.bg-anim--drift-orbs::after {
  right: 8%;
  bottom: 6%;
  background: radial-gradient(circle, rgba(var(--accent-rgb), 0.18), transparent 70%);
  animation: bgAnimDrift2 32s ease-in-out infinite;
}

@keyframes bgAnimDrift1 {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(8%, 12%); }
}

@keyframes bgAnimDrift2 {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(-10%, -8%); }
}

/* 2. Aurora — wide diagonal color band sweeping slowly */
.bg-anim--aurora {
  background: linear-gradient(115deg,
    transparent 20%,
    rgba(var(--green-rgb), 0.14) 40%,
    rgba(var(--accent-rgb), 0.14) 55%,
    transparent 75%);
  background-size: 250% 250%;
  animation: bgAnimAurora 34s ease-in-out infinite;
}

@keyframes bgAnimAurora {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}

/* 3. Grain pulse — faint vignette breathing */
.bg-anim--grain-pulse {
  background: radial-gradient(ellipse 70% 60% at 50% 40%, rgba(var(--green-rgb), 0.16), transparent 70%);
  animation: bgAnimPulse 14s ease-in-out infinite;
}

@keyframes bgAnimPulse {
  0%, 100% { opacity: 0.45; }
  50% { opacity: 1; }
}

/* 4. Soft grid — faint ruled lines panning diagonally */
.bg-anim--soft-grid {
  background-image:
    linear-gradient(rgba(var(--green-rgb), 0.08) 1px, transparent 1px),
    linear-gradient(90deg, rgba(var(--green-rgb), 0.08) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(circle at 50% 50%, black, transparent 75%);
  -webkit-mask-image: radial-gradient(circle at 50% 50%, black, transparent 75%);
  animation: bgAnimGridPan 40s linear infinite;
}

@keyframes bgAnimGridPan {
  0% { background-position: 0 0, 0 0; }
  100% { background-position: 46px 46px, 46px 46px; }
}

/* 5. Floating particles — small dots drifting upward at two speeds */
.bg-anim--floating-particles::before,
.bg-anim--floating-particles::after {
  content: '';
  position: absolute;
  inset: -20% 0;
  background-image: radial-gradient(circle, rgba(var(--accent-rgb), 0.35) 0 2px, transparent 3px);
  background-repeat: repeat;
  background-size: 90px 120px;
}

.bg-anim--floating-particles::before {
  background-position: 10px 20px;
  animation: bgAnimFloat 22s linear infinite;
}

.bg-anim--floating-particles::after {
  background-position: 60px 80px;
  opacity: 0.6;
  animation: bgAnimFloat 30s linear infinite reverse;
}

@keyframes bgAnimFloat {
  0% { transform: translateY(0); }
  100% { transform: translateY(-120px); }
}

/* 6. Gradient sweep — soft glow sweeping left to right */
.bg-anim--gradient-sweep {
  background: radial-gradient(ellipse 45% 70% at 50% 50%, rgba(var(--accent-rgb), 0.18), transparent 70%);
  background-repeat: no-repeat;
  animation: bgAnimSweep 28s ease-in-out infinite;
}

@keyframes bgAnimSweep {
  0%, 100% { background-position: -20% 50%; }
  50% { background-position: 120% 50%; }
}

/* 7. Slow rotate — large soft conic blob rotating gently */
.bg-anim--slow-rotate::before {
  content: '';
  position: absolute;
  inset: -30%;
  background: conic-gradient(from 0deg,
    rgba(var(--green-rgb), 0.10),
    rgba(var(--accent-rgb), 0.10),
    rgba(var(--green-rgb), 0.10));
  filter: blur(50px);
  animation: bgAnimRotate 60s linear infinite;
}

@keyframes bgAnimRotate {
  to { transform: rotate(360deg); }
}

/* 8. Pulse glow — single centered glow breathing */
.bg-anim--pulse-glow {
  background: radial-gradient(circle at 50% 45%, rgba(var(--accent-rgb), 0.22), transparent 60%);
  animation: bgAnimPulseGlow 9s ease-in-out infinite;
}

@keyframes bgAnimPulseGlow {
  0%, 100% { transform: scale(0.92); opacity: 0.6; }
  50% { transform: scale(1.05); opacity: 1; }
}

/* 9. Rising bubbles — a steady grid of soft circles rising, seamless loop */
.bg-anim--rising-bubbles {
  background-image: radial-gradient(circle, rgba(var(--green-rgb), 0.22) 0 5px, transparent 6px);
  background-repeat: repeat;
  background-size: 70px 140px;
  animation: bgAnimRiseUp 20s linear infinite;
}

@keyframes bgAnimRiseUp {
  0% { background-position: 0 0; }
  100% { background-position: 0 -140px; }
}

/* 10. Wave lines — gently shifting diagonal stripes */
.bg-anim--wave-lines {
  background-image: repeating-linear-gradient(
    -6deg,
    rgba(var(--accent-rgb), 0.10) 0px,
    rgba(var(--accent-rgb), 0.10) 2px,
    transparent 2px,
    transparent 34px
  );
  background-size: 200% 200%;
  animation: bgAnimWave 24s ease-in-out infinite;
}

@keyframes bgAnimWave {
  0%, 100% { background-position: 0% 0%; }
  50% { background-position: 12% 8%; }
}

@media (prefers-reduced-motion: reduce) {
  .bg-anim,
  .bg-anim::before,
  .bg-anim::after {
    animation: none !important;
  }
}

/* ==========================================================================
   Error pages (403 / 404 / 500 / 503) — error_page_renderer.py.
   Tinted by --err-accent/--err-accent-rgb, set once on <html class=
   "error-html"> by accent_style() (same mechanism as book covers/anime
   posters), falling back to the site's own --accent when the author hasn't
   picked a seed color for "errors" in /admin yet.
   ========================================================================== */
.error-body {
  --err-glow: rgba(var(--err-accent-rgb, var(--accent-rgb)), 0.35);
  --err-soft: rgba(var(--err-accent-rgb, var(--accent-rgb)), 0.14);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
}

.error-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 2rem;
  border-bottom: 1px solid var(--border);
}

.error-nav__inner {
  display: contents;
}

.error-nav__logo {
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  color: var(--err-accent, var(--accent));
}

.error-nav__back {
  color: var(--text-dim);
  font-size: 0.85rem;
  transition: color var(--transition-fast);
}

.error-nav__back:hover,
.error-nav__back:focus-visible {
  color: var(--err-accent, var(--accent));
}

.error-page {
  flex: 1;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 3rem 1.5rem;
}

.error-page::before {
  content: "";
  position: absolute;
  inset: -20%;
  background: radial-gradient(circle at 50% 40%, var(--err-soft), transparent 60%);
  pointer-events: none;
}

.error-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
  max-width: 480px;
  text-align: center;
}

.error-page__code {
  font-size: 5.5rem;
  font-weight: 800;
  line-height: 1;
  color: var(--err-accent, var(--accent));
  text-shadow: 0 0 40px var(--err-glow);
}

.error-page__title {
  font-size: 1.5rem;
  font-weight: 700;
}

.error-page__desc {
  color: var(--text-dim);
}

.error-page__contact {
  margin-top: 0.25rem;
  font-size: 0.9rem;
  color: var(--text-faint);
}

.error-page__contact a {
  color: var(--err-accent, var(--accent));
  transition: color var(--transition-fast);
}

.error-page__contact a:hover,
.error-page__contact a:focus-visible {
  color: var(--text);
}

@media (max-width: 640px) {
  .error-nav {
    padding: 0.85rem 1.25rem;
  }

  .error-page__code {
    font-size: 4rem;
  }
}
