:root {
  --color-bg: #f5f3ef;
  --color-surface: #f6f9f2;
  --color-text: #26331f;
  --color-text-on-dark: #e9f1e3;
  --color-muted: #5d6b58;
  --color-primary: #2e5d43;
  --color-primary-dark: #1e4230;
  --color-secondary: #7a8a75;
  --color-secondary-light: #d8e2cf;
  --color-accent: #d9a441;
  --color-border: #c9d6c0;
  --font-main: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --radius: 12px;
  --shadow-soft: 0 2px 5px rgba(9, 22, 13, 0.22), 0 12px 30px rgba(9, 22, 13, 0.26);
  --shadow-lift: 0 4px 10px rgba(9, 22, 13, 0.26), 0 20px 44px rgba(9, 22, 13, 0.34);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: var(--font-main);
  background-color: #18301f;
  color: var(--color-text-on-dark);
  line-height: 1.5;
}

/* Deep forest backdrop: soft light from the top, dark edges, and a pair of
   blurred leaf silhouettes (blur is baked into the SVGs). Fixed pseudo-layer
   so it stays put while the page scrolls; admin.css disables it for the
   cabinet, which keeps its own plain background. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='700' height='700' viewBox='0 0 700 700'%3E%3Cfilter id='b'%3E%3CfeGaussianBlur stdDeviation='11'/%3E%3C/filter%3E%3Cg filter='url(%23b)' fill='%230a1e11'%3E%3Cg opacity='.55'%3E%3Cpath d='M350 650 C 295 420 295 250 350 60 C 405 250 405 420 350 650 Z'/%3E%3C/g%3E%3Cg opacity='.4' transform='rotate(-34 350 650)'%3E%3Cpath d='M350 650 C 300 440 300 300 350 130 C 400 300 400 440 350 650 Z'/%3E%3C/g%3E%3Cg opacity='.4' transform='rotate(34 350 650)'%3E%3Cpath d='M350 650 C 300 440 300 300 350 130 C 400 300 400 440 350 650 Z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E") left -170px bottom -180px / 720px auto no-repeat,
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='700' height='700' viewBox='0 0 700 700'%3E%3Cfilter id='b'%3E%3CfeGaussianBlur stdDeviation='12'/%3E%3C/filter%3E%3Cg filter='url(%23b)' fill='%230a1e11'%3E%3Cg opacity='.45' transform='rotate(155 350 350)'%3E%3Cpath d='M350 650 C 295 420 295 250 350 60 C 405 250 405 420 350 650 Z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E") right -190px top -140px / 640px auto no-repeat,
    radial-gradient(ellipse 130% 75% at 50% -12%, rgba(96, 168, 122, 0.5), rgba(96, 168, 122, 0) 62%),
    linear-gradient(176deg, #2f5f41 0%, #24492f 42%, #16301f 100%);
}

/* Footer sticks to the bottom of the viewport on short pages, but scrolls with
   content normally once content is taller than the screen. */
body > main {
  flex: 1 0 auto;
}

body > footer {
  flex-shrink: 0;
}

/* Every media slot on the site accepts an uploaded clip as readily as a photo
   (see the uploader in backend/routes/media.js), so <video> is styled in
   lockstep with <img> everywhere below — otherwise a slot silently changes
   layout the moment its content becomes a video. */
img,
video {
  max-width: 100%;
  display: block;
}

.container {
  /* width:100% is load-bearing here. <body> is a column flex container, and
     auto margins on the cross axis suppress stretching — so without it the
     container is sized to its content and merely centred, which looks right
     only while the content happens to be wide. Give a page narrow copy and the
     whole layout, photo and heading included, shrinks with it. */
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 20px;
}

a {
  color: #cfe3b4;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* Header — floating dark bar, separated from the backdrop */

.site-header {
  padding-top: 16px;
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 10px 18px;
  flex-wrap: wrap;
  background: linear-gradient(180deg, #31372f, #22261f);
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 16px;
  box-shadow: 0 2px 5px rgba(5, 14, 8, 0.4), 0 16px 36px rgba(5, 14, 8, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

/* Light chip behind the logo so any logo (including a dark one) stays visible
   on the dark bar. */
/* No light chip behind the logo: the header uses a variant of the client's
   logo made for dark backgrounds (scripts/make-icons.js → logo-horizontal-light),
   and a pale plate on the dark bar looked pasted on. Padding stays so the
   click area and the header height do not change. */
.logo-slot {
  border-radius: 10px;
  padding: 5px 4px;
  display: inline-flex;
}

.logo-slot img,
.logo-slot video {
  height: 36px;
  width: auto;
}

.main-nav {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.main-nav a {
  color: #d6e2cf;
  font-weight: 600;
  padding: 7px 12px;
  border-radius: 999px;
}

.main-nav a:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.09);
  text-decoration: none;
}

/* Segmented language switch */

.lang-switch {
  display: flex;
  gap: 2px;
  padding: 3px;
  background: rgba(0, 0, 0, 0.3);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 999px;
}

.lang-switch a {
  padding: 4px 11px;
  border-radius: 999px;
  color: #aec0a8;
  font-size: 0.85rem;
  font-weight: 600;
}

.lang-switch a:hover {
  color: #fff;
  text-decoration: none;
}

.lang-switch a.active {
  background: linear-gradient(180deg, #41795a, #2c5a3f);
  color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

/* Category grid (homepage) — light volumetric cards over the dark backdrop */

.category-page-main {
  min-height: 60vh;
  padding: 48px 0;
}

.category-grid {
  display: flex;
  flex-direction: column;
  gap: 34px;
}

.category-tile {
  position: relative;
  display: block;
  min-height: clamp(220px, 24vw, 420px);
  border-radius: 18px;
  overflow: hidden;
  color: inherit;
  background: #e6efe0;
  border: 1px solid #d7e3cc;
  box-shadow: 0 2px 5px rgba(7, 18, 10, 0.3), 0 20px 46px rgba(7, 18, 10, 0.42), inset 0 1px 0 rgba(255, 255, 255, 0.65);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}

.category-tile:hover {
  text-decoration: none;
  transform: translateY(-3px);
  box-shadow: 0 4px 8px rgba(7, 18, 10, 0.32), 0 28px 56px rgba(7, 18, 10, 0.48), inset 0 1px 0 rgba(255, 255, 255, 0.65);
}

.category-photo-frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 1;
}

/* Posters stay visible, just slightly hazed. On hover the photo zooms in a
   touch (smooth animation) and the CTA pill fades in. */
.category-photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: blur(4px);
  transform: scale(1.06);
  transition: transform 0.35s ease;
}

.category-tile:hover .category-photo,
.category-tile:focus-visible .category-photo {
  transform: scale(1.15);
}

/* A faint, colourless lift — just enough to keep a dark title legible over a
   dark photo. This used to be a sage wash at 55-62%, which over placeholder
   art looked like nothing but over real photographs veiled them in green and
   drained their colour; the readability it bought now comes from the halo
   behind the title itself (see .category-plate) instead of from covering the
   whole picture. */
.category-photo-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0.2));
  /* An ::after is generated as the element's last child, so this wash paints
     above the replace overlay and would otherwise absorb the click meant for
     it. It is decoration only and never needs pointer events. */
  pointer-events: none;
}

.category-plate {
  position: absolute;
  inset: 0;
  z-index: 2;
  /* A wide, faint lift under the title. Kept weak on purpose: most of the
     legibility comes from the glow hugging the letters below, which stays
     invisible on a light photo instead of printing a grey ellipse on a dark
     one. */
  background: radial-gradient(70% 55% at 50% 50%, rgba(252, 253, 250, 0.3), rgba(252, 253, 250, 0) 78%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 24px;
  text-align: center;
}

.category-plate span {
  font-size: 1.5rem;
  font-weight: 700;
  color: #223c2a;
  /* Carries the legibility now that the photo is no longer washed out: a tight
     white glow around the glyphs reads as intentional on a dark photo and
     disappears on a light one, which a rectangle or a blob behind the text
     cannot do. */
  text-shadow:
    0 1px 2px rgba(255, 255, 255, 0.95),
    0 0 5px rgba(255, 255, 255, 0.95),
    0 0 11px rgba(255, 255, 255, 0.85),
    0 0 22px rgba(255, 255, 255, 0.6);
}

/* White pill inside the tile — purely visual, the whole tile is the link.
   Hidden until the tile is hovered (or focused from the keyboard); absolutely
   positioned so its appearance never shifts the centered title. */
.category-cta {
  position: absolute;
  left: 50%;
  bottom: 42px;
  display: inline-block;
  padding: 10px 28px;
  background: linear-gradient(180deg, #ffffff, #edf3e8);
  border: 1px solid #ccd8c2;
  border-radius: 999px;
  color: #2c4a35;
  font-weight: 600;
  font-size: 0.95rem;
  box-shadow: 0 2px 5px rgba(15, 35, 20, 0.28), inset 0 1px 0 #ffffff;
  opacity: 0;
  transform: translateX(-50%) translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.category-tile:hover .category-cta,
.category-tile:focus-visible .category-cta {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  background: linear-gradient(180deg, #ffffff, #f4f8f0);
}

/* Category detail page */

.category-detail {
  padding: 40px 20px;
}

.category-hero img,
.category-hero video {
  width: 100%;
  max-height: 420px;
  object-fit: cover;
  border-radius: 16px;
  box-shadow: var(--shadow-soft);
  margin-bottom: 24px;
}

/* Buttons — volumetric green pills */

.btn {
  display: inline-block;
  padding: 11px 26px;
  background: linear-gradient(180deg, #41795a, #2b583d);
  color: #fff;
  border: 1px solid #23492f;
  border-radius: 999px;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 2px 5px rgba(9, 22, 13, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.22);
  transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}

.btn:hover {
  background: linear-gradient(180deg, #4a8a67, #316347);
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: 0 4px 9px rgba(9, 22, 13, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

.btn:active {
  transform: translateY(1px);
  box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3);
}

.hero-image img,
.hero-image video {
  border-radius: 16px;
  box-shadow: var(--shadow-soft);
  width: 100%;
}

/* Sections */

section {
  padding: 48px 0;
}

section h2 {
  color: #f0f5e9;
  border-bottom: 2px solid rgba(255, 255, 255, 0.18);
  padding-bottom: 8px;
  margin-bottom: 24px;
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 24px;
}

.card {
  background: linear-gradient(180deg, #fbfdf8, #edf3e7);
  border: 1px solid #d3dfcc;
  border-radius: 16px;
  overflow: hidden;
  color: var(--color-text);
  box-shadow: var(--shadow-soft);
}

.card a {
  color: var(--color-primary);
}

.card img,
.card video {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.card-body {
  padding: 16px;
}

.card-body h3 {
  margin: 0 0 8px;
  color: var(--color-primary-dark);
}

.card-body p {
  color: var(--color-muted);
  margin: 0 0 8px;
}

.badge {
  display: inline-block;
  font-size: 0.8rem;
  background: var(--color-secondary-light);
  color: var(--color-primary-dark);
  padding: 2px 10px;
  border-radius: 999px;
}

/* Gallery */

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

.gallery-grid img,
.gallery-grid video {
  border-radius: 14px;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  box-shadow: var(--shadow-soft);
}

/* Contact form */

.contact-form {
  display: grid;
  gap: 12px;
  max-width: 480px;
}

.contact-form input,
.contact-form textarea {
  padding: 11px 14px;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  font-family: inherit;
  font-size: 1rem;
  background: #f6f9f2;
  color: var(--color-text);
  box-shadow: inset 0 2px 4px rgba(20, 40, 25, 0.09);
}

.contact-form input:focus,
.contact-form textarea:focus {
  outline: 2px solid rgba(96, 168, 122, 0.6);
  outline-offset: 1px;
}

.form-status {
  font-size: 0.9rem;
}

.form-status.success {
  color: #8fd09a;
}

.form-status.error {
  color: #f0a3a3;
}

/* Footer — minimal, no bar: just quiet text over the backdrop */

.site-footer {
  color: rgba(233, 241, 227, 0.6);
  padding: 20px 0;
  margin-top: 48px;
  font-size: 0.9rem;
  border-top: 1px solid rgba(233, 241, 227, 0.15);
}

.site-footer a {
  color: rgba(233, 241, 227, 0.85);
}

.footer-inner {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px 18px;
}

/* Watermark slot (applied over featured product/gallery imagery once client provides the mark) */

.watermark-slot {
  position: absolute;
  right: 8px;
  bottom: 8px;
  width: 64px;
  opacity: 0.85;
  pointer-events: none;
}

.media-frame {
  position: relative;
}

/* Staff (admin/editor) edit mode */

/* Agentation (visual feedback devtool, staff-only) can otherwise end up
   behind a hovered .category-tile — the hover lift uses `transform`, which
   opens a new stacking context that competes with fixed UI lacking its own
   z-index. Forced above everything, same range as the library's own
   active-mode overlays. */
.agentation-devtools {
  z-index: 100000 !important;
}

.staff-toolbar {
  position: fixed;
  bottom: 16px;
  left: 16px;
  z-index: 1000;
  background: linear-gradient(180deg, #31372f, #22261f);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 12px;
  padding: 10px 14px;
  display: flex;
  align-items: center;
  gap: 10px;
  box-shadow: var(--shadow-lift);
  font-size: 0.9rem;
}

.staff-toolbar button {
  background: linear-gradient(180deg, #41795a, #2b583d);
  color: #fff;
  border: 1px solid #23492f;
  border-radius: 999px;
  padding: 6px 14px;
  cursor: pointer;
}

.staff-toolbar button:hover {
  background: linear-gradient(180deg, #4a8a67, #316347);
}

.format-toolbar {
  display: none;
  position: absolute;
  z-index: 1100;
  align-items: center;
  gap: 6px;
  background: #22261f;
  color: #fff;
  padding: 6px 8px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  box-shadow: var(--shadow-lift);
  font-size: 0.85rem;
  flex-wrap: wrap;
  max-width: 90vw;
}

.format-toolbar button,
.format-toolbar select {
  background: #39413a;
  color: #fff;
  border: 1px solid #4d5a4d;
  border-radius: 8px;
  padding: 4px 8px;
  cursor: pointer;
  font-size: 0.85rem;
}

.format-toolbar button:hover {
  background: #4a5a4b;
}

.format-toolbar button.active {
  background: var(--color-primary);
  border-color: #fff;
  box-shadow: inset 0 0 0 1px #fff;
}

.format-toolbar select:focus,
.format-toolbar select:hover {
  border-color: var(--color-accent);
}

.format-toolbar .needs-selection {
  opacity: 0.4;
}

.format-toolbar .format-hint {
  display: none;
  font-size: 0.78rem;
  color: #b7c8b0;
  font-style: italic;
  white-space: nowrap;
}

.format-toolbar .format-sep {
  width: 1px;
  align-self: stretch;
  background: #4d5a4d;
  margin: 0 2px;
}

.format-toolbar .format-color {
  display: flex;
  align-items: center;
  gap: 4px;
  cursor: default;
}

.format-toolbar .format-color input[type="color"] {
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid #4d5a4d;
  border-radius: 8px;
  background: none;
  cursor: pointer;
}

body.edit-mode [data-block] {
  outline: 1px dashed transparent;
  transition: outline-color 0.15s;
}

body.edit-mode [data-block]:hover {
  outline-color: var(--color-accent);
  cursor: text;
}

body.edit-mode img[data-block]:hover,
body.edit-mode video[data-block]:hover {
  cursor: pointer;
}

body.edit-mode [data-block][contenteditable="true"]:focus {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  background: rgba(255, 248, 236, 0.95);
  color: var(--color-text);
}

.item-controls {
  display: none;
  gap: 6px;
  margin-top: 8px;
  position: relative;
  z-index: 3;
}

body.edit-mode .item-controls {
  display: flex;
}

body.edit-mode .item-card {
  position: relative;
  cursor: grab;
}

body.edit-mode .card.item-card,
body.edit-mode .category-tile.item-card {
  outline: 1px dashed var(--color-border);
}

.item-controls button {
  font-size: 0.8rem;
  padding: 4px 8px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  border-radius: 8px;
  cursor: pointer;
}

.item-controls .danger {
  color: #c04a4a;
  border-color: #c04a4a;
}

.add-item-btn {
  display: none;
  margin-top: 12px;
}

body.edit-mode .add-item-btn {
  display: inline-block;
}

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.image-replace-overlay {
  display: none;
  position: absolute;
  inset: 0;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  font-size: 0.85rem;
  cursor: pointer;
}

body.edit-mode .media-frame:hover .image-replace-overlay,
body.edit-mode .category-tile:hover .image-replace-overlay {
  display: flex;
}

/* .category-plate is a full-tile absolute layer at z-index 2, so it used to
   swallow every click meant for the replace overlay underneath it (which sits
   inside .category-photo-frame at z-index 1 and therefore cannot outrank the
   plate from inside its stacking context) — replacing a category photo from the
   editor was impossible, the click always landed on the plate. In edit mode the
   plate stops taking pointer events and hands them back to the overlay; its
   editable title opts back in, so clicking the text still edits the text and
   clicking anywhere else replaces the photo. The CTA pill stays inert: it is
   decorative, the whole tile is the link. */
body.edit-mode .category-plate {
  pointer-events: none;
}

body.edit-mode .category-plate [data-field] {
  pointer-events: auto;
}

/* Reframing: while a card is being panned, the replace-photo overlay has to get
   out of the way — it covers the whole media area and would swallow the drag
   (and a click on it would open the file picker mid-gesture). */
body.edit-mode .item-card.reframing .image-replace-overlay {
  display: none !important;
}

body.edit-mode .item-card.reframing {
  outline: 2px solid var(--color-accent);
  cursor: grab;
}

body.edit-mode .item-card.reframing:active {
  cursor: grabbing;
}

body.edit-mode .item-card.reframing [data-field="image_url"] {
  /* the pan is a drag, not a text selection or a native image drag */
  user-select: none;
  -webkit-user-drag: none;
}

/* Upload feedback. Transcoding a phone clip takes tens of seconds, and every
   upload failure used to be a silent console.error — including the rejection an
   editor hits when picking a file the server cannot handle. */

.staff-busy {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1200;
  background: linear-gradient(180deg, #31372f, #22261f);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 999px;
  padding: 10px 20px;
  font-size: 0.9rem;
  box-shadow: var(--shadow-lift);
}

.staff-toast {
  position: fixed;
  bottom: 70px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1200;
  max-width: min(560px, calc(100vw - 32px));
  background: #22261f;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 12px;
  padding: 12px 18px;
  font-size: 0.9rem;
  line-height: 1.4;
  box-shadow: var(--shadow-lift);
}

.staff-toast.is-error {
  border-color: #c0574a;
  background: linear-gradient(180deg, #3a2420, #241a17);
}

/* Responsive — from small phones up through 4K displays. Grid/flex layouts
   (card-grid, gallery-grid, header-inner) already reflow on their own via
   auto-fit/flex-wrap; these breakpoints tune spacing and type scale at the
   two ends of the range where that alone isn't enough. */

@media (max-width: 640px) {
  .container {
    padding: 0 14px;
  }

  .header-inner {
    justify-content: center;
    text-align: center;
  }

  .main-nav {
    justify-content: center;
    width: 100%;
  }

  section {
    padding: 32px 0;
  }

  .category-plate {
    padding: 16px;
    gap: 10px;
  }

  .category-plate span {
    font-size: 1.15rem;
  }

  .category-cta {
    bottom: 20px;
    padding: 8px 20px;
    font-size: 0.85rem;
  }

  .card-grid,
  .gallery-grid {
    gap: 14px;
  }

  .staff-toolbar {
    left: 16px;
    right: 16px;
    justify-content: center;
  }
}

@media (min-width: 2200px) {
  .category-plate span {
    font-size: 1.85rem;
  }
}

/* Sections taken off the public site
   ---------------------------------
   The hiding rule is scoped to visitors rather than overridden for staff on
   purpose: a plain `display:none` would need an override to put back, and the
   only safe value differs per section (the category grid is a grid, the rest
   are blocks). Staff simply never match this selector. */
body:not(.staff-view) [data-section-hidden] {
  display: none;
}

body.staff-view [data-section-hidden] {
  outline: 2px dashed var(--color-accent);
  outline-offset: 10px;
}

.section-visibility {
  display: none;
}

body.edit-mode .section-visibility {
  display: flex;
  justify-content: flex-end;
  padding: 10px 0 2px;
}

.section-visibility button {
  background: rgba(34, 38, 31, 0.85);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 999px;
  padding: 5px 14px;
  font-size: 0.82rem;
  cursor: pointer;
}

.section-visibility.is-hidden button {
  background: var(--color-accent);
  color: #2a2410;
  border-color: #b9862c;
  font-weight: 600;
}

/* Enlarged view for gallery media */

.lightbox {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1500;
  background: rgba(10, 16, 11, 0.92);
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 24px;
}

.lightbox.is-open {
  display: flex;
}

body.lightbox-open {
  overflow: hidden;
}

.lightbox-stage {
  margin: 0;
  max-width: min(1200px, 92vw);
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.lightbox-media img,
.lightbox-media video {
  max-width: min(1200px, 86vw);
  max-height: 82vh;
  width: auto;
  height: auto;
  border-radius: 10px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
}

.lightbox-caption {
  color: #e9f1e3;
  font-size: 0.95rem;
  text-align: center;
}

.lightbox-close,
.lightbox-nav {
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  cursor: pointer;
  line-height: 1;
}

.lightbox-close {
  position: absolute;
  top: 18px;
  right: 18px;
  width: 42px;
  height: 42px;
  font-size: 1.6rem;
}

.lightbox-nav {
  width: 48px;
  height: 48px;
  font-size: 2rem;
  flex-shrink: 0;
}

.lightbox-close:hover,
.lightbox-nav:hover {
  background: rgba(255, 255, 255, 0.2);
}

/* A photo in the gallery is now something you can open, so say so on hover —
   but not in edit mode, where the same click replaces the file. */
body:not(.edit-mode) .gallery-grid [data-field="image_url"] {
  cursor: zoom-in;
}

@media (max-width: 640px) {
  .lightbox {
    padding: 12px;
  }

  .lightbox-nav {
    width: 40px;
    height: 40px;
    font-size: 1.6rem;
  }
}

/* Sections the client added themselves. The delete control sits opposite the
   visibility control so the two never overlap at the top of the section. */
.custom-section {
  position: relative;
}

body.edit-mode .custom-section > .item-controls {
  position: absolute;
  top: 12px;
  left: 0;
  margin: 0;
}

/* Long-form copy: category descriptions and the body of a client-made section.
   Before this, text ran the full 1100px container — about 130 characters a
   line, roughly double what is comfortable to read, at a line height meant for
   short intros. The measure is capped and the rhythm loosened; on a dark
   background both matter more than on white. */
#category-description,
[data-block$="_body"],
[data-block$="_intro"] {
  max-width: 68ch;
  font-size: 1.05rem;
  line-height: 1.7;
}

#category-description > p,
[data-block$="_body"] > p,
[data-block$="_intro"] > p {
  margin: 0 0 14px;
}

#category-description h3,
[data-block$="_body"] h3 {
  margin: 30px 0 12px;
  font-size: 1.16rem;
  color: #eaf3e2;
}

#category-description ul,
[data-block$="_body"] ul {
  margin: 0 0 18px;
  padding-left: 1.2em;
}

#category-description li,
[data-block$="_body"] li {
  margin-bottom: 9px;
}

#category-description li::marker,
[data-block$="_body"] li::marker {
  color: var(--color-accent);
}

#category-description strong,
[data-block$="_body"] strong {
  color: #f2f8ec;
}

.category-detail h1 {
  margin: 4px 0 20px;
}

@media (max-width: 640px) {
  #category-description,
  [data-block$="_body"],
  [data-block$="_intro"] {
    font-size: 1rem;
  }
}


/* Using the width of a real screen
   -------------------------------
   The container was capped at 1100px until 2200px wide, so a 16:9 monitor spent
   about 40% of itself on empty margins. It now grows in steps. Text is a
   separate matter: a line stays readable at 65-75 characters no matter how wide
   the window is, so the extra room goes into layout — the photo moves beside
   the copy — rather than into longer lines. */

@media (min-width: 1400px) {
  .container {
    max-width: 1260px;
  }
}

@media (min-width: 1700px) {
  .container {
    max-width: 1440px;
  }
}

@media (min-width: 2200px) {
  .container {
    max-width: 1660px;
  }
}

/* The editor's per-section bar must never become a column of its own once a
   section is laid out as a grid. */
.section-visibility {
  grid-column: 1 / -1;
}

/* Category page: photo beside the text as soon as there is room for both. */
@media (min-width: 1000px) {
  .category-detail {
    display: grid;
    grid-template-columns: minmax(300px, 42%) minmax(0, 1fr);
    column-gap: clamp(32px, 3.5vw, 64px);
    row-gap: 0;
    align-items: start;
  }

  /* The photo is sticky inside a plain wrapper, not directly in the grid. A
     sticky grid item is supposed to be held inside its own grid area, but it
     measurably is not: the photo travelled 459px where the area allowed 395 and
     slid 64px over the cards below. Stretching an ordinary block to the row and
     sticking the photo inside it gives a containing block that does hold. */
  .category-hero-col {
    grid-column: 1;
    grid-row: 1;
    align-self: stretch;
  }

  .category-hero {
    position: sticky;
    top: 24px;
    /* comes to rest a little before the next block, not flush against it */
    margin-bottom: 32px;
  }

  .category-hero img,
  .category-hero video {
    max-height: clamp(420px, 56vh, 660px);
    margin-bottom: 0;
  }

  .category-body {
    grid-column: 2;
    grid-row: 1;
  }

  #category-name {
    margin-top: 0;
  }

  #category-description {
    max-width: 70ch;
  }
}

/* Contacts: the details and the form share the row instead of stacking with a
   half-empty page beside them. */
@media (min-width: 900px) {
  body[data-page="contacts"] > main {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(340px, 40%);
    column-gap: clamp(32px, 4vw, 72px);
    align-items: start;
  }

  body[data-page="contacts"] .contact-form {
    max-width: none;
  }
}

/* About: the photo sits in the text rather than above it. A float, not a grid,
   because the editor injects its own element into this section and a float does
   not care where in the order that lands. */
@media (min-width: 900px) {
  [data-section="about"] > .media-frame {
    float: left;
    width: 34%;
    margin: 6px clamp(24px, 3vw, 44px) 18px 0;
  }

  [data-section="about"]::after {
    content: "";
    display: block;
    clear: both;
  }
}

/* Gallery cards are <figure> elements, and the browser's default margin on
   those is 1em 40px — so every 220px grid cell was holding a 140px thumbnail
   with 80px of default margin around it, which read as a sparse grid rather
   than as a bug. */
.gallery-grid .item-card {
  margin: 0;
}

/* Category tiles ease in as the page loads. fill-mode is `backwards`, not
   `both`: with `both` the animation's final transform would keep applying and
   the hover lift on a tile would have nothing to move. Respects a reduced-motion
   preference, where the tiles simply appear. */
/* A tile waits until its own photo is there and then rises into place, one
   after another. Running it the moment the markup lands meant the motion
   happened on an empty card while the photo was still arriving — technically
   an animation, but nothing anyone would notice.
   The hidden state is scoped to :not(.is-revealed) on purpose: the animation
   fills backwards (so the hover lift still works afterwards), which means once
   it ends the tile falls back to whatever CSS says — and that must not be
   opacity: 0. */
.category-grid.tiles-staged .category-tile:not(.is-revealed) {
  opacity: 0;
}

.category-grid .category-tile.is-revealed {
  animation: tile-rise 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
}

@media (prefers-reduced-motion: reduce) {
  .category-grid.tiles-staged .category-tile:not(.is-revealed) {
    opacity: 1;
  }

  .category-grid .category-tile.is-revealed {
    animation: none;
  }
}

@keyframes tile-rise {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
}

/* "Other categories" at the foot of a category page */

.category-more {
  grid-column: 1 / -1;
  padding-top: 8px;
}

.category-more h2 {
  font-size: 1.15rem;
}

.category-more-grid {
  display: grid;
  /* auto-fill, not auto-fit: with only two categories left to show, auto-fit
     collapses the empty tracks and stretches the two cards across the whole
     row, where they compete with the page they are a footnote to. */
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 18px;
}

.category-more-card {
  display: block;
  border-radius: 14px;
  overflow: hidden;
  background: linear-gradient(180deg, #fbfdf8, #edf3e7);
  border: 1px solid #d3dfcc;
  box-shadow: var(--shadow-soft);
  transition: transform 0.16s ease, box-shadow 0.16s ease;
}

.category-more-card:hover {
  text-decoration: none;
  transform: translateY(-3px);
  box-shadow: var(--shadow-lift);
}

.category-more-card img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

.category-more-card span {
  display: block;
  padding: 11px 14px;
  color: #223c2a;
  font-weight: 600;
}

/* The hero on a category page is now an editable slot like any other. */
.category-hero .item-controls {
  position: absolute;
  left: 10px;
  bottom: 10px;
  margin: 0;
  z-index: 3;
}

/* A figure inside body copy — a diagram or photo that belongs to the text
   rather than to the page. Held to the reading column so it does not break the
   measure, with the caption reading as a caption and not as another paragraph. */
.prose-figure {
  margin: 22px 0 24px;
}

.prose-figure img,
.prose-figure video {
  width: 100%;
  border-radius: 12px;
  /* schematics are drawn on white; a light backing keeps them from floating on
     the dark page as a bright rectangle with no edge */
  background: #fdfdfb;
  box-shadow: var(--shadow-soft);
}

.prose-figure figcaption {
  margin-top: 9px;
  font-size: 0.92rem;
  color: #c2d3b8;
  text-align: center;
}

/* Окно согласия на сбор статистики.
   Садится в правый нижний угол, а не полосой во всю ширину: слева внизу живёт
   панель сотрудника, и полоса перекрывала бы её. Ниже просмотра фото по
   z-index, выше панели — чтобы не мешать ни тому, ни другому. */
.consent-bar {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 1400;
  max-width: min(440px, calc(100vw - 32px));
  padding: 16px 18px;
  background: linear-gradient(180deg, #31372f, #22261f);
  color: #e9f1e3;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 14px;
  box-shadow: var(--shadow-lift);
}

.consent-text {
  margin: 0 0 12px;
  font-size: 0.92rem;
  line-height: 1.5;
}

.consent-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}

.consent-actions .btn {
  padding: 8px 20px;
  font-size: 0.9rem;
}

.consent-decline {
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: #cfe3b4;
  border-radius: 999px;
  padding: 8px 16px;
  font-size: 0.9rem;
  cursor: pointer;
}

.consent-decline:hover {
  background: rgba(255, 255, 255, 0.08);
}

@media (max-width: 640px) {
  .consent-bar {
    left: 16px;
    right: 16px;
    max-width: none;
  }
}

/* Страницы ошибок */

.error-page {
  padding: 70px 20px 90px;
  text-align: center;
}

.error-code {
  margin: 0;
  font-size: clamp(4rem, 12vw, 8rem);
  font-weight: 700;
  line-height: 1;
  color: rgba(233, 241, 227, 0.16);
  letter-spacing: 0.04em;
}

.error-page h1 {
  margin: 6px 0 14px;
}

.error-text {
  max-width: 52ch;
  margin: 0 auto 26px;
  color: #cfe0c4;
}
