/* Metal Montaj — site stylesheet
   Tokens sourced from the Organic design system (see design_handoff_metal_montaj/styles.css),
   with Archivo swapped in as the display face and radii squared off per client request. */

/* Type: Archivo and Figtree were the original picks, but NEITHER ships a
   Cyrillic subset — so the Bulgarian site (the primary market) was silently
   falling back to system-ui on every character. Replaced with three faces that
   do carry Cyrillic and suit a fabrication shop:
     Vollkorn      — sturdy old-style serif, the heirloom-craft voice
     IBM Plex Sans — engineering-neutral text face
     IBM Plex Mono — specs, labels and stats, in a workshop cut-list voice */
@import url('https://fonts.googleapis.com/css2?family=Vollkorn:wght@600;700;800&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

:root {
  /* Page background is white. --color-surface (form fields, cards) was the
     original design's cream, #ebddc5 — correct against the old cream page,
     but a hard jump against white. Lightened so boxes still read as a
     distinct warm surface without looking like a block of tan.
     --color-nav-tint is the glass nav's own cream, independent of --color-bg
     — the nav needs to read as glass over a page, not disappear into it. */
  --color-bg: #ffffff;
  --color-surface: #f2e9d9;
  --color-nav-tint: #f5ead8;
  --color-band: #faf6f0;   /* the one tint used on the home page bands */
  --color-text: #201e1d;
  --color-accent: #c67139;
  --color-accent-2: #7a8a5e;
  --color-divider: color-mix(in srgb, #201e1d 16%, transparent);

  --color-neutral-100: #f9f4ed;
  --color-neutral-200: #eee7db;
  --color-neutral-300: #dcd3c4;
  --color-neutral-400: #c0b6a5;
  --color-neutral-500: #a19786;
  --color-neutral-600: #82796a;
  --color-neutral-700: #645c50;
  --color-neutral-800: #474238;
  --color-neutral-900: #2e2b25;

  --color-accent-100: #fff2eb;
  --color-accent-200: #ffe1d0;
  --color-accent-300: #ffc6a5;
  --color-accent-400: #f6a06b;
  --color-accent-500: #d67f48;
  --color-accent-600: #b2622d;
  --color-accent-700: #8c491a;
  --color-accent-800: #643312;
  --color-accent-900: #402310;

  --color-accent-2-100: #f0fae1;
  --color-accent-2-800: #3d472b;

  --font-display: 'Vollkorn', Georgia, 'Times New Roman', serif;
  --font-body: 'IBM Plex Sans', system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;

  --shadow-sm: 0 1px 2px rgba(46, 43, 37, .14);
  --shadow-md: 0 3px 10px rgba(46, 43, 37, .16);
  --shadow-lg: 0 12px 32px rgba(46, 43, 37, .22);

  /* floating nav geometry — content clears the bar by --nav-clearance */
  --nav-top: 16px;
  --nav-clearance: calc(64px + var(--nav-top) * 2);

  /* page gutter, fluid: the hero and every section share it so content
     reaches the same distance from the viewport edge at any width */
  --gutter: clamp(20px, 4vw, 72px);
  --content-max: 1760px;

  /* squared-off radii — this site does not use the system's pill defaults */
  --radius-btn: 8px;
  --radius-photo: 14px;
  --radius-strike: 4px;
  --radius-input: 8px;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 16.5px;
  line-height: 1.6;
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
}

p { margin: 0; }
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }

:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

/* — scrollbar: thin and on-palette instead of the OS default — */
html { scrollbar-width: thin; scrollbar-color: var(--color-neutral-400) transparent; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background-color: var(--color-neutral-400);
  border-radius: 999px;
  border: 3px solid var(--color-bg);
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background-color: var(--color-neutral-500); }
::-webkit-scrollbar-corner { background: transparent; }

.washed { filter: saturate(0.6) contrast(0.85) brightness(1.1) opacity(0.94); }

.text-muted-55 { color: color-mix(in srgb, var(--color-text) 55%, transparent); }
.text-muted-62 { color: color-mix(in srgb, var(--color-text) 62%, transparent); }
.text-muted-78 { color: color-mix(in srgb, var(--color-text) 78%, transparent); }

.wrap { max-width: var(--content-max); margin: 0 auto; padding: 0 var(--gutter); }

/* ── buttons ─────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-body); font-weight: 600; font-size: 14.5px;
  padding: 13px 24px; border-radius: var(--radius-btn);
  white-space: nowrap; cursor: pointer; transition: background-color .15s ease, border-color .15s ease;
}
.btn-primary { background: var(--color-accent); color: var(--color-bg); border: 1px solid transparent; }
.btn-primary:hover { background: var(--color-accent-600); }
.btn-primary:active { background: var(--color-accent-700); }
.btn-primary:disabled { opacity: .55; cursor: not-allowed; background: var(--color-accent); }
.btn-secondary {
  background: transparent; color: var(--color-text);
  border: 1px solid color-mix(in srgb, var(--color-text) 22%, transparent);
}
.btn-secondary:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.btn-secondary:active { background: color-mix(in srgb, var(--color-text) 14%, transparent); }

/* ── floating glass nav ──────────────────────────────────────────────
   Fixed to the viewport: it stays put while the page scrolls. The glass is
   a blurred, saturated sample of whatever passes beneath, with a bright
   top edge and a soft inner highlight standing in for a lens edge.        */

.glass-nav {
  position: fixed;
  top: var(--nav-top); left: 50%;
  transform: translateX(-50%);
  width: min(var(--content-max), calc(100% - var(--gutter) * 2));
  z-index: 40;
  border-radius: 18px;
  background: color-mix(in srgb, var(--color-nav-tint) 58%, transparent);
  backdrop-filter: blur(18px) saturate(180%);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  border: 1px solid color-mix(in srgb, #fff 55%, transparent);
  box-shadow:
    0 1px 0 color-mix(in srgb, #fff 60%, transparent) inset,
    0 -1px 0 color-mix(in srgb, var(--color-neutral-900) 6%, transparent) inset,
    0 8px 28px color-mix(in srgb, var(--color-neutral-900) 12%, transparent);
  transition: background-color .3s ease, box-shadow .3s ease, border-color .3s ease;
}
/* once past the hero the bar sits over ordinary content — firm it up a little */
.glass-nav.is-stuck {
  background: color-mix(in srgb, var(--color-nav-tint) 72%, transparent);
  box-shadow:
    0 1px 0 color-mix(in srgb, #fff 60%, transparent) inset,
    0 10px 34px color-mix(in srgb, var(--color-neutral-900) 18%, transparent);
}
.glass-nav-inner {
  display: flex; align-items: center; gap: clamp(18px, 2.4vw, 34px);
  height: 64px;
  padding: 0 clamp(16px, 1.6vw, 24px);
}
.nav-links { display: flex; align-items: center; gap: clamp(18px, 2.4vw, 34px); }

.brand { display: flex; align-items: center; gap: 10px; margin-right: auto; }
.brand-mark { width: 40px; height: 40px; flex: none; display: block; }
.brand-word {
  font-family: var(--font-display); font-weight: 700;
  font-size: 17px; letter-spacing: .07em;
}

.nav-link { font-size: 14px; font-weight: 400; transition: color .15s ease; }
.nav-link:hover { color: var(--color-accent); }
.nav-link.is-current { font-weight: 600; }

.nav-cta.btn { font-size: 13px; padding: 10px 18px; }

/* — language switch — */
.lang-switch {
  display: flex; align-items: center; gap: 6px;
  font-family: var(--font-display); font-size: 12px; font-weight: 600;
  letter-spacing: .03em;
}
.lang-switch a {
  color: color-mix(in srgb, var(--color-text) 45%, transparent);
  transition: color .15s ease;
}
.lang-switch a:hover { color: var(--color-accent); }
.lang-switch .lang-current { color: var(--color-text); }
.lang-switch .lang-sep { color: color-mix(in srgb, var(--color-text) 25%, transparent); }

.nav-toggle {
  display: none;
  width: 36px; height: 36px; margin-left: auto;
  align-items: center; justify-content: center;
}
.nav-toggle span, .nav-toggle::before, .nav-toggle::after {
  content: ''; display: block; width: 20px; height: 2px; background: var(--color-text);
  position: relative; transition: transform .2s ease, opacity .2s ease;
}
.nav-toggle::before { position: absolute; transform: translateY(-6px); }
.nav-toggle::after { position: absolute; transform: translateY(6px); }
.nav-toggle.is-open span { opacity: 0; }
.nav-toggle.is-open::before { transform: rotate(45deg); }
.nav-toggle.is-open::after { transform: rotate(-45deg); }

/* ── page footer ─────────────────────────────────────────────────────
   The markup is one flat run of spans (shared with /en/, which must not be
   edited), so the two rows are composed here rather than in the HTML: `order`
   groups the children, and a full-width pseudo-element forces the line break
   while doubling as the rule between the rows.

     row 1   УСЛУГИ   Порти и огради · Парапети · …
     ────────────────────────────────────────────────────────────────
     row 2   © Метал Монтаж ЕООД            посещения по уговорка   EN / BG   */

.site-footer {
  border-top: 1px solid var(--color-divider);
  padding: 34px 0 30px;
  margin-top: 96px;
}
.site-footer .wrap {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: 24px; row-gap: 9px;
}
.site-footer .wrap::before {
  content: ''; order: 2;
  flex: 0 0 100%; height: 1px; margin: 12px 0 4px;
  background: var(--color-divider);
}
.footer-label {
  order: 1;
  font-family: var(--font-mono);
  font-size: 10px; font-weight: 500; letter-spacing: .14em; text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 42%, transparent);
}
.footer-item {
  order: 1; font-size: 13px;
  color: color-mix(in srgb, var(--color-text) 66%, transparent);
}
.footer-copy {
  order: 3; width: auto; margin-top: 0;
  font-family: var(--font-mono); font-size: 11px;
  color: color-mix(in srgb, var(--color-text) 42%, transparent);
}
.footer-note {
  order: 4; margin-left: auto;
  font-family: var(--font-mono); font-size: 11px;
  color: color-mix(in srgb, var(--color-text) 42%, transparent);
}
.lang-switch { order: 5; }

/* quiet, text-only — matches footer-note's own weight rather than adding an
   icon or a louder treatment; sits right after the language switch since
   both are small utility links, not page content */
.footer-social { order: 6; }
.footer-social a {
  font-family: var(--font-mono); font-size: 11px;
  color: color-mix(in srgb, var(--color-text) 42%, transparent);
  transition: color .15s ease;
}
.footer-social a:hover { color: var(--color-accent); }

/* ══════════════════════════════════════════════════════════════════════
   HOME — hero (approved direction 2a)
   ══════════════════════════════════════════════════════════════════════ */

/* The hero fills the viewport at any width. The design's 1120×640 stage is
   kept only as a set of ratios: the copy column and the photo panel are a
   two-up grid, and the hammer/bar/burst group is anchored to the photo's
   left edge so the strike stays registered no matter how wide the page is. */

.hero-region {
  position: relative;
  background: var(--color-bg);
  overflow: hidden;
  display: flex; flex-direction: column;
}

.hero-body {
  flex: 1;
  width: 100%; max-width: var(--content-max);
  margin: 0 auto;
  padding: calc(var(--nav-clearance) + clamp(16px, 3vh, 48px)) var(--gutter) clamp(20px, 3.5vh, 44px);
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(28px, 4vw, 72px);
}

.hero-copy {
  display: flex; flex-direction: column; gap: clamp(14px, 1.8vh, 18px);
  z-index: 4;
}

.hero-eyebrow {
  font-family: var(--font-body); font-size: 11.5px; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  color: var(--color-accent-700);
}

.hero-headline {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(46px, 6.1vw, 104px);
  line-height: .94; letter-spacing: -.035em;
}
.hero-headline .line { display: block; }

.hero-para {
  margin: 0; max-width: 46ch;
  font-size: clamp(15.5px, 1.05vw, 18px); line-height: 1.6;
  color: color-mix(in srgb, var(--color-text) 78%, transparent);
}

.hero-actions { display: flex; flex-wrap: wrap; gap: 10px; }

.hero-stats { display: flex; flex-wrap: wrap; gap: clamp(24px, 3vw, 48px); margin-top: 6px; }
.hero-stat-num {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(26px, 2.2vw, 36px);
  line-height: 1; letter-spacing: -.02em;
}
.hero-stat-cap {
  margin-top: 5px; font-size: 12px;
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}

/* — photo panel + strike group — */

.hero-media { position: relative; }

.hero-photo {
  position: relative; z-index: 2;
  width: 100%;
  aspect-ratio: 380 / 372;
  max-height: min(64vh, 620px);
  margin-left: auto;
  border-radius: var(--radius-photo); overflow: hidden;
  box-shadow: var(--shadow-md);
}
.hero-photo img { width: 100%; height: 100%; object-fit: cover; }
.hero-photo .photo-placeholder { width: 100%; height: 100%; }

/* zero-size anchor sitting on the impact point: 68px left of the photo's
   edge and 64.5% down it, exactly as in the 1120×640 original */
.strike-group {
  position: absolute;
  left: -68px; top: 64.5%;
  width: 0; height: 0;
  z-index: 3; /* without this the group has no stacking context of its own, so its
                 children's z-index (hammer=3, burst=4) only rank against each other
                 and the whole cluster paints as if z-index:0 — behind the photo (z:2) */
  transform: scale(var(--strike-scale, 1));
  transform-origin: 0 0;
  cursor: pointer;
}

.hero-strike-bar {
  position: absolute; left: -127px; top: 0;
  width: 215px; height: 7px;
  border-radius: var(--radius-strike);
  background: var(--color-neutral-800);
  z-index: 1;
}

.hero-burst { position: absolute; left: 0; top: 0; width: 0; height: 0; z-index: 4; }
.hero-ring {
  position: absolute; width: 160px; height: 160px;
  border: 1.5px solid var(--color-accent-400);
  border-radius: 999px;
  opacity: 0;
}
.hero-spark { position: absolute; border-radius: 999px; opacity: 0; }

/* head bottom (viewBox y=466, i.e. 866px down the element) lands on the bar
   top; head centre (x=100) on the anchor. Pivot is viewBox y=10 → element 410. */
.hero-hammer {
  position: absolute; left: -100px; top: -866px;
  transform-origin: 100px 410px;
  z-index: 3;
  filter: drop-shadow(0 8px 16px color-mix(in srgb, var(--color-neutral-900) 45%, transparent));
}

.foot-strip {
  position: relative; z-index: 5;
  min-height: 56px;
  background: var(--color-bg);
  border-top: 1px solid var(--color-divider);
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 8px clamp(20px, 2.6vw, 38px);
  padding: 10px var(--gutter);
}
.foot-strip .footer-note { margin-left: auto; }

/* — hero entrance animation — */
@keyframes hamFallHeavy {
  0%   { transform: translateY(-620px) rotate(-22deg); }
  52%  { transform: translateY(0) rotate(0); }
  62%  { transform: translateY(-30px) rotate(-4deg); }
  76%  { transform: translateY(-2px) rotate(0); }
  88%  { transform: translateY(-6px) rotate(0); }
  100% { transform: translateY(0) rotate(0); }
}
@keyframes jolt {
  0%, 100% { transform: translateY(0); }
  16%      { transform: translateY(5px); }
  42%      { transform: translateY(-2px); }
  70%      { transform: translateY(1px); }
}
@keyframes ringOut {
  0%   { opacity: .5; transform: translate(-50%, -50%) scale(.12); }
  100% { opacity: 0;  transform: translate(-50%, -50%) scale(1); }
}
@keyframes sparkFly {
  0%   { opacity: 0; transform: translate(0, 0) scale(1.2); }
  10%  { opacity: 1; }
  100% { opacity: 0; transform: translate(var(--sx), var(--sy)) scale(.25); }
}
@media (prefers-reduced-motion: no-preference) {
  /* text renders in place, no entrance animation — only the strike itself moves */
  .hero-run .hero-hammer { animation: hamFallHeavy 1.05s cubic-bezier(.48,.04,.62,1) .02s both; }
  .hero-run .hero-strike-bar { animation: jolt .45s ease-out .48s both; }
  .hero-run .hero-ring { animation: ringOut .75s ease-out .48s both; }
  .hero-run .hero-spark { animation: sparkFly ease-out both; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-hammer { transform: translateY(0) rotate(0); }
  .hero-burst { display: none; }
  /* `.js` included deliberately: the rule that hides these tiles is scoped to
     it, and without matching that specificity this one never won and the
     tiles stayed hidden until the reveal ran anyway. */
  .js .gallery-item { --rv-o: 1; --rv-y: 0px; }
}

/* ── home: about section ─────────────────────────────────────────────── */

.about-section { padding: 64px 0 24px; scroll-margin-top: var(--nav-clearance); }
.about-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: center;
}
.about-copy { display: flex; flex-direction: column; gap: 18px; }
.about-title { font-size: clamp(28px, 3vw, 46px); letter-spacing: -.03em; max-width: 15ch; }
.about-photo {
  aspect-ratio: 4 / 5; max-height: 620px;
  border-radius: var(--radius-photo);
  overflow: hidden; box-shadow: var(--shadow-md);
}
.about-photo img { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 900px) {
  .about-grid { grid-template-columns: 1fr; gap: 32px; }
  .about-section { padding-top: 56px; }
  .about-photo { aspect-ratio: 4 / 3; }
}

/* ── generic photo placeholder (used until real photography arrives) ─── */

.photo-placeholder {
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  background:
    repeating-linear-gradient(135deg,
      color-mix(in srgb, var(--color-neutral-800) 6%, var(--color-surface)) 0 2px,
      var(--color-surface) 2px 14px);
  color: color-mix(in srgb, var(--color-text) 45%, transparent);
  font-size: 12.5px; line-height: 1.5; padding: 16px;
}

/* ══════════════════════════════════════════════════════════════════════
   Shared: page header for interior pages
   ══════════════════════════════════════════════════════════════════════ */

.page-head { padding: calc(var(--nav-clearance) + 40px) 0 40px; }
.page-eyebrow {
  font-size: 11.5px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  color: var(--color-accent-700); margin-bottom: 12px; display: block;
}
.page-title { font-size: clamp(34px, 4.4vw, 64px); letter-spacing: -.03em; }
.page-lede {
  margin-top: 14px; max-width: 620px; font-size: 16.5px; line-height: 1.6;
  color: color-mix(in srgb, var(--color-text) 78%, transparent);
}

/* ══════════════════════════════════════════════════════════════════════
   GALLERY
   ══════════════════════════════════════════════════════════════════════ */

.filter-row {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-bottom: 32px;
}
.filter-chip {
  font-family: var(--font-body); font-size: 13px; font-weight: 600;
  padding: 8px 16px; border-radius: var(--radius-btn);
  border: 1px solid color-mix(in srgb, var(--color-text) 22%, transparent);
  color: color-mix(in srgb, var(--color-text) 62%, transparent);
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.filter-chip:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.filter-chip.is-active {
  background: var(--color-accent); border-color: var(--color-accent); color: var(--color-bg);
}

.masonry {
  column-count: 3; column-gap: 24px;
  padding-bottom: 96px;
}
@media (min-width: 1500px) { .masonry { column-count: 4; } }
.gallery-item {
  break-inside: avoid; margin-bottom: 24px;
  position: relative; border-radius: var(--radius-photo); overflow: hidden;
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition: box-shadow .2s ease, transform .2s ease;
}
/* The pieces are in the HTML now, so they must be visible without JavaScript —
   a crawler that does not run scripts would otherwise index a page of
   opacity:0 content. The reveal animation's starting state is therefore only
   applied once JS has confirmed it can finish it. The hidden state and the
   entrance itself are both owned by the motion section further down (search
   "gallery grid") — this file used to also carry a `riseIn` keyframe doing
   the same job here, with `animation-fill-mode: both` pinning `transform`
   permanently once it finished, which silently killed the hover lift below
   for the life of the element. Removed rather than reconciled. */
.gallery-item:hover, .gallery-item:focus-visible { box-shadow: var(--shadow-md); }
.gallery-item .photo-placeholder { width: 100%; }

.gallery-caption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 16px 14px 12px;
  background: linear-gradient(to top, rgba(32,30,29,.72), rgba(32,30,29,0));
  color: #f5ead8;
  transform: translateY(100%);
  transition: transform .2s ease;
}
.gallery-item:hover .gallery-caption,
.gallery-item:focus-visible .gallery-caption { transform: translateY(0); }
.gallery-caption .piece-name { display: block; font-family: var(--font-display); font-weight: 600; font-size: 14px; }
.gallery-caption .piece-meta { display: block; font-size: 11.5px; opacity: .82; margin-top: 2px; }

.gallery-item[hidden] { display: none; }

/* — lightbox — */
.lightbox {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  padding: 40px;
  background: color-mix(in srgb, var(--color-neutral-900) 82%, transparent);
}
.lightbox[hidden] { display: none; }
.lightbox-panel {
  width: min(920px, 100%); max-height: 100%;
  background: var(--color-bg); border-radius: var(--radius-photo);
  overflow: hidden; display: grid; grid-template-columns: 1.3fr 1fr;
  box-shadow: var(--shadow-lg);
}
/* the wrapper (not .lightbox-media itself) is the grid column now, so a
   multi-photo piece's thumbnail strip has something to anchor to at the
   photo's own bottom edge regardless of which piece is showing */
.lightbox-media-wrap { position: relative; min-height: 320px; }
.lightbox-media { height: 100%; }
.lightbox-media .photo-placeholder { height: 100%; }

/* — thumbnail strip: only a piece with more than one photo has one — */
.lb-thumbs {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; gap: 7px; padding: 12px;
  background: linear-gradient(to top, rgba(24,20,18,.65), transparent);
  overflow-x: auto;
}
.lb-thumbs[hidden] { display: none; }
.lb-thumb {
  flex: none; width: 46px; height: 46px; border-radius: 6px; overflow: hidden;
  display: block; padding: 0; border: 2px solid transparent;
  opacity: .62; transition: opacity .15s ease, border-color .15s ease;
}
.lb-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lb-thumb:hover { opacity: .88; }
.lb-thumb.is-active { opacity: 1; border-color: #fff; box-shadow: 0 0 0 1px var(--color-accent); }
.lightbox-body { padding: 32px; display: flex; flex-direction: column; gap: 14px; overflow-y: auto; }
.lightbox-title { font-size: 26px; letter-spacing: -.02em; }
.lightbox-note { font-size: 14.5px; line-height: 1.6; }
.lightbox-facts { margin: 0; font-size: 13.5px; }
.lightbox-facts .fact-row { display: grid; grid-template-columns: 92px 1fr; gap: 6px 14px; }
.lightbox-facts .fact-row[hidden] { display: none; }
.lightbox-facts dt { color: color-mix(in srgb, var(--color-text) 55%, transparent); }
.lightbox-facts dd { margin: 0; }
.lightbox-media img { width: 100%; height: 100%; object-fit: cover; }
.gallery-item img { width: 100%; height: auto; display: block; object-fit: cover; }
.lightbox-close {
  position: absolute; top: 20px; right: 20px;
  width: 40px; height: 40px; border-radius: var(--radius-btn);
  background: var(--color-bg); box-shadow: var(--shadow-sm);
  display: flex; align-items: center; justify-content: center; font-size: 18px;
}
.lightbox-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: var(--radius-btn);
  background: var(--color-bg); box-shadow: var(--shadow-sm);
  display: flex; align-items: center; justify-content: center; font-size: 20px;
}
.lightbox-prev { left: 20px; }
.lightbox-next { right: 20px; }

/* ══════════════════════════════════════════════════════════════════════
   CONTACT
   ══════════════════════════════════════════════════════════════════════ */

.contact-grid {
  display: grid; grid-template-columns: 1.1fr .9fr; gap: 64px;
  padding-bottom: 96px;
}

.field { display: flex; flex-direction: column; gap: 6px; }
.field-label { font-size: 12px; color: color-mix(in srgb, var(--color-text) 70%, transparent); }
.field-required { color: var(--color-accent-700); }
.field-optional { color: color-mix(in srgb, var(--color-text) 42%, transparent); }

/* Honeypot. Positioned off-canvas rather than display:none — better bots skip
   fields that are display:none/hidden, but will still fill this one. */
.hp-field {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}
.field-row { display: flex; gap: 16px; }
.field-row .field { flex: 1; min-width: 0; }

.input, textarea.input, select.input {
  width: 100%; min-height: 44px; padding: 10px 14px; font: inherit; font-size: 14.5px;
  color: var(--color-text); background: var(--color-surface);
  border: 1px solid var(--color-divider); border-radius: var(--radius-input);
  transition: border-color .15s ease;
}
textarea.input { min-height: 110px; resize: vertical; }
select.input { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--color-text) 50%), linear-gradient(135deg, var(--color-text) 50%, transparent 50%); background-position: calc(100% - 20px) center, calc(100% - 15px) center; background-size: 5px 5px; background-repeat: no-repeat; }
.input:hover { border-color: color-mix(in srgb, var(--color-text) 45%, transparent); }
.input:focus-visible { border-color: var(--color-accent); }
.input.is-invalid { border-color: var(--color-accent-700); }

.field-error {
  font-size: 12px; color: var(--color-accent-700); display: none;
}
.field.is-invalid .field-error { display: block; }

.file-drop {
  border: 1px dashed color-mix(in srgb, var(--color-text) 30%, transparent);
  border-radius: var(--radius-input); padding: 18px; text-align: center;
  font-size: 13px; color: color-mix(in srgb, var(--color-text) 55%, transparent);
  cursor: pointer; background: var(--color-surface);
}
.file-drop:hover { border-color: var(--color-accent); }
.file-thumbs { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.file-thumb {
  width: 56px; height: 56px; border-radius: 6px; overflow: hidden;
  background: var(--color-neutral-200); position: relative;
  font-size: 9px; display: flex; align-items: center; justify-content: center;
  color: color-mix(in srgb, var(--color-text) 55%, transparent); text-align: center; padding: 4px;
}
.file-thumb button {
  position: absolute; top: 2px; right: 2px; width: 16px; height: 16px;
  background: rgba(32,30,29,.6); color: #f5ead8; border-radius: 4px; font-size: 11px; line-height: 1;
}

.gallery-pick-btn {
  align-self: flex-start; margin-top: 8px;
  font-size: 12.5px; font-weight: 600;
  color: var(--color-accent-700); text-decoration: underline; text-underline-offset: 2px;
}
.gallery-pick-btn:hover { color: var(--color-accent-800); }

/* — reference-photo picker: choose an existing gallery piece instead of uploading — */
.gallery-picker {
  position: fixed; inset: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center;
  padding: 40px;
  background: color-mix(in srgb, var(--color-neutral-900) 82%, transparent);
}
.gallery-picker[hidden] { display: none; }
.gallery-picker-panel {
  width: min(920px, 100%); max-height: 100%;
  background: var(--color-bg); border-radius: var(--radius-photo);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; overflow: hidden;
}
.gallery-picker-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 22px; border-bottom: 1px solid var(--color-divider);
}
.gallery-picker-title { font-size: 17px; letter-spacing: -.01em; }
.gallery-picker-close {
  width: 32px; height: 32px; border-radius: var(--radius-btn); flex: none;
  display: flex; align-items: center; justify-content: center; font-size: 15px;
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}
.gallery-picker-close:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.gallery-picker-grid {
  display: flex; flex-direction: column; gap: 26px;
  padding: 22px; overflow-y: auto;
}
.gallery-pick-section-title {
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--color-accent-700);
  margin-bottom: 12px; padding-bottom: 8px;
  border-bottom: 1px solid var(--color-divider);
}
.gallery-pick-section-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
}
.gallery-pick-item {
  position: relative; aspect-ratio: 1; border-radius: 10px; overflow: hidden;
  border: 2px solid transparent; cursor: pointer;
}
.gallery-pick-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery-pick-item:hover, .gallery-pick-item:focus-visible { border-color: var(--color-accent); }
.gallery-pick-item:hover img, .gallery-pick-item:focus-visible img { transform: scale(1.04); }
.gallery-pick-item img { transition: transform .25s ease; }
.gallery-pick-item span {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 20px 10px 8px;
  background: linear-gradient(to top, rgba(32,30,29,.85), rgba(32,30,29,0));
  color: #f5ead8; font-size: 12px; font-weight: 500; line-height: 1.3;
}
@media (max-width: 900px) {
  .gallery-pick-section-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 640px) {
  .gallery-picker { padding: 16px; }
  .gallery-picker-grid { padding: 18px; gap: 22px; }
}

.contact-form { display: flex; flex-direction: column; gap: 18px; }
.form-submit-row { display: flex; align-items: center; gap: 14px; margin-top: 6px; }
.form-status { font-size: 13px; color: var(--color-accent-700); }

.form-success {
  padding: 40px; background: var(--color-surface); border-radius: var(--radius-photo);
  display: flex; flex-direction: column; gap: 10px;
}
.form-success h3 { font-size: 22px; }

/* - privacy notice -
   GDPR wants the notice where the data is actually collected, but a block of
   legal text under the button would swamp the form. So it sits as one quiet
   line and opens in place: no banner, no modal, no second page to maintain.
   Native <details>, so it costs no JS and touches none of the site's own. */
.privacy-note {
  margin-top: 20px; padding-top: 16px;
  border-top: 1px solid var(--color-divider);
  font-size: 12.5px; line-height: 1.65;
  color: color-mix(in srgb, var(--color-text) 62%, transparent);
}
.privacy-note > summary {
  display: flex; align-items: baseline; gap: 12px;
  cursor: pointer; list-style: none;
}
/* the default disclosure triangle would be the loudest thing in the block */
.privacy-note > summary::-webkit-details-marker { display: none; }
.privacy-note > summary::marker { content: ""; }
.privacy-note > summary:focus-visible {
  outline: 2px solid var(--color-accent); outline-offset: 4px; border-radius: 2px;
}

.privacy-more {
  flex: none; margin-left: auto;
  font-family: var(--font-mono); font-size: 11px;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--color-accent-700);
  text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px;
}
/* the +/- carries the open state, so the word itself needs no translating */
.privacy-more::after { content: " +"; text-decoration: none; }
.privacy-note[open] .privacy-more::after { content: " \2212"; }
.privacy-note > summary:hover .privacy-more { color: var(--color-accent-800); }

/* label/value rows, echoing the info card in the column alongside */
.privacy-body { margin: 16px 0 0; display: flex; flex-direction: column; gap: 11px; }
.privacy-body > div { display: flex; gap: 14px; }
.privacy-body dt {
  width: 124px; flex: none;
  font-family: var(--font-mono); font-size: 10.5px; line-height: 1.95;
  letter-spacing: .04em; text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 42%, transparent);
}
.privacy-body dd { margin: 0; min-width: 0; }
.privacy-note a {
  color: inherit; text-decoration: underline;
  text-underline-offset: 2px; text-decoration-thickness: 1px; word-break: break-word;
}
.privacy-note a:hover { color: var(--color-accent-700); }

@media (max-width: 560px) {
  .privacy-body > div { flex-direction: column; gap: 1px; }
  .privacy-body dt { width: auto; line-height: 1.6; }
}

.contact-side { display: flex; flex-direction: column; gap: 28px; }
.info-card {
  background: var(--color-surface); border-radius: var(--radius-photo);
  padding: 24px; display: flex; flex-direction: column; gap: 14px;
}
.info-row { display: flex; gap: 12px; font-size: 14px; }
.info-row .info-label { width: 92px; flex: none; color: color-mix(in srgb, var(--color-text) 55%, transparent); }
.info-link { color: var(--color-text); text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; word-break: break-word; }
.info-link:hover { color: var(--color-accent-700); }
.map-placeholder {
  height: 220px; border-radius: var(--radius-photo); overflow: hidden;
}

/* ── responsive ──────────────────────────────────────────────────────── */

/* The hammer group keeps the design's exact internal geometry; it is scaled
   as a whole about the impact point so it stays in proportion to the page. */
@media (min-width: 1800px) { .strike-group { --strike-scale: 1.18; } }
@media (max-width: 1279px) { .strike-group { --strike-scale: .86; } }
@media (max-width: 1023px) { .strike-group { --strike-scale: .72; } }

/* Two columns are held down to 900px so the hammer keeps the side-by-side
   geometry it needs — at ~140px wide it matches the handoff's tablet note.
   Once the hero stacks there is nowhere for it to fall, so it steps aside
   and the forge-in headline carries the moment. */
@media (max-width: 900px) {
  .hero-body { grid-template-columns: 1fr; align-content: center; }
  .hero-media { order: -1; max-width: 560px; width: 100%; }
  .hero-photo { aspect-ratio: 16 / 10; max-height: 34vh; }
  .hero-region { min-height: 0; }
  .strike-group { display: none; }

  .contact-grid { grid-template-columns: 1fr; gap: 48px; }
  .masonry { column-count: 2; }
}

@media (max-width: 767px) {
  .hero-media { max-width: none; }
  .hero-photo { aspect-ratio: 3 / 2; max-height: 30vh; }
  .foot-strip .footer-note { margin-left: 0; width: 100%; }

  .nav-links {
    display: none;
    position: absolute; left: 0; right: 0; top: calc(100% + 8px);
    flex-direction: column; align-items: stretch; gap: 0;
    padding: 8px 20px 18px;
    border-radius: 18px;
    background: color-mix(in srgb, var(--color-nav-tint) 86%, transparent);
    backdrop-filter: blur(18px) saturate(180%);
    -webkit-backdrop-filter: blur(18px) saturate(180%);
    border: 1px solid color-mix(in srgb, #fff 55%, transparent);
    box-shadow: 0 10px 34px color-mix(in srgb, var(--color-neutral-900) 18%, transparent);
  }
  .nav-links.is-open { display: flex; }
  .nav-links .nav-link { padding: 13px 0; border-bottom: 1px solid var(--color-divider); }
  .nav-links .nav-cta { margin-top: 14px; align-self: flex-start; }
  .nav-toggle { display: flex; }
  .glass-nav-inner { position: relative; }

  .page-title { font-size: clamp(30px, 8vw, 40px); }
  .masonry { column-count: 1; }
  .lightbox { padding: 16px; }
  .lightbox-panel { grid-template-columns: 1fr; overflow-y: auto; }
  .field-row { flex-direction: column; gap: 18px; }
}

/* ══════════════════════════════════════════════════════════════════════
   PHONES — the site is browsed on a phone far more than on a desktop, so
   these rules are corrections to real handset behaviour, not just narrower
   versions of the desktop layout.
   Reference widths: 360 (Galaxy A/S24), 375 (iPhone SE), 390 (iPhone 15/16),
   412 (S24 Ultra / Pixel), 430 (iPhone Pro Max).
   ══════════════════════════════════════════════════════════════════════ */

@media (max-width: 767px) {
  /* iOS Safari zooms the whole page in when a focused form control is under
     16px, and never zooms back out. This is THE mobile form bug — every
     control has to be at least 16px. */
  .input, textarea.input, select.input { font-size: 16px; }
  .file-drop { font-size: 14px; }

  /* The glass nav's low opacity was tuned for a wide desktop hero photo behind
     it. On a phone it sits over much busier, more varied content (photos,
     dense text) at close reading distance, and the brand word/links lose
     enough contrast to be hard to read. Solid enough to read reliably; still
     blurred, so it doesn't look like a plain flat bar. */
  .glass-nav { background: color-mix(in srgb, var(--color-nav-tint) 92%, transparent); }
  .glass-nav.is-stuck { background: color-mix(in srgb, var(--color-nav-tint) 97%, transparent); }

  /* Touch targets. Apple asks for 44×44, Material for 48×48; the hamburger is
     the primary navigation control on a phone and was 36×36. */
  .nav-toggle { width: 44px; height: 44px; margin-right: -6px; }
  .btn { padding: 14px 22px; min-height: 48px; font-size: 15px; }
  .filter-chip { padding: 11px 18px; min-height: 44px; }
  .gallery-pick-btn { min-height: 44px; display: inline-flex; align-items: center; }
  .lang-switch a, .lang-switch .lang-current {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px; min-width: 36px; padding: 0 8px;
  }
  .brand { min-height: 44px; }
  .lightbox-close, .lightbox-nav { width: 44px; height: 44px; }
  .file-thumb button { width: 22px; height: 22px; font-size: 13px; }

  /* Full-width CTAs read as buttons on a phone; side by side they wrap into
     two ragged rows at 360px. */
  .hero-actions { flex-direction: column; align-items: stretch; }
  .hero-actions .btn { width: 100%; }

  /* Tighten the hero so the headline, copy and buttons land on first screen. */
  .hero-body { padding-top: calc(var(--nav-clearance) + 8px); gap: 20px; }
  .hero-copy { gap: 12px; }
  .hero-stats { gap: 18px 26px; }
  .hero-photo { aspect-ratio: 4 / 3; max-height: 26vh; }

  .about-section { padding: 56px 0 8px; }
  .about-grid { gap: 28px; }

  /* Stop the info rows from splitting awkwardly at narrow widths. */
  .info-row { flex-direction: column; gap: 2px; }
  .info-row .info-label { width: auto; font-size: 12px; }
  .info-card { padding: 20px; }
  /* Phone and email are tap-to-call / tap-to-mail here, so they need to be
     real targets rather than 22px of text. */
  .info-link { display: inline-flex; align-items: center; min-height: 44px; }

  .site-footer { margin-top: 56px; padding: 26px 0; }
  .site-footer .wrap { column-gap: 18px; row-gap: 8px; }
  /* row 2 stops sharing a line and stacks left-aligned */
  .footer-copy { flex: 0 0 100%; }
  .footer-note { margin-left: 0; flex: 0 0 100%; }
  .lang-switch { flex: 0 0 100%; }
  .footer-social { flex: 0 0 100%; }

  .gallery-picker { padding: 0; align-items: flex-end; }
  .gallery-picker-panel {
    width: 100%; max-height: 88vh;
    border-radius: var(--radius-photo) var(--radius-photo) 0 0;
  }
}

/* Notched and rounded-corner handsets: keep content clear of the camera cutout
   in landscape and of the home indicator at the bottom. */
@supports (padding: max(0px, env(safe-area-inset-left))) {
  @media (max-width: 1023px) {
    .wrap, .hero-body {
      padding-left: max(var(--gutter), env(safe-area-inset-left));
      padding-right: max(var(--gutter), env(safe-area-inset-right));
    }
    .site-footer { padding-bottom: max(28px, env(safe-area-inset-bottom)); }
    .gallery-picker-grid { padding-bottom: max(20px, env(safe-area-inset-bottom)); }
  }
}

/* Very narrow handsets (360px and under) — trim type a little further so long
   Bulgarian compounds such as "Парапети и балюстради" stop wrapping badly. */
@media (max-width: 380px) {
  .hero-headline { font-size: 42px; }
  .brand-word { font-size: 15px; letter-spacing: .05em; }
  .brand-mark { width: 34px; height: 34px; }
  .filter-chip { font-size: 12.5px; padding: 10px 14px; }
  .about-title { font-size: 26px; }
  .gallery-pick-section-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
}

/* Landscape phones: the viewport is only ~380px tall, so a hero sized for a
   portrait screen would push everything off. */
@media (max-height: 480px) and (orientation: landscape) {
  .hero-photo { max-height: 42vh; }
  .hero-body { padding-top: calc(var(--nav-clearance) + 4px); }
  .lightbox-panel { max-height: 94vh; }
  .gallery-picker-panel { max-height: 94vh; }
}


/* ══════════════════════════════════════════════════════════════════════
   HOME
   ══════════════════════════════════════════════════════════════════════

   Light, quiet and mostly white. The page is carried by type, whitespace and
   hairline rules rather than by cards, shadows and filled boxes — the work in
   the photographs is busy enough on its own, and anything competing with it
   makes the page look cheaper than the ironwork does.

   Scoped to `body.home` so Gallery and Contact are untouched.
   ══════════════════════════════════════════════════════════════════════ */

body.home { background: var(--color-bg); }

/* Photos on the home page are NOT run through .washed. That filter exists to
   stop full-bleed photography fighting a cream page, but the home page is the
   one place where the piece itself has to look sharp and dark against white. */

/* — a single page-load reveal, staggered by an inline --d — */
@keyframes homeRise {
  /* Negative: the element starts above its resting place and settles down
     into it, so it enters the way something arriving from off the top of the
     page reads, rather than rising up out of the ground below it. */
  from { opacity: 0; transform: translateY(-14px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: no-preference) {
  body.home .rise { animation: homeRise .7s cubic-bezier(.16,.84,.44,1) both; animation-delay: var(--d, 0s); }
}

/* — small caps label in mono: the eyebrow on every section — */
.tag {
  display: inline-flex; align-items: center; gap: 11px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--color-accent-700);
}
.tag::before {
  content: ''; width: 22px; height: 1px; flex: none;
  background: var(--color-accent);
}

/* ══ hero ══════════════════════════════════════════════════════════ */

.forge-hero {
  padding: calc(var(--nav-clearance) + clamp(24px, 5vh, 64px)) 0 clamp(48px, 8vh, 96px);
}

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.04fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(32px, 4.5vw, 80px);
}

.hero-copy-2 { display: flex; flex-direction: column; align-items: flex-start; }

.forge-headline {
  margin-top: 20px;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(34px, 4.6vw, 70px);
  line-height: 1.04; letter-spacing: -.03em;
  color: var(--color-text);
}
/* The claim, set apart from the category by colour and weight only.
   Vollkorn's italic is a true cursive: fine in Latin, but Cyrillic italic
   letterforms change shape entirely and read as handwriting, which is the
   wrong register for a workshop. Roman throughout. */
.forge-headline .hot {
  display: block;
  font-weight: 600;
  font-size: .8em;
  color: var(--color-accent-700);
}

.forge-lede {
  margin-top: 22px; max-width: 50ch;
  font-size: clamp(16px, 1.05vw, 18.5px); line-height: 1.65;
  color: color-mix(in srgb, var(--color-text) 74%, transparent);
}
.forge-lede b { color: var(--color-text); font-weight: 600; }

.hero-cta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 30px; }

/* — the numbers, set as a spec line rather than three headline figures — */
.spec-row {
  display: flex; flex-wrap: wrap; gap: clamp(22px, 3vw, 54px);
  width: 100%;
  margin-top: clamp(30px, 4.5vh, 52px);
  padding-top: 24px;
  border-top: 1px solid var(--color-divider);
}
.spec-num {
  font-family: var(--font-mono); font-weight: 500;
  font-size: clamp(23px, 1.9vw, 31px); line-height: 1;
  color: var(--color-text);
}
.spec-cap {
  margin-top: 7px; max-width: 21ch;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 50%, transparent);
}

/* — the finished piece, with the forge cut into its corner: product and
     process in one frame, which is what every good blacksmith site leads
     with. Both photographs full-strength, no wash. — */
.hero-media-2 { position: relative; }

.hero-plate {
  border-radius: var(--radius-photo); overflow: hidden;
  aspect-ratio: 4 / 3;
  box-shadow: var(--shadow-md);
}
/* the hero photo is portrait (1067x1601) inside a landscape 4:3 frame, so a
   plain center crop loses the flame tips off the top and the tools' feet off
   the bottom equally — biasing the crop upward keeps the fire, which is the
   part actually doing the work in this shot */
.hero-plate img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 32%; }

.hero-inset {
  position: absolute; margin: 0;
  left: clamp(-44px, -2.4vw, -16px); bottom: clamp(-30px, -2.4vw, -18px);
  width: clamp(136px, 19vw, 232px);
  aspect-ratio: 3 / 2;
  border-radius: 10px; overflow: hidden;
  border: 4px solid var(--color-bg);
  box-shadow: var(--shadow-lg);
}
.hero-inset img { width: 100%; height: 100%; object-fit: cover; }
.hero-inset figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 20px 9px 6px;
  background: linear-gradient(to top, rgba(24,20,18,.82), transparent);
  font-family: var(--font-mono); font-size: 9px; letter-spacing: .1em;
  text-transform: uppercase; color: #f5ead8;
}

/* ══ shared section furniture ════════════════════════════════════ */

.band { padding: clamp(64px, 9vh, 120px) 0; }
.band-head { max-width: 720px; margin-bottom: clamp(32px, 4vw, 54px); }
.band-title {
  margin-top: 16px;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(27px, 3.2vw, 46px); line-height: 1.08; letter-spacing: -.025em;
}
.band-lede {
  margin-top: 16px; max-width: 58ch;
  font-size: 16.5px; line-height: 1.65;
  color: color-mix(in srgb, var(--color-text) 68%, transparent);
}

/* ══ work — five ways into the gallery ═══════════════════════════
   Captions sit under the photograph rather than over it. An overlay caption
   is unreadable on a phone against a busy ironwork photo, and it hides the
   part of the piece the visitor came to look at. */

.work-grid {
  display: grid; grid-template-columns: repeat(6, 1fr);
  gap: clamp(18px, 2vw, 34px) clamp(14px, 1.6vw, 26px);
}
.work-card:nth-child(1), .work-card:nth-child(2) { grid-column: span 3; }
.work-card:nth-child(3), .work-card:nth-child(4), .work-card:nth-child(5) { grid-column: span 2; }

.work-card { display: block; }
.work-shot {
  display: block; overflow: hidden;
  border-radius: var(--radius-photo);
  aspect-ratio: 4 / 3;
  background: var(--color-neutral-200);
}
.work-shot img {
  width: 100%; height: 100%; object-fit: cover;
  /* Driven by a property rather than a direct transform: the arrival settle
     (motion section) is a keyframe on the same element, and a hover rule that
     set `transform` outright would be what it had to fight. */
  transform: scale(var(--shot-scale, 1));
  transition: transform .6s cubic-bezier(.2,.8,.3,1);
}
.work-card:hover, .work-card:focus-visible { --shot-scale: 1.04; }

.work-body {
  position: relative;
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; margin-top: 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--color-divider);
}
/* The accent rule is drawn over the divider rather than recolouring it, so it
   can arrive from the left edge instead of the whole line changing at once —
   the hover reads as the card being underlined, which is what the pointer is
   actually doing. Sits on the divider, hence the -1px. */
.work-body::after {
  content: ''; position: absolute; left: 0; bottom: -1px;
  width: 100%; height: 1px;
  background: var(--color-accent);
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .45s var(--ease-out-soft, ease);
}
.work-card:hover .work-body::after,
.work-card:focus-visible .work-body::after { transform: scaleX(1); }

.work-name {
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(17px, 1.25vw, 21px); letter-spacing: -.015em;
  transition: color .2s ease;
}
.work-card:hover .work-name, .work-card:focus-visible .work-name { color: var(--color-accent-700); }
.work-count {
  flex: none;
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 45%, transparent);
}

.work-all { margin-top: clamp(30px, 3.4vw, 46px); }

/* ══ process — four steps under hairline rules ═══════════════════ */

.process-band { background: var(--color-band); }

.steps {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(24px, 2.6vw, 44px);
}
.step {
  position: relative;
  padding-top: 22px;
  /* Transparent, but still 2px: it is what reserves the space the rule sits
     in, so the pseudo-element below can be swept in without the step's own
     box moving. --rule is 1 unless the motion section takes it away. */
  border-top: 2px solid transparent;
}
.step::before {
  content: ''; position: absolute; left: 0; top: -2px;
  width: 100%; height: 2px;
  background: var(--color-text);
  transform: scaleX(var(--rule, 1)); transform-origin: 0 50%;
  transition: transform .9s var(--ease-out-soft, ease);
}
.step-no {
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  letter-spacing: .14em; color: var(--color-accent);
}
.step-title {
  margin-top: 12px;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(18px, 1.35vw, 22px); letter-spacing: -.02em;
}
.step-text {
  margin-top: 10px; font-size: 15px; line-height: 1.6;
  color: color-mix(in srgb, var(--color-text) 70%, transparent);
}

/* ══ the shop ════════════════════════════════════════════════════ */

.shop-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .8fr);
  gap: clamp(34px, 4.5vw, 76px); align-items: center;
}
.shop-copy p {
  font-size: 16.5px; line-height: 1.7;
  color: color-mix(in srgb, var(--color-text) 74%, transparent);
}
.shop-copy p + p { margin-top: 16px; }
.shop-copy p b { color: var(--color-text); font-weight: 600; }

.shop-photo {
  position: relative; margin: 0;
  border-radius: var(--radius-photo); overflow: hidden;
  aspect-ratio: 4 / 5; max-height: 620px;
  box-shadow: var(--shadow-md);
}
.shop-photo img { width: 100%; height: 100%; object-fit: cover; }
.shop-photo figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 40px 18px 14px;
  background: linear-gradient(to top, rgba(24,20,18,.8), transparent);
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em;
  text-transform: uppercase; color: #f5ead8;
}

/* — the four things every caller asks, as a plain definition list — */
.fact-strip {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px 28px; margin: 32px 0 0;
  padding-top: 24px; border-top: 1px solid var(--color-divider);
}
.fact-strip dt {
  font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 45%, transparent);
}
.fact-strip dd { margin: 6px 0 0; font-size: 15.5px; }

/* ══ closing call ════════════════════════════════════════════════ */

.cta-band {
  padding: clamp(64px, 9vh, 112px) 0;
  background: var(--color-band);
  border-top: 1px solid var(--color-divider);
  text-align: center;
}
.cta-band .tag { justify-content: center; }
.cta-title {
  margin: 16px auto 0; max-width: 17ch;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(28px, 3.6vw, 52px); line-height: 1.06; letter-spacing: -.03em;
}
.cta-lede {
  margin: 18px auto 0; max-width: 52ch;
  font-size: 16.5px; line-height: 1.62;
  color: color-mix(in srgb, var(--color-text) 68%, transparent);
}
.cta-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 28px; }
.cta-line {
  margin-top: 24px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: .09em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 45%, transparent);
}

body.home .site-footer { margin-top: 0; }

/* ══ home — responsive ═══════════════════════════════════════════ */

@media (max-width: 1100px) {
  .work-card:nth-child(n) { grid-column: span 3; }
  .steps { grid-template-columns: repeat(2, 1fr); gap: 30px; }
}

@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; gap: 34px; }
  .hero-media-2 { order: -1; }
  .hero-plate { aspect-ratio: 16 / 10; }
  .hero-inset { left: auto; right: -8px; bottom: -22px; width: clamp(118px, 27vw, 176px); }
  .shop-grid { grid-template-columns: 1fr; gap: 34px; }
  .shop-photo { aspect-ratio: 4 / 3; }
}

/* Phones. The site is browsed on a phone far more than on a desktop, so this
   block is where the home page is actually designed: one column, full-width
   tap targets, and nothing that needs a wide viewport to make sense. */
@media (max-width: 767px) {
  .forge-hero { padding-top: calc(var(--nav-clearance) + 12px); padding-bottom: 40px; }
  .work-card:nth-child(n) { grid-column: span 6; }
  .work-grid { gap: 26px; }
  .steps { grid-template-columns: 1fr; gap: 24px; }
  .fact-strip { grid-template-columns: 1fr; gap: 18px; }

  .hero-cta, .cta-actions { flex-direction: column; align-items: stretch; width: 100%; }
  .hero-cta .btn, .cta-actions .btn { width: 100%; }

  .spec-row { gap: 18px 32px; }
  .hero-inset { width: 118px; right: 0; bottom: -18px; border-width: 3px; }
  /* at 118px wide the caption wraps over half the photograph — the inset
     reads perfectly well without it on a phone */
  .hero-inset figcaption { display: none; }
  /* the mono eyebrow is wide: trimmed so it holds one line at 360px */
  .tag { font-size: 10px; letter-spacing: .11em; }
  .tag::before { width: 16px; }
  .band { padding: 56px 0; }
  .cta-band { padding: 56px 0; }
}

@media (max-width: 380px) {
  .forge-headline { font-size: 34px; }
  .band-title { font-size: 25px; }
}


/* ══════════════════════════════════════════════════════════════════════
   MOTION
   ══════════════════════════════════════════════════════════════════════

   One easing curve and three durations, shared by every page. Everything
   below animates opacity and transform only — nothing touches a layout
   property, so nothing costs a reflow while the page is scrolling.

   What is allowed to move: a section arriving on screen, a control
   answering a press, and an overlay opening or closing. Nothing loops,
   nothing bounces, and nothing moves far — the ironwork in the photographs
   is the loudest thing on the page and it stays that way.
   ══════════════════════════════════════════════════════════════════════ */

:root {
  /* Decelerating with no overshoot: quick off the mark, long tail. Used by
     anything entering the page. Already the curve behind .rise, lifted into
     a token so the rest of the site can share it. */
  --ease-out-soft: cubic-bezier(.16, .84, .44, 1);
  /* Symmetrical, for moves that run both ways — an overlay, a press. */
  --ease-inout: cubic-bezier(.4, 0, .2, 1);

  --dur-press: .12s;
  --dur-overlay: .3s;
  --dur-reveal: .72s;
}

/* ── sections arriving on scroll ──────────────────────────────────────

   Reveal targets. This list is mirrored in js/reveal.js, which observes the
   same elements; it has to exist here as well because a stylesheet applies
   at parse time, and if the hidden starting state were added by the script
   the browser could paint a section and then blink it away.

   The two states ride on custom properties rather than on two competing
   selectors, so the list is only written once: the rule reads --rv-o and
   --rv-y, and .is-in — added by the observer — sets them. Custom properties
   inherit rather than compete, so a hover rule elsewhere can move the same
   element by changing --rv-y without fighting this rule for specificity.

   Scoped to `html.js`, set by a one-line inline script in each <head>: with
   scripts off, and for a crawler that does not run them, nothing is ever
   hidden and the finished page is what gets rendered and indexed. */

@media (prefers-reduced-motion: no-preference) {
  .js :is(
    .band-head > *,
    .work-grid > .work-card,
    .work-all,
    .steps > .step,
    .shop-copy > *,
    .shop-photo,
    .cta-band .wrap > *,
    .page-head .wrap > *,
    .filter-row,
    .contact-grid > *
  ) {
    opacity: var(--rv-o, 0);
    /* Negative: content starts above its resting place and settles down into
       it, rather than rising up into place from below. */
    transform: translateY(var(--rv-y, -18px));
    transition:
      opacity var(--dur-reveal) var(--ease-out-soft),
      transform var(--dur-reveal) var(--ease-out-soft);
    /* --i is the element's place in its own group, set and capped by
       reveal.js — past about five steps a stagger stops reading as one
       gesture and starts reading as a queue. */
    transition-delay: calc(var(--i, 0) * 70ms);
  }
  .is-in { --rv-o: 1; --rv-y: 0px; }
}

/* ── home hero ────────────────────────────────────────────────────────
   The copy already rises in on load (.rise). The photograph gets the one
   move worth giving a photograph: it lands a shade over-scaled and settles
   back, so the frame reads as arriving rather than as having always been
   there. Slower than the text, and with no offset — a photo that slides
   looks like a slideshow. */

@keyframes photoSettle {
  from { transform: scale(1.055); }
  to   { transform: none; }
}

/* The bar is fixed and centred by a transform, so its own entrance has to
   carry translateX(-50%) through every frame or it snaps to the left edge.
   Home only: this is the arrival of the site, not something to replay on
   every page the visitor opens. */
@keyframes navIn {
  from { opacity: 0; transform: translateX(-50%) translateY(-14px); }
  to   { opacity: 1; transform: translateX(-50%); }
}

@media (prefers-reduced-motion: no-preference) {
  body.home .hero-plate img { animation: photoSettle 1.6s var(--ease-out-soft) both; }
  body.home .glass-nav { animation: navIn .55s var(--ease-out-soft) both; }
}

/* ── controls answer the press ────────────────────────────────────────
   A control dips a couple of percent under the finger and comes straight
   back. It is the smallest possible confirmation that the tap landed, and
   on a touch screen — where there is no hover to fall back on — it is the
   only one the visitor gets. */

.btn {
  transition:
    background-color .15s ease,
    border-color .15s ease,
    transform var(--dur-press) var(--ease-inout);
}
.btn:active { transform: scale(.97); }
.btn:disabled:active { transform: none; }

.filter-chip {
  transition:
    background-color .15s ease, color .15s ease, border-color .15s ease,
    transform var(--dur-press) var(--ease-inout);
}
.filter-chip:active { transform: scale(.96); }

.nav-toggle { transition: transform var(--dur-press) var(--ease-inout); }
.nav-toggle:active { transform: scale(.9); }

.lightbox-close, .lightbox-nav, .gallery-picker-close, .lb-thumb {
  transition:
    opacity .15s ease, border-color .15s ease,
    transform var(--dur-press) var(--ease-inout);
}
.lightbox-close:active, .gallery-picker-close:active { transform: scale(.9); }
.lightbox-nav:active { transform: translateY(-50%) scale(.9); }

/* ── gallery grid ─────────────────────────────────────────────────────
   The card lifts and the photograph inside it drifts up in scale rather
   more slowly; that difference in speed is what gives the hover its depth,
   not the shadow.

   The reveal used to be an `animation` with fill-mode `both`, which meant
   every revealed tile held its final transform permanently and the hover
   lift below could never take effect. It is a transition now, moving the
   same --rv-y the sections above use, so hover and reveal share one
   property without overriding each other. */

.js .gallery-item {
  opacity: var(--rv-o, 0);
  /* Negative: tiles settle down into the grid from above rather than rising
     up into it, matching the direction every other reveal on the site now
     uses. */
  transform: translateY(var(--rv-y, -20px));
  transition:
    opacity .55s var(--ease-out-soft),
    transform .55s var(--ease-out-soft),
    box-shadow .3s var(--ease-out-soft);
  transition-delay: calc(var(--i, 0) * 60ms);
}
.gallery-item.is-visible { --rv-o: 1; --rv-y: 0px; }
.gallery-item:hover, .gallery-item:focus-visible {
  --rv-y: -3px;
  transition-delay: 0s;   /* a hover must not wait out the reveal stagger */
}
.gallery-item img { transition: transform .75s var(--ease-out-soft); }
.gallery-item:hover img, .gallery-item:focus-visible img { transform: scale(1.04); }
.gallery-caption { transition: transform .34s var(--ease-out-soft); }

/* ── overlays ─────────────────────────────────────────────────────────
   The scrim fades and the panel comes up from a whisker under full size —
   the gesture a sheet uses on iOS. Both the opening and the closing are
   animated, which is why `hidden` is no longer flipped on its own:
   gallery.js and contact.js set .is-open first and only mark the overlay
   hidden once the panel has finished leaving. */

.lightbox, .gallery-picker {
  opacity: 0;
  transition: opacity var(--dur-overlay) var(--ease-inout);
}
.lightbox.is-open, .gallery-picker.is-open { opacity: 1; }
/* Both are full-viewport, and for the length of the closing fade they are
   still `hidden="false"` and so still on top of the page. Without this the
   invisible scrim would swallow every click for a third of a second after it
   was dismissed. */
.lightbox:not(.is-open), .gallery-picker:not(.is-open) { pointer-events: none; }

.lightbox-panel, .gallery-picker-panel {
  opacity: 0;
  transform: scale(.97) translateY(10px);
  transition:
    opacity var(--dur-overlay) var(--ease-inout),
    transform .34s var(--ease-out-soft);
}
.lightbox.is-open .lightbox-panel,
.gallery-picker.is-open .gallery-picker-panel { opacity: 1; transform: none; }

/* On a phone the picker is a bottom sheet, and a sheet that scales up from
   its centre looks like a dialog that lost its way. It slides. */
@media (max-width: 767px) {
  .gallery-picker-panel { transform: translateY(24px); }
  .gallery-picker.is-open .gallery-picker-panel { transform: none; }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes revealIn {
  from { opacity: 0; transform: translateY(-5px); }
  to   { opacity: 1; transform: none; }
}
/* Stepping between pieces replaces the <img>, so this replays per photo and
   the two shots cross over instead of cutting. */
@media (prefers-reduced-motion: no-preference) {
  .lightbox-media img { animation: fadeIn .3s var(--ease-out-soft) both; }
}

/* ── disclosures ──────────────────────────────────────────────────────
   Content that appears in place rather than arriving with the page: the
   mobile menu, a validation message, an attached thumbnail, the privacy
   notice. All the same short move, so they read as one behaviour. */

@keyframes navDrop {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: no-preference) {
  @media (max-width: 767px) {
    .nav-links.is-open { animation: navDrop .3s var(--ease-out-soft) both; }
  }
  .field.is-invalid .field-error { animation: revealIn .28s var(--ease-out-soft) both; }
  .file-thumb { animation: revealIn .3s var(--ease-out-soft) both; }
  .privacy-note[open] .privacy-body { animation: revealIn .34s var(--ease-out-soft) both; }
}

/* ── in-page anchors ──────────────────────────────────────────────────
   The nav is fixed, so a jump to #shop has to stop short of the top of the
   viewport or the heading lands underneath the bar. And the jump itself is
   worth animating: it is the one place on the site where the visitor would
   otherwise lose their place entirely. */

.band, .cta-band { scroll-margin-top: var(--nav-clearance); }

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* ══════════════════════════════════════════════════════════════════════
   MOTION — second pass
   ══════════════════════════════════════════════════════════════════════

   The first pass gave every section the same fade-up. That is the baseline
   everyone has, and it reads as such. This pass replaces the generic move
   with one that belongs to each thing being moved: a headline is uncovered,
   a rule is drawn, a photograph settles, a page hands over to the next page.

   Still opacity, transform and clip-path only. Still nothing that loops.
   ══════════════════════════════════════════════════════════════════════ */

/* ── headlines are uncovered, not faded ───────────────────────────────
   Type looks wrong fading in — letterforms go grey and muddy through the
   middle of the fade. Uncovering it top-first keeps every glyph at full
   weight the whole way, and reads as a blind lifting off the line rather
   than as a wipe passing over it.

   The clip's top edge is what stays fixed open (-8%/-35%, per side); it is
   the BOTTOM inset that moves, from 100% — which pulls the visible window's
   bottom edge up to the line's own top, hiding everything — down to -35%,
   clear of the line. So the reveal uncovers from the cap-height down to the
   baseline, not the other way round: swap which inset is held fixed and
   which one animates, and the direction reverses.

   The negative insets matter: at 0 the clip would shear the ascenders and
   the accents on Cyrillic capitals, which is exactly the kind of damage
   nobody notices in review and everybody notices in use. */

@keyframes titleWipe {
  from { clip-path: inset(-35% -8% 100% -8%); transform: translateY(-.32em); }
  to   { clip-path: inset(-35% -8% -35% -8%); transform: none; }
}

@media (prefers-reduced-motion: no-preference) {
  /* Held covered until the observer arrives. The fade the section reveal
     would otherwise give them is switched off here — --rv-o/--rv-y are set
     outright, so the wipe is the whole move and the two do not compete. */
  .js :is(.band-title, .page-title, .cta-title) {
    --rv-o: 1;
    --rv-y: 0px;
    clip-path: inset(-35% -8% 100% -8%);
  }
  /* The uncovered clip is declared as well as animated. The animation fills
     forwards to the same value, so it makes no difference while it runs — but
     it means the headline is revealed by the class landing rather than by the
     animation finishing, and a headline is not a thing to leave depending on
     an animation nobody has guaranteed will play. */
  .js :is(.band-title, .page-title, .cta-title).is-in {
    clip-path: inset(-35% -8% -35% -8%);
    animation: titleWipe .95s var(--ease-out-soft) both;
  }

  /* The hero headline runs on page load rather than on scroll, so it takes
     the wipe in place of .rise's plain fade, keeping .rise's own delay. */
  body.home .forge-headline.rise {
    animation: titleWipe 1.05s var(--ease-out-soft) both;
    animation-delay: var(--d, 0s);
  }
}

/* ── the process rules draw themselves ────────────────────────────────
   Four steps under four hairlines. The rules arriving left to right, a beat
   behind their own step, is the whole gesture — it turns a grid of four
   boxes into a sequence you read in order. */

@media (prefers-reduced-motion: no-preference) {
  .js .steps > .step { --rule: 0; }
  .js .steps > .step.is-in { --rule: 1; }
  .js .steps > .step::before { transition-delay: calc(var(--i, 0) * 70ms + 120ms); }
}

/* ── photographs settle rather than appear ────────────────────────────
   The same move the hero photograph makes on load, given to the work grid on
   arrival: a shade over-scale, easing back. `backwards` and not `both` is
   deliberate — a filled-forwards animation would pin the transform for good
   and the hover scale could never take it back, which is the exact bug the
   gallery reveal used to have. */

@keyframes shotSettle {
  from { transform: scale(1.06); }
}

@media (prefers-reduced-motion: no-preference) {
  .js .work-card.is-in .work-shot img {
    animation: shotSettle 1.15s var(--ease-out-soft) backwards;
    animation-delay: calc(var(--i, 0) * 70ms);
  }
}

/* ── the hero has depth on the way out ────────────────────────────────
   The plate and the inset pull apart slightly as the hero leaves, which is
   the only thing on the page tied to scroll position rather than to a
   trigger. Scroll-driven CSS, so it costs no script and no scroll listener,
   and it is behind @supports because a browser without it simply gets a hero
   that does not drift — which is what the page does today.

   Desktop only. On a phone the hero is already stacked and short, and
   anything moving under a thumb on a small screen reads as a glitch. */

@keyframes plateDrift { to { transform: translateY(-30px); } }
@keyframes insetDrift { to { transform: translateY(16px); } }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 900px) {
    body.home .hero-plate {
      animation: plateDrift linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 100%;
    }
    body.home .hero-inset {
      animation: insetDrift linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 100%;
    }
  }
}

/* ── one page hands over to the next ──────────────────────────────────
   Three pages that share a nav, a footer and a palette should not blink
   white between them. A cross-document view transition dissolves the old
   page into the new one, and naming the nav bar keeps it standing still
   while the content changes underneath — which is what makes the site feel
   like one thing rather than three documents.

   No script: a browser without it navigates the way it always did. */

@view-transition { navigation: auto; }

.glass-nav { view-transition-name: site-nav; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: .32s;
  animation-timing-function: var(--ease-inout);
}

/* The tile a visitor clicked in the gallery grows into the lightbox photo
   instead of the panel appearing over the top of it. gallery.js hands the
   name to the tile's own <img> for the length of the transition; the name
   lives here on the lightbox photo, which is display:none until it opens and
   so never holds it at the same time. */
.lightbox-media img { view-transition-name: lb-photo; }

::view-transition-group(lb-photo) {
  animation-duration: .42s;
  animation-timing-function: var(--ease-out-soft);
}

/* View transitions run on pseudo-elements outside the document tree, so the
   blanket reduce rule at the end of the file does not reach them. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ── the escape hatch ─────────────────────────────────────────────────
   Last in the file so it beats everything above it. A visitor who has asked
   their system for reduced motion gets the finished state of every one of
   these, instantly — not a slower version of them. reveal.js checks the same
   preference and never hides anything in the first place. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
}
