:root {
  --main-bg-color: rgb(255, 255, 255);
  --main-text-color: rgb(0, 0, 0);
  --header-bg-color: #D4F078;
  --navi-bg-color: #383838;
  --navi-text-color: #ffffff;
  --header-height: 45px;
  --navi-height: 20px;

  --font-size-base: 14px;
  --font-size-b3: 0.51rem; /* 6.14px */
  --font-size-b2: 0.64rem; /* 7.68px */
  --font-size-b1: 0.80rem; /* 9.6px */
  --font-size-0: 1.00rem; /* 12px */
  --font-size-h5: 1.25rem; /* 15px */
  --font-size-h4: 1.56rem; /* 18.75px */
  --font-size-h3: 1.95rem; /* 23.44px */
  --font-size-h2: 2.44rem; /* 29.3px */
  --font-size-h1: 3.05rem; /* 36.62px */

  --banner-height: 52px;
  --bar-height: 44px;
  --rule-color: #1a1a1a;
  --content-gap: 16px; /* uniform gap between the top/bottom rules and the work area */

  /* Footer band height — every view pads its bottom by this, and the fixed
     .bottom-bar fills it. Mobile swaps it for a compact fixed height (see the
     mobile section at the end of this file). */
  --footer-h: 20vh;

  /* Translucent backdrop for the fixed header/footer (blur shows content). */
  --chrome-bg: rgba(255, 255, 255, 0.55);

  /* Bottom toolbar type: a touch larger than the 12px body base. */
  --bar-font-size: 15.5px;

  /* Shared rhythm for the page transitions. Bump these to taste — everything
     keys off them. --vt-slow: the header glide / footer-bar travel / content
     slide; --vt-cross: the quick stagger beat + the overlay footer-word fade. */
  --vt-slow: 0.5s;
  --vt-cross: 0.2s;
  --vt-ease: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Light OFF = "Slide + Text" theme: warm light-grey page, dark text, mounts
   take their category colour. */
html[data-light="off"] {
  --main-bg-color: #c7c9c1;
  --main-text-color: #1a1a1a;
  --rule-color: #2e2e2e;
}

/* Light-off ambient: edge falloff makes the grey page read as a lit surface.
   Fixed overlay above page and chrome but below the projector surfaces (post/
   document headers at 950, viewer at 1000). Kept at opacity 0 so the toggle
   fades it in rather than popping it. */
body::after {
  background: radial-gradient(115% 115% at 50% 42%, transparent 30%, rgba(8, 8, 8, 0.5) 100%);
  content: "";
  inset: 0;
  opacity: 0;
  pointer-events: none;
  position: fixed;
  transition: opacity 0.2s ease;
  z-index: 940;
}

html[data-light="off"] body::after { opacity: 1; }

html {
  /* Paint the canvas at the root: during a cross-document swap the browser
     briefly shows the bare canvas (defaults to WHITE) — a blink between two
     grey light-off pages. data-light is server-side, so this is correct from
     the first frame. */
  background-color: var(--main-bg-color);
  display: block;
  width: 100%;
}

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

body {
  align-items: center;
  background-color: var(--main-bg-color);
  color: var(--main-text-color);
  display: flex;
  transition: background-color 0.2s ease;
  flex-flow: column;
  font-family: "News Gothic Condensed", "Arial Narrow", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
  font-size: var(--font-size-base);
  width: 100%;
}

h1, h2, h3, h4, h5, h6 {
  font-style: normal;
  text-wrap-style: balance;
}

h5 {
  font-size: var(--font-size-h5);
}

h4 {
  font-size: var(--font-size-h4);
}

h3 {
  font-size: var(--font-size-h3);
}

h2 {
  font-size: var(--font-size-h2);
}

h1 {
  font-size: var(--font-size-h1);
}

/* ── Top banner (Works / Viewer / About / Archive) ──────────────────────────── */
.top-banner {
  align-items: end;
  background: var(--main-bg-color);
  display: flex;
  transition: background-color 0.2s ease;
  font-size: var(--font-size-h4);
  height: var(--banner-height);
  justify-content: space-between;
  left: 0;
  padding: 0 16px 8px;
  position: fixed;
  top: 0;
  width: 100%;
  z-index: 900;
}

/* Inset rule (line #1) under the banner, matching the home page. */
.top-banner::after {
  background: var(--rule-color);
  bottom: 0;
  content: "";
  height: 1px;
  left: 16px;
  position: absolute;
  right: 16px;
}

.top-banner .banner-left,
.top-banner .banner-right {
  color: inherit;
  text-decoration: none;
}

/* ── Shared banner inner (Home / Works / Viewer) ────────────────────────────── */
/* The inset rule + name layout, reused everywhere so spacing/lines match. */
.banner-inner {
  align-items: end;
  border-bottom: 1px solid var(--rule-color);
  display: flex;
  font-size: var(--font-size-h4);
  justify-content: space-between;
  margin: 0 16px;
  padding: 4px 0 8px; /* names → line gap; mirrors the footer's line → items gap */
}

.banner-inner .banner-left { color: inherit; text-decoration: none; }

/* ── Bottom toolbar ─────────────────────────────────────────────────────────── */
/* The footer occupies the bottom 20% of the viewport. Its content (rule + items)
   sits at the top of that area; the rule is the same inset distance from the
   items as the banner rule is from the names. */
.bottom-bar {
  background: var(--main-bg-color);
  bottom: 0;
  display: flex;
  flex-direction: column;
  font-size: var(--bar-font-size);
  height: var(--footer-h);
  justify-content: flex-start; /* line + nav + buttons sit at the top of the footer area */
  left: 0;
  padding: 0;
  position: fixed;
  transition: background-color 0.2s ease;
  width: 100%;
  z-index: 900;
}

.bar-inner {
  align-items: center;
  border-top: 1px solid var(--rule-color);
  display: flex;
  font-size: var(--bar-font-size);
  gap: 24px;
  justify-content: space-between;
  margin: 0 16px;
  padding: 8px 0 0; /* line #2 → items gap; matches the banner's text → line #1 gap */
}

.bottom-bar .bar-nav {
  display: flex;
  gap: 32px;
}

.bottom-bar a,
.bar-link {
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  font: inherit;
  padding: 0;
  text-decoration: none;
}

.bottom-bar a:hover,
.bar-link:hover {
  opacity: 0.6;
}

/* Second footer row (gallery views): view tools — Index toggle / Filter /
   Sort / Search — sitting under the primary nav row. */
.bar-tools {
  align-items: baseline;
  display: flex;
  gap: 32px;
  margin: 0 16px;
  padding: 10px 0 0;
}

/* Toolbar groups (Works footer + Viewer footer): left / center / right. */
.bar-inner .bar-group { align-items: baseline; display: flex; gap: 32px; }
.bar-inner .bar-center { flex: 1; gap: 48px; justify-content: center; overflow: hidden; }
.bar-inner .hover-title { font-weight: 500; white-space: nowrap; }
.bar-inner .hover-desc {
  opacity: 0.65;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Filter / Sort slide-down panels + Search (shared by Works, Index, Notes,
   Archive tools footers). Live here (not in home.css) so every gallery-style
   view that renders snippets/toolbar.php gets them. ─────────────────────────── */
.tool-panel {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin: 0 16px;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-height 0.25s ease, opacity 0.2s ease, padding 0.25s ease;
}

.tool-panel.open {
  max-height: 96px;
  opacity: 1;
  padding: 14px 0 0;
}

/* Category chips: plain words; the active one gets an underline in its own
   category colour (the mount colour, where defined). */
.cat-chip,
.sort-opt {
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  color: inherit;
  cursor: pointer;
  font: inherit;
  opacity: 0.55;
  padding: 0 0 2px;
}
.cat-chip { text-transform: lowercase; }

.cat-chip:hover,
.sort-opt:hover { opacity: 0.85; }

.cat-chip.active {
  border-bottom-color: var(--cat-color, currentColor);
  opacity: 1;
}
.sort-opt.active {
  border-bottom-color: currentColor;
  opacity: 1;
}

/* Active tool button (Filter / Sort with a panel open) */
.tool-btn[aria-expanded="true"] { opacity: 0.6; }

/* Search: underline-only input sliding out to the right */
.search-tool {
  align-items: baseline;
  display: inline-flex;
}

.search-input {
  background: none;
  border: 0;
  border-bottom: 1px solid transparent;
  caret-color: currentColor;
  color: inherit;
  font: inherit;
  margin-left: 0;
  opacity: 0;
  outline: none;
  padding: 0 2px 2px;
  transition: width 0.25s ease, opacity 0.2s ease, margin-left 0.25s ease;
  width: 0;
}

.search-tool.open .search-input {
  border-bottom-color: var(--rule-color);
  margin-left: 10px;
  opacity: 1;
  width: 200px;
}

/* main fills the space between the fixed chrome. Individual views decide
   whether to pad for the banner/bar (the home view positions its own). */
main {
  align-self: stretch;
  min-height: 100vh;
  min-height: 100dvh;
  width: 100%;
}

/* ── Simple stub / content pages (About, Archive) ───────────────────────────── */
.page-stub {
  padding: calc(var(--banner-height) + 24px) 16px calc(var(--footer-h) + 24px);
  width: 100%;
  max-width: 900px;
  margin: 0 auto;
}

.page-stub img { height: auto; max-width: 100%; }
.page-stub h2 { margin-bottom: 12px; }
.page-stub .stub-note { color: #888; }
.page-stub .profile-picture { margin-bottom: 16px; }
.page-stub .profile-picture img { border-radius: 6px; }
.page-stub .socials { display: flex; gap: 16px; margin-top: 16px; }
.page-stub .socials a { color: inherit; }

/* ── Page transitions (View Transitions API) ─────────────────────────────────── */
/* Chrome is REUSED, not redrawn: banner and footer bar are the SAME elements
   on every page (shared view-transition-names), so they glide in place. Two
   mechanisms (--vt-* tokens): cross-document (here) and same-document (overlay,
   project.css). @view-transition opt-in emitted in header.php when
   `walz.animations` is on. */

/* Opaque strip fixed at the top on every page — the symmetric counterpart to
   the footer cover. Slides moving upward disappear behind it so they exit
   cleanly at the header line rather than the viewport top. */
.banner-mask {
  background: var(--main-bg-color);
  height: var(--banner-height);
  left: 0;
  pointer-events: none;
  position: fixed;
  right: 0;
  top: 0;
  transition: background-color 0.2s ease;
  z-index: 800; /* below banner (900) / footer (900), above page content */
  view-transition-name: site-banner-cover;
}

.wzw-banner,
.top-banner          { view-transition-name: site-banner; }
.bottom-bar          { view-transition-name: site-footer-cover; }
.bottom-bar .bar-inner { view-transition-name: site-footer; }

/* Deep cross-document views (text post, standalone image) reuse the gallery
   footer as their top bar — the footer LINE morphs up and back, like the
   overlay (project.css). Cover chrome (banner, mask, footer BOX) is un-named so
   it slides WITH the page; only .bar-inner (site-footer) travels. Flag set in
   header.php. */
html[data-vt-deep] .wzw-banner,
html[data-vt-deep] .top-banner,
html[data-vt-deep] .banner-mask,
html[data-vt-deep] .bottom-bar { view-transition-name: none; }

@keyframes vtFadeOut { to   { opacity: 0; } }
@keyframes vtFadeIn  { from { opacity: 0; } }

/* Slide keyframes (shared with the single-image overlay in project.css). */
@keyframes vtSlideUpOut   { to   { transform: translateY(-100%); } }
@keyframes vtSlideUpIn    { from { transform: translateY(100%);  } }
@keyframes vtSlideDownOut { to   { transform: translateY(100%);  } }
@keyframes vtSlideDownIn  { from { transform: translateY(-100%); } }

/* The page content SLIDES — no fades. The three chrome groups (banner, footer
   bar, footer BOX) sit ABOVE the content, so the incoming page rises out of the
   footer LINE. DOWN (home → page): old content tucks under the header, new
   rises from the footer line. UP reverses. */
::view-transition-group(site-banner-cover),
::view-transition-group(site-footer),
::view-transition-group(site-footer-cover) { animation-duration: var(--vt-slow); animation-timing-function: var(--vt-ease); z-index: 1; }
/* Banner text sits above all covers — always the topmost chrome layer. */
::view-transition-group(site-banner) { animation-duration: var(--vt-slow); animation-timing-function: var(--vt-ease); z-index: 2; }
html[data-vt-dir="up"]::view-transition-group(site-banner) { animation-delay: var(--vt-cross); }

/* Banner cover is identical on every page (same position, same colour) — suppress
   the default crossfade so it stays fully opaque and never lets the sliding root
   show through. */
html[data-vt-dir]::view-transition-old(site-banner-cover),
html[data-vt-dir]::view-transition-new(site-banner-cover) { animation: none; }

html[data-vt-dir="down"]::view-transition-old(root) { animation: vtSlideUpOut   var(--vt-slow) var(--vt-ease) both; }
html[data-vt-dir="down"]::view-transition-new(root) { animation: vtSlideUpIn    var(--vt-slow) var(--vt-ease) both; }
html[data-vt-dir="up"]::view-transition-old(root)   { animation: vtSlideDownOut var(--vt-slow) var(--vt-ease) both; }
html[data-vt-dir="up"]::view-transition-new(root)   { animation: vtSlideDownIn  var(--vt-slow) var(--vt-ease) both; }

html[data-vt-dir]::view-transition-old(site-footer) { animation: vtFadeOut var(--vt-cross) var(--vt-ease) both; }
html[data-vt-dir]::view-transition-new(site-footer) { animation: vtFadeIn  var(--vt-cross) var(--vt-ease) both; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ── Master motion switch (config: walz.animations = false) ──────────────────── */
/* <html data-anim="off"> is emitted by snippets/header.php. It strips every CSS
   transition / animation site-wide (the @view-transition opt-in and the overlay's
   startViewTransition are skipped separately — see header.php and home.js).
   0.001ms rather than 0s so animationend / transitionend listeners still fire. */
html[data-anim="off"] *,
html[data-anim="off"] *::before,
html[data-anim="off"] *::after {
  animation-duration: 0.001ms !important;
  animation-delay: 0s !important;
  transition-duration: 0.001ms !important;
  transition-delay: 0s !important;
  scroll-behavior: auto !important;
}
html[data-anim="off"]::view-transition-group(*),
html[data-anim="off"]::view-transition-old(*),
html[data-anim="off"]::view-transition-new(*) { animation: none !important; }

/* ── Freeze exemptions ────────────────────────────────────────────────────────── */
/* Two motions survive the freeze (besides the JS-driven home marquee):

   1. The light toggle's cross-fade — tints, mount colours, image filter, label
      opacity, vignette. The light-off hover-backlight rides the same
      transitions, so a resting slide keeps its pop. Durations restated from the
      base rules (0.2s, 0.35s labels). */
html[data-anim="off"] body,
html[data-anim="off"] body::after,
html[data-anim="off"] .top-banner,
html[data-anim="off"] .banner-mask,
html[data-anim="off"] .bottom-bar,
html[data-anim="off"] .wzw-banner.is-top,
html[data-anim="off"] .slide-card,
html[data-anim="off"] .slide-card figure,
html[data-anim="off"] .slide-card img,
html[data-anim="off"] .archive-doc img {
  transition-duration: 0.2s !important;
}
html[data-anim="off"] .slide-card .slide-meta { transition-duration: 0.35s !important; }

/* 2. The slide-projector swap (Prev / Next) inside the single-image viewer.
      Only the image carriage keeps moving — the viewer open/close transition
      stays frozen with the rest of the page motion. Durations/delay restated
      from templates/project.css. */
html[data-anim="off"] #project-img.slide-enter {
  animation-duration: 0.5s !important;
  animation-delay: 0.12s !important;
}
html[data-anim="off"] #project-img.slide-exit { animation-duration: 0.32s !important; }

/* ── Mobile chrome (≤700px) ───────────────────────────────────────────────────── */
/* Site-wide mobile rules for the shared chrome. View-specific mobile layout
   lives in each template's own CSS (home.css / archive.css / post.css / …). */
@media (max-width: 700px) {

  /* ── Deep-view headers: full metadata, stacked ──
     Viewer / post / document headers list Back, then title / year / description
     each on its own line — nothing truncates. The extra .bar-inner level keeps
     these rules ahead of the template CSS (loaded after this file). */
  .viewer-top .bar-inner,
  .post-header .bar-inner,
  .document-header .bar-inner {
    align-items: flex-start;
    flex-direction: column;
    gap: 12px;
  }

  .viewer-top .bar-inner .bar-center,
  .post-header .bar-inner .bar-center,
  .document-header .bar-inner .bar-center {
    align-items: flex-start;
    flex-direction: column;
    gap: 6px;
    justify-content: flex-start;
    overflow: visible;
  }

  /* The metadata wraps naturally instead of truncating; empty spans (the JS
     overlay always renders all three) don't leave stray gaps. */
  .viewer-top .bar-inner .bar-center span,
  .post-header .bar-inner .bar-center span,
  .document-header .bar-inner .bar-center span {
    overflow: visible;
    text-overflow: unset;
    white-space: normal;
  }
  .bar-inner .bar-center span:empty { display: none; }

  /* ── Bottom bar: compact fixed band instead of 20vh ──
     Anchored to bottom:0 with auto height, the bar grows UPWARD when a tool
     panel opens — the panel expands over the content like a sheet instead of
     running off-screen. Views pad their bottom by the same token. */
  :root { --footer-h: calc(96px + env(safe-area-inset-bottom)); }

  .bottom-bar {
    height: auto;
    min-height: var(--footer-h);
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* Tighter rows so nav + tools fit a ~390px row. */
  .bar-inner { gap: 12px; }
  .bottom-bar .bar-nav { gap: 14px; }
  .bar-tools { gap: 18px; }

  /* The footer-centre hover metadata is hover-fed (Works grid) — meaningless on
     mobile; the corner labels on the mounts carry the titles instead. (The
     deep-view .bar-center lives in .viewer-top / .post-header / .document-header,
     not .bottom-bar, so the Info panel above is unaffected.) */
  .bottom-bar .bar-center { display: none; }

  /* Chips wrap onto more rows on a narrow screen — give the panel room. */
  .tool-panel { gap: 14px 22px; }
  .tool-panel.open { max-height: 200px; }

  /* 16px input font kills the iOS focus-zoom; the width yields to the row. */
  .search-tool.open .search-input {
    font-size: 16px;
    width: clamp(90px, 38vw, 200px);
  }
}

/* ── Touch ergonomics (any touch device, any width) ─────────────────────────── */
/* Bare-text controls get ~44px effective hit areas: symmetric padding + equal
   negative margin inflates the tappable box without moving a pixel of layout
   (text spacing is unchanged; the padded boxes expand into the flex gaps). */
@media (pointer: coarse) {
  .bar-link,
  .bottom-bar a,
  .light-toggle,
  .cat-chip,
  .sort-opt {
    margin: -12px -6px;
    padding: 12px 6px;
  }
}

/* ── Keyboard avoidance ───────────────────────────────────────────────────────── */
/* JS (index.js) translates the fixed footer up by the keyboard-occluded height
   (visualViewport) so the rule, tools row and open panel ride on top of the
   keyboard while the search input is focused. */