/*
Theme Name:   Blocksy Child — Overprint
Theme URI:    https://jeremymak.orgs.hk
Description:  CMYK overprint design system. Process colours, real multiply blending, misregistration effects.
Author:       Jeremy Mak
Template:     blocksy
Version:      1.0.0
Text Domain:  blocksy-child-overprint
*/

/* ==========================================================================
   0. 你可以自己改嘅設定  /  USER SETTINGS

   呢一段係全部你會想調嘅數值。改完存檔，瀏覽器 Cmd+Shift+R 就見到。
   唔好改呢段以外嘅嘢，除非你知道自己做緊乜。
   ========================================================================== */

:root {
  /* --- 間距 ------------------------------------------------------------
     數字細啲 = 元素之間貼近啲。
     --gap-hero-bottom 控制 hero 圖同下面內容嘅距離。   */
  --gap-section:     4rem;    /* 每個區塊上下留白 */
  --gap-hero-bottom: 2.5rem;  /* hero 圖之後嘅留白 */

  /* --- Hero 高度 -------------------------------------------------------
     78vh = 佔螢幕高度 78%。想矮啲改 60vh，想滿屏改 100vh。 */
  --hero-height: 78vh;

  /* --- 篩選列 ----------------------------------------------------------
     --filter-align: center 置中 / flex-start 靠左 / flex-end 靠右
     --filter-size:  字級。1 = 標準，1.3 = 大 30%
     --filter-gap:   每個分類之間嘅距離                         */
  --filter-align: center;
  --filter-size:  1;
  --filter-gap:   2rem;

  /* --- 色版錯位幅度 ----------------------------------------------------
     hover 落圖片時三塊色版飄開幾遠。大啲 = 效果誇張啲。 */
  --misreg: 6px;

  /* 入場動畫。0 = 完全靜止 */
  --reveal-distance: 24px;
  --reveal-duration: 0.7s;

  /* 內容最大闊度 */
  --wrap-w: 1440px;

  /* 字體。喺 Customizer 揀 */
  --font-family: 'Archivo', -apple-system, BlinkMacSystemFont, sans-serif;

  /* --- 作品網格 ---------------------------------------------------------
     --grid-cols:  桌面欄數。參考站 v4 = 4 欄
     --card-gap:   卡片之間嘅距離
     --card-align: 標題同分類嘅對齊。center 置中 / left 靠左        */
  --grid-cols:  4;
  --card-gap:   1.5rem;
  --card-align: center;

  /* 卡片框嘅比例。無論原圖係咩比例都會裁成呢啲形狀。
     每個 project 可以喺 Project Details 揀邊個形狀，
     混合唔同形狀就會造成 masonry 錯落效果。 */
  --ratio-tall: 2 / 3;
  --ratio-sq:   1 / 1;
  --ratio-wide: 4 / 3;
  --card-focus: center;   /* 裁切重心。可改 top / bottom / "50% 30%" */

  /* --- Slider / Hero 文字位置 ------------------------------------------
     --slide-v: center 上下置中 / end 貼底 / start 貼頂
     --slide-h: start 靠左 / center 置中 / end 靠右
     --slide-w: 文字區最闊幾多（避免標題一行拉太長）           */
  --slide-v: center;
  --slide-h: start;
  --slide-w: 16ch;

  /* --- Header 疊喺 hero 上面 -------------------------------------------
     --header-ink: header 文字同 logo 嘅顏色。深色 hero 用 var(--paper)，
                   淺色 hero 用 var(--ink)。
     唔想 header 疊住，將 --header-overlay 由 1 改做 0。      */
  --header-overlay: 1;

  /* --- Project 標題帶 ---------------------------------------------------
     --titleband-h:    高度。參考站係 240px
     --titleband-veil: 蓋喺圖上面嘅色罩。淺色罩配黑字，深色罩配白字
     --titleband-ink:  標題顏色                                     */
  --titleband-h:    240px;
  --titleband-veil: rgba(244, 242, 237, 0.72);
  --titleband-ink:  var(--ink);

  /* --- Header 外觀 ------------------------------------------------------
     --header-ink:   文字同 logo 顏色（hero 上面嗰陣）
     --header-size:  選單字級。1 = 標準，1.3 = 大 30%
     --header-track: 字距
     --header-pad:   header 上下高度                              */
  --header-ink:       var(--ink);
  --header-size:      1;
  --header-track:     0.14em;
  --header-bg:        #ffffff;  /* 黏住時嘅底色。刻意同 --paper 唔同色，
                                   否則同背景撞色，睇唔出有變化 */
  --header-rule:      rgba(17, 17, 17, 0.12);  /* 下邊線 */
  --header-shadow:    0 1px 12px rgba(17, 17, 17, 0.06);

  --header-pad:       1.6rem;   /* 頂部時上下留白 → 約 100px 高，同參考站一致 */
  --header-pad-stuck: 0.55rem;  /* 黏住時 → 約 60px 高 */
  --logo-h:           48px;
  --logo-h-stuck:     34px;
}


/* ==========================================================================
   1. DESIGN TOKENS
   Change values here — everything else inherits.

   Restored after a deduplication pass removed this block by mistake: three
   :root blocks existed, two of them held these tokens, and the pass kept the
   one that did not. The check that followed compared variable counts rather
   than variable names, so it reported success. It now compares names.
   ========================================================================== */

:root {

  /* --- Process colours ------------------------------------------------ */
  --c: #00AEEF;
  --m: #EC008C;
  --y: #FFF200;
  --k: #111111;

  --ink:        #111111;
  --paper:      #F4F2ED;
  --paper-warm: #E9E6DF;
  --bg:         var(--paper);
  --accent:     var(--m);

  /* --- Type sizes at full scale --------------------------------------- */
  --size--1: 0.8125rem;
  --size-0:  1rem;
  --size-1:  1.25rem;
  --size-2:  1.5rem;
  --size-3:  2rem;
  --size-4:  2.75rem;
  --size-5:  3.75rem;
  --size-6:  4.8125rem;

  /* Coefficients, overridden per breakpoint in section 1b */
  --coef-title:  1;
  --coef-text:   1;
  --coef-button: 1;

  /* Resolved steps. Headings follow the title curve, body the text curve. */
  --step--1: calc(var(--size--1) * var(--coef-text));
  --step-0:  calc(var(--size-0)  * var(--coef-text));
  --step-1:  calc(var(--size-1)  * var(--coef-text));
  --step-2:  calc(var(--size-2)  * var(--coef-title));
  --step-3:  calc(var(--size-3)  * var(--coef-title));
  --step-4:  calc(var(--size-4)  * var(--coef-title));
  --step-5:  calc(var(--size-5)  * var(--coef-title));
  --step-6:  calc(var(--size-6)  * var(--coef-title));

  --track-head:  -0.035em;
  --weight-head: 800;

  /* --- Spacing --------------------------------------------------------- */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1.5rem;
  --sp-5: 2.5rem;
  --sp-6: 4rem;
  --sp-7: 6rem;

  /* --- Motion ---------------------------------------------------------- */
  --dur-fast: 180ms;
  --dur-med:  420ms;
  --dur-slow: 900ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
}




/* ==========================================================================
   1b. BREAKPOINT COEFFICIENTS

   Seven steps, mirroring the reference theme's responsive coefficient
   lists. Titles shrink hard; body text has a floor so it never drops
   below a readable size; buttons follow their own curve, which is not
   monotonic — that is deliberate in the original and kept here.
   ========================================================================== */

/* 1: above 1600 — base, coefficients all 1 */

@media (max-width: 1600px) {
  :root { --coef-title: 1; --coef-text: 1; --coef-button: 1; }
}

/* Title coefficients are overridden by the Customizer output, which is
   printed after this file. Text and button curves stay here because they
   need a floor for readability rather than a free choice. */

@media (max-width: 1400px) {
  :root { --coef-title: 0.8; --coef-text: 0.94; --coef-button: 0.8; }
}

@media (max-width: 1200px) {
  :root { --coef-text: 0.94; --coef-button: 0.9; }
}

@media (max-width: 1000px) {
  :root { --coef-title: 0.6; --coef-text: 0.9; --coef-button: 0.8; }
}

@media (max-width: 768px) {
  :root { --coef-title: 0.45; --coef-text: 0.9; --coef-button: 0.7; }
}

@media (max-width: 600px) {
  :root { --coef-text: 0.88; --coef-button: 0.6; }
}

/* Buttons and technical labels track their own coefficient */
.op-slide__btn,
.op-label {
  font-size: calc(var(--size--1) * var(--coef-button));
}

/* ==========================================================================
   2. BASE
   ========================================================================== */

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-family);
  font-size: var(--step-0);
  font-variation-settings: 'wght' 400;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4,
.op-display {
  font-family: var(--font-family);
  font-variation-settings: 'wght' 800;
  letter-spacing: -0.035em;
  line-height: 0.92;
  text-transform: uppercase;
  margin: 0 0 var(--sp-4);
}

h1 { font-size: var(--step-6); }
h2 { font-size: var(--step-4); }
h3 { font-size: var(--step-2); }

/* Technical labels — the small uppercase text that carries the print voice */
.op-label {
  font-size: var(--step--1);
  font-variation-settings: 'wght' 600;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  line-height: 1;
}

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

/* Link underline that fills like ink */
.entry-content a,
.op-link {
  background-image: linear-gradient(var(--m), var(--m));
  background-repeat: no-repeat;
  background-size: 100% 1px;
  background-position: 0 100%;
  transition: background-size var(--dur-med) var(--ease-out);
}
.entry-content a:hover,
.op-link:hover { background-size: 100% 40%; }

/* ==========================================================================
   3. PLATE REGISTRATION — the signature effect
   Three process plates stacked, multiply-blended, offset on hover.
   ========================================================================== */

.op-plate {
  position: relative;
  display: block;
  overflow: hidden;
  background: #fff;
  isolation: isolate;      /* keeps multiply contained */
  aspect-ratio: 4 / 3;
}

.op-plate img,
.op-plate .op-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  will-change: transform;
}

/* Each plate carries one process colour, multiplied over the others */
.op-plate .op-layer { mix-blend-mode: multiply; }
.op-plate .op-layer--c { filter: url(#op-sep-c); }
.op-plate .op-layer--m { filter: url(#op-sep-m); }
.op-plate .op-layer--y { filter: url(#op-sep-y); }

/* Touch devices: no hover state, so plates sit registered and meta shows */
@media (pointer: coarse) {
  .op-card__meta { opacity: 1 !important; transform: none !important; }
}

/* ==========================================================================
   4. PROJECT CARD
   ========================================================================== */

.op-card { position: relative; display: block; }

.op-card__meta {
  padding-top: var(--sp-3);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-3);
}

.op-card__title {
  font-size: var(--step-1);
  font-variation-settings: 'wght' 800;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  margin: 0;
}

.op-card__year {
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  opacity: 0.5;
}

.op-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
  gap: var(--sp-5) var(--sp-4);
}

/* ==========================================================================
   6. SCROLL REVEALS — native CSS, no JS
   ========================================================================== */

@supports (animation-timeline: view()) {
  [data-anim="fade-up"] {
    animation: op-fade-up linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 60%;
  }
}

@keyframes op-fade-up {
  from { opacity: 0; transform: translateY(2.5rem); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Split-line reveal target state, set up by JS */
.op-line { display: block; overflow: hidden; }
.op-line > span { display: block; will-change: transform; }

/* ==========================================================================
   7. LAYOUT HELPERS
   ========================================================================== */

/* The header lives in the parent theme's container, everything below lives
   in this one. Two container systems with different widths and different
   side padding do not line up, which is why a heading can sit thirty pixels
   off the logo above it.
 *
 * "Match" reads the parent theme's own container variables where they exist,
 * so the two systems agree by construction rather than by a number found
 * through trial. Where they do not exist the theme's own values apply. */

.op-wrap {
  width: min(100% - var(--wrap-pad, var(--sp-4)) * 2, var(--wrap-w, 1440px));
  margin-inline: auto;
}

/* This once read the parent theme's own container variables by name. The
   names were guessed, none of them existed, and an undefined variable inside
   min() takes the whole calculation down — the container computed to zero and
   every page collapsed to a column.
 *
 * Alignment is now done with values this theme actually owns. Set the width
 * and side padding to match the header and the two line up; opAlign() in the
 * console reports the remaining difference so it is measured, not guessed. */

body.op-wrap-match .op-wrap {
  width: min(100% - var(--wrap-pad, var(--sp-4)) * 2, var(--wrap-w, 1440px));
}

.op-section { padding-block: var(--gap-section); }

.op-bleed { width: 100vw; margin-inline: calc(50% - 50vw); }

/* ==========================================================================
   8. ACCESSIBILITY
   ========================================================================== */

/* Reduce-motion rules. Applied through a class rather than a media
   query so the Customizer can override the preference. */
html.op-reduced *,
html.op-reduced *::before,
html.op-reduced *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

html.op-reduced [data-anim] { opacity: 1 !important; transform: none !important; }


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

/* ==========================================================================
   9. HERO
   ========================================================================== */

.op-hero {
  position: relative;
  min-height: var(--hero-height);
  margin-bottom: var(--gap-hero-bottom);
  display: grid;
  align-items: end;
  overflow: hidden;
}

.op-hero__media,
.op-slide__media {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  /* Headroom for the upward travel. Tied to the same ratio the JS uses,
     so changing one does not expose the container edge. */
  height: calc(100% + (100% * var(--hero-parallax, 0.35)));
  aspect-ratio: auto;
}

.op-hero__media img,
.op-slide__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--hero-x, 50%) var(--hero-y, 50%);
  display: block;
}

.op-hero__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(17, 17, 17, calc(var(--hero-veil, 55) / 100)), transparent 60%);
  pointer-events: none;
  z-index: 2;
}

.op-hero {
  align-items: var(--slide-v);
}

.op-hero__body {
  position: relative;
  z-index: 3;
  display: grid;
  justify-items: var(--slide-h);
  text-align: left;
  padding-block: var(--sp-6);
  color: var(--slide-ink, var(--paper));
}

.op-hero:not(:has(.op-hero__media)) .op-hero__body { color: var(--ink); }

.op-hero__title {
  font-size: calc(var(--step-6) * var(--slide-title-scale, 1));
  max-width: var(--slide-w);
  margin-bottom: var(--sp-3);
}

.op-hero__sub {
  font-size: calc(var(--step--1) * var(--slide-sub-scale, 1));
  max-width: 48ch;
  margin-bottom: var(--sp-5);
  opacity: 0.85;
}

.op-hero__cue {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}

.op-hero__arrow {
  width: 1.6em;
  height: 1px;
  background: currentColor;
  transition: width var(--dur-med) var(--ease-out);
}
.op-hero__cue:hover .op-hero__arrow { width: 3em; }

/* ==========================================================================
   10. FILTER BAR
   ========================================================================== */

.op-filter {
  display: flex;
  flex-wrap: wrap;
  justify-content: var(--filter-align);
  gap: var(--filter-gap);
  margin-bottom: var(--sp-5);
}

.op-filter__btn {
  font-size: calc(var(--size--1) * var(--coef-button) * var(--filter-size));
  background: none;
  border: 0;
  padding: 0.35em 0;
  cursor: pointer;
  color: var(--ink);
  opacity: 0.45;
  position: relative;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.op-filter__btn::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 0; height: 3px;
  background: var(--m);
  transition: width var(--dur-med) var(--ease-out);
}

.op-filter__btn:hover { opacity: 0.8; }
.op-filter__btn.is-active { opacity: 1; }
.op-filter__btn.is-active::after { width: 100%; }

/* ==========================================================================
   11. MASONRY GRID

   Default state is a plain grid: every card sits in its own row. Safe, and
   it is what you get if JavaScript never runs.

   Once measured, JS adds .is-masonry and gives each card a row span based
   on its real rendered height. Row gap must be zero in that mode — a span
   of 50 rows would otherwise collect 49 gaps, which is what produced the
   enormous vertical holes.
   ========================================================================== */

.op-grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
  column-gap: var(--card-gap);
  row-gap: var(--card-gap);
  align-items: start;
}

.op-grid.is-masonry {
  grid-auto-rows: 4px;   /* measurement unit, not a card height */
  row-gap: 0;            /* spacing comes from each card's margin instead */
}

/* The desktop column count comes from the Customizer; these steps keep the
   grid sane on narrower screens whatever that number is. */
@media (max-width: 1400px) { :root { --grid-cols: 3 !important; } }
@media (max-width: 1000px) { :root { --grid-cols: 2 !important; } }
@media (max-width: 600px)  { :root { --grid-cols: 1 !important; } }

/* Fixed frames. The image is cropped to fill them, so an upload's own
   proportions never leak into the layout. */
.op-card--tall .op-plate { aspect-ratio: var(--ratio-tall); }
.op-card--sq   .op-plate { aspect-ratio: var(--ratio-sq); }
.op-card--wide .op-plate { aspect-ratio: var(--ratio-wide); }

.op-card .op-plate img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--card-focus, center);
}

.op-grid.is-masonry .op-card { margin-bottom: var(--card-gap); }
.op-card.is-hidden { display: none; }

/* Plus mark over the plate on hover */
.op-card__mark {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-out);
  pointer-events: none;
}
.op-card__mark span { position: absolute; background: var(--paper); }
.op-card__mark span:nth-child(1) { width: 30px; height: 2px; }
.op-card__mark span:nth-child(2) { width: 2px;  height: 30px; }
.op-card__link:hover .op-card__mark { opacity: 1; }

/* Title and category under the image */
.op-card__meta {
  padding-top: var(--sp-3);
  text-align: var(--card-align);
  display: grid;
  gap: 2px;
}

.op-card__title {
  font-size: var(--step-1);
  font-variation-settings: 'wght' 800;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0;
  transition: color var(--dur-fast) var(--ease-out);
}
.op-card__link:hover .op-card__title { color: var(--accent, var(--m)); }

.op-card__cats {
  margin: 0;
  font-size: calc(var(--size--1) * var(--coef-button));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.4;
}

.op-more {
  margin: var(--gap-section) 0 0;
  text-align: center;
}

/* ==========================================================================
   12. CARD SHADER
   ========================================================================== */

.op-card__shader {
  position: absolute;
  inset: 0;
  z-index: 4;
  background: var(--accent, var(--m));
  opacity: 0;
  mix-blend-mode: multiply;
  transition: opacity var(--dur-med) var(--ease-out);
  pointer-events: none;
}
.op-card__link:hover .op-card__shader { opacity: 0.22; }

@media (pointer: coarse) {
  .op-card__shader { opacity: 0 !important; }
  .op-card__mark   { opacity: 0.9; }
}

/* ==========================================================================
   13. GALLERY MODES
   Restyles native Gallery blocks based on the project's layout setting.
   ========================================================================== */

.op-prose { max-width: 100%; }
.op-prose p { max-width: 68ch; }

.op-prose .wp-block-gallery {
  margin-block: var(--sp-6);
  gap: var(--sp-3) !important;
}

/* grid — even columns */
.op-gal--grid .wp-block-gallery {
  display: grid !important;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
}
.op-gal--grid .wp-block-gallery figure { margin: 0; }

/* masonry — mixed heights via columns */
.op-gal--masonry .wp-block-gallery {
  display: block !important;
  columns: 2;
  column-gap: var(--sp-3);
}
@media (min-width: 900px) { .op-gal--masonry .wp-block-gallery { columns: 3; } }
.op-gal--masonry .wp-block-gallery figure {
  break-inside: avoid;
  margin: 0 0 var(--sp-3);
  width: 100% !important;
}

/* carousel — full bleed horizontal scroll */
.op-gal--carousel .wp-block-gallery {
  display: flex !important;
  flex-wrap: nowrap;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding-inline: var(--sp-4);
  scrollbar-width: thin;
}
.op-gal--carousel .wp-block-gallery figure {
  flex: 0 0 min(78vw, 640px);
  scroll-snap-align: center;
  margin: 0;
}

/* side — pairs */
.op-gal--side .wp-block-gallery {
  display: grid !important;
  grid-template-columns: 1fr;
}
@media (min-width: 700px) {
  .op-gal--side .wp-block-gallery { grid-template-columns: 1fr 1fr; }
}
.op-gal--side .wp-block-gallery figure { margin: 0; }

/* single — one large image per row */
.op-gal--single .wp-block-gallery {
  display: grid !important;
  grid-template-columns: 1fr;
  gap: var(--sp-5) !important;
}
.op-gal--single .wp-block-gallery figure { margin: 0; }
.op-gal--single .wp-block-gallery img { width: 100%; }

/* ==========================================================================
   15. ARCHIVE
   ========================================================================== */

.op-archive__title { font-size: var(--step-5); margin: 0; }

/* ==========================================================================
   16. MOBILE OVERRIDES
   ========================================================================== */

@media (max-width: 767px) {

  .op-hero { min-height: var(--hero-height-m, 70vh); }

  /* Horizontal carousels become stacks */
  .op-gal--carousel .wp-block-gallery {
    display: grid !important;
    grid-template-columns: 1fr;
    width: auto;
    margin-inline: 0;
    padding-inline: 0;
    overflow: visible;
  }
  .op-gal--carousel .wp-block-gallery figure { flex: none; }

  .op-pager { flex-direction: column; }
  .op-pager__link--next { text-align: left; margin-left: 0; }
}

/* ==========================================================================
   17. MOTION SUPPORT
   ========================================================================== */

/* Line masking for heading reveals */
.op-line {
  display: block;
  overflow: hidden;
}
.op-line > span {
  display: block;
  will-change: transform;
}

/* Elements JS will animate start hidden, but only when JS is present.
   The .op-js class is set by an inline script so no-JS never hides content. */
.op-js .op-card,
.op-js .op-prose p,
.op-js .op-prose li,
.op-js .op-prose figure { will-change: transform, opacity; }

/* ==========================================================================
   18. PAGE TRANSITION VEIL
   ========================================================================== */

.op-veil {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: var(--paper);
  opacity: 1;
  display: grid;
  place-items: center;
}

/* Registration cross, spinning slowly while the page swaps */
.op-veil__mark {
  position: relative;
  width: 44px;
  height: 44px;
  animation: op-spin 2.4s linear infinite;
}
.op-veil__mark span {
  position: absolute;
  top: 50%; left: 50%;
  background: var(--ink);
  transform: translate(-50%, -50%);
}
.op-veil__mark span:nth-child(1) { width: 44px; height: 1.5px; }
.op-veil__mark span:nth-child(2) { width: 1.5px; height: 44px; }

.op-veil__mark::after {
  content: '';
  position: absolute;
  inset: 11px;
  border: 1.5px solid var(--m);
  border-radius: 50%;
}

@keyframes op-spin {
  to { transform: rotate(360deg); }
}

/* ==========================================================================
   19. BACK TO TOP
   ========================================================================== */

.op-totop {
  position: fixed;
  right: var(--sp-4);
  bottom: var(--sp-4);
  z-index: 900;
  width: 46px;
  height: 46px;
  border: 2px solid var(--ink);
  background: var(--paper);
  cursor: pointer;
  display: grid;
  place-items: center;
  opacity: 0;
  transform: translateY(14px);
  pointer-events: none;
  transition: opacity var(--dur-med) var(--ease-out),
              transform var(--dur-med) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}

.op-totop.is-on {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.op-totop:hover { background: var(--m); }

.op-totop span {
  width: 10px;
  height: 10px;
  border-left: 2px solid var(--ink);
  border-top: 2px solid var(--ink);
  transform: rotate(45deg) translate(2px, 2px);
}
.op-totop:hover span { border-color: var(--paper); }

/* ==========================================================================
   21. REDUCED MOTION — everything above stands down
   ========================================================================== */

/* Reduce-motion rules. Applied through a class rather than a media
   query so the Customizer can override the preference. */
html.op-reduced .op-veil { display: none; }
html.op-reduced .op-veil__mark { animation: none; }
html.op-reduced .op-totop { transition: none; }
html.op-reduced .op-line,
html.op-reduced .op-line > span { overflow: visible; transform: none !important; }


/* ==========================================================================
   22. LIGHTBOX  (replaces prettyPhoto)
   ========================================================================== */

.op-zoomable { cursor: zoom-in; }

.op-lb {
  position: fixed;
  inset: 0;
  z-index: 9998;
  display: none;
  place-items: center;
  background: rgba(17, 17, 17, 0.94);
  padding: var(--sp-5) var(--sp-4);
}

.op-lb.is-open { display: grid; }
body.op-lb-open { overflow: hidden; }

.op-lb__stage {
  margin: 0;
  display: grid;
  gap: var(--sp-3);
  justify-items: center;
  max-width: min(92vw, 1400px);
}

.op-lb__img {
  max-width: 100%;
  max-height: 78vh;
  width: auto;
  height: auto;
  display: block;
  background: #fff;
}

.op-lb__cap {
  color: var(--paper);
  opacity: 0.6;
  text-align: center;
  max-width: 60ch;
}

.op-lb__count {
  position: absolute;
  left: var(--sp-4);
  bottom: var(--sp-4);
  color: var(--paper);
  opacity: 0.5;
}

/* Close — a registration cross rotated 45deg */
.op-lb__close {
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  width: 40px;
  height: 40px;
  background: none;
  border: 0;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.op-lb__close span {
  position: absolute;
  width: 26px;
  height: 2px;
  background: var(--paper);
  transition: background var(--dur-fast) var(--ease-out);
}
.op-lb__close span:nth-child(1) { transform: rotate(45deg); }
.op-lb__close span:nth-child(2) { transform: rotate(-45deg); }
.op-lb__close:hover span { background: var(--m); }

/* Prev / next */
.op-lb__nav {
  position: absolute;
  top: 50%;
  width: 52px;
  height: 52px;
  transform: translateY(-50%);
  background: none;
  border: 0;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.op-lb__nav--prev { left: var(--sp-3); }
.op-lb__nav--next { right: var(--sp-3); }

.op-lb__nav span {
  width: 14px;
  height: 14px;
  border-left: 2px solid var(--paper);
  border-bottom: 2px solid var(--paper);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.op-lb__nav--prev span { transform: rotate(45deg); }
.op-lb__nav--next span { transform: rotate(-135deg); }
.op-lb__nav:hover span { border-color: var(--m); }

@media (max-width: 767px) {
  .op-lb__nav { width: 40px; }
  .op-lb__nav--prev { left: 0; }
  .op-lb__nav--next { right: 0; }
  .op-lb__img { max-height: 68vh; }
}

/* ==========================================================================
   23. STICKY PROJECT INFO
   ========================================================================== */

@media (min-width: 900px) {
  .op-single__meta.is-sticky {
    position: sticky;
    top: var(--sp-6);
  }
}

/* ==========================================================================
   24. SECTION PARALLAX
   ========================================================================== */

[data-parallax] {
  position: relative;
  overflow: hidden;
}

[data-parallax-target] {
  will-change: transform;
}

/* Give the target extra height so the drift never exposes an edge */
[data-parallax] > [data-parallax-target] {
  position: absolute;
  inset: -12% 0;
  width: 100%;
  height: 124%;
  object-fit: cover;
}

/* ==========================================================================
   25. HERO SLIDER
   ========================================================================== */

.op-slider {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  background: var(--ink);
}

.op-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 900ms var(--ease-out), visibility 0s linear 900ms;
}

.op-slide.is-on {
  opacity: 1;
  visibility: visible;
  transition: opacity 900ms var(--ease-out), visibility 0s;
  z-index: 2;
}

.op-slide__media {
  position: absolute;
  inset: 0;
}
.op-slide__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.op-slide__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(17, 17, 17, calc(var(--hero-veil, 55) / 100)), transparent 60%);
}

/* --- Caption ---------------------------------------------------------
   The rise holds the element invisible for the first half of its travel,
   then brings it in. That hesitation is what gives the motion weight.
   -------------------------------------------------------------------- */

.op-slide__body {
  position: relative;
  z-index: 3;
  height: 100%;
  display: grid;
  align-content: var(--slide-v);
  justify-items: var(--slide-h);
  text-align: left;
  padding-block: var(--sp-6);
  color: var(--slide-ink, var(--paper));
}

.op-slide__line {
  position: relative;
  top: 70px;
  opacity: 0;
}

.op-slide__line.is-in {
  animation: op-rise 1.2s cubic-bezier(0.165, 0.840, 0.440, 1) both;
}

@keyframes op-rise {
  0%   { top: 70px; opacity: 0; }
  50%  { top: 45px; opacity: 0; }
  100% { top: 0;    opacity: 1; }
}

.op-slide__sub {
  font-size: calc(var(--step--1) * var(--slide-sub-scale, 1));
  text-transform: uppercase;
  letter-spacing: 0.18em;
  margin-bottom: var(--sp-3);
  opacity: 0.75;
}

.op-slide__title {
  font-size: calc(var(--step-6) * var(--slide-title-scale, 1));
  color: var(--slide-ink, var(--paper));
  margin: 0 0 var(--sp-4);
  max-width: var(--slide-w);
}

.op-slide__rule {
  width: 90px;
  height: 3px;
  background: var(--m);
  margin-bottom: var(--sp-4);
}

.op-slide__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  border: 2px solid currentColor;
  padding: 0.75em 1.5em;
  font-size: calc(var(--step--1) * var(--slide-btn-scale, 1));
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-variation-settings: 'wght' 600;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
  justify-self: start;
}
.op-slide__btn:hover {
  background: var(--m);
  border-color: var(--m);
  color: var(--paper);
}

/* --- Dots ------------------------------------------------------------ */

.op-slider__dots {
  position: absolute;
  z-index: 5;
  bottom: var(--sp-4);
  right: var(--sp-4);
  display: flex;
  gap: var(--sp-2);
}

.op-slider__dots button {
  width: 26px;
  height: 3px;
  border: 0;
  padding: 0;
  cursor: pointer;
  background: rgba(244, 242, 237, 0.35);
  transition: background var(--dur-fast) var(--ease-out);
}
.op-slider__dots button.is-on { background: var(--m); }

/* --- Arrows with slide numbers --------------------------------------- */

.op-slider__arrow {
  position: absolute;
  z-index: 5;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: 0;
  cursor: pointer;
  color: var(--paper);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-3);
}
.op-slider__arrow--prev { left: var(--sp-3); }
.op-slider__arrow--next { right: var(--sp-3); }

.op-slider__num {
  font-size: var(--step--1);
  letter-spacing: 0.1em;
  font-variant-numeric: tabular-nums;
  opacity: 0;
  transform: translateX(8px);
  transition: opacity var(--dur-med) var(--ease-out),
              transform var(--dur-med) var(--ease-out);
}
.op-slider__arrow--prev .op-slider__num { transform: translateX(-8px); }
.op-slider__arrow:hover .op-slider__num { opacity: 0.7; transform: translateX(0); }

.op-slider__chev {
  width: 12px;
  height: 12px;
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transition: border-color var(--dur-fast) var(--ease-out);
}
.op-slider__arrow--prev .op-slider__chev { transform: rotate(45deg); }
.op-slider__arrow--next .op-slider__chev { transform: rotate(-135deg); }
.op-slider__arrow:hover .op-slider__chev { border-color: var(--m); }

@media (max-width: 767px) {
  .op-slider__arrow { display: none; }
  .op-slide__title { font-size: var(--step-4); }
  .op-slide__body { padding-block: var(--sp-6); }
}

/* Reduce-motion rules. Applied through a class rather than a media
   query so the Customizer can override the preference. */
html.op-reduced .op-slide { transition: opacity 0.2s linear; }
html.op-reduced .op-slide__line { top: 0; opacity: 1; animation: none !important; }


/* ==========================================================================
   26. COUNTERS
   ========================================================================== */

.op-count {
  font-size: var(--step-5);
  font-variation-settings: 'wght' 800;
  letter-spacing: -0.04em;
  line-height: 1;
  display: block;
  font-variant-numeric: tabular-nums;
}

.op-stat {
  display: grid;
  gap: var(--sp-2);
  border-top: 2px solid var(--ink);
  padding-top: var(--sp-3);
}

.op-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
  gap: var(--sp-5) var(--sp-4);
  margin-block: var(--sp-6);
}

/* ==========================================================================
   27. VIDEO BACKGROUND
   ========================================================================== */

.op-videobg {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.op-videobg video,
.op-videobg__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.op-videobg video { z-index: 2; }
.op-videobg__poster { z-index: 1; }

/* Reduce-motion rules. Applied through a class rather than a media
   query so the Customizer can override the preference. */
html.op-reduced .op-slide { transition: opacity 0.2s linear; transform: none; }
html.op-reduced .op-slide.is-on { transform: none; }


/* ==========================================================================
   28. SCROLL APPEAR VARIANTS
   Bridge exposes four directions as classes on any element. Same idea:
   data-from="left|right|top|bottom|fade" on anything.
   ========================================================================== */

.op-js [data-from] { opacity: 0; }

.op-js [data-from].is-in {
  animation: op-from-bottom 0.9s cubic-bezier(0.165, 0.840, 0.440, 1) both;
}
.op-js [data-from="left"].is-in  { animation-name: op-from-left; }
.op-js [data-from="right"].is-in { animation-name: op-from-right; }
.op-js [data-from="top"].is-in   { animation-name: op-from-top; }
.op-js [data-from="fade"].is-in  { animation-name: op-fade-in; }

@keyframes op-from-bottom {
  from { opacity: 0; transform: translateY(50px); }
  to   { opacity: 1; transform: none; }
}
@keyframes op-from-top {
  from { opacity: 0; transform: translateY(-50px); }
  to   { opacity: 1; transform: none; }
}
@keyframes op-from-left {
  from { opacity: 0; transform: translateX(-60px); }
  to   { opacity: 1; transform: none; }
}
@keyframes op-from-right {
  from { opacity: 0; transform: translateX(60px); }
  to   { opacity: 1; transform: none; }
}
@keyframes op-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Reduce-motion rules. Applied through a class rather than a media
   query so the Customizer can override the preference. */
html.op-reduced .op-js [data-from] { opacity: 1 !important; animation: none !important; }



.op-filter-hint {
  padding: var(--sp-3);
  border: 2px dashed var(--ink);
  opacity: 0.55;
  margin-bottom: var(--sp-5);
}

/* Cards being repositioned by FLIP must not fight the transform */
.op-card { will-change: transform, opacity; }

/* ==========================================================================
   30. HEADER — fixed, transparent at the top, solid once scrolled

   The background is painted by a ::before layer rather than on the header
   itself. Blocksy paints its own backgrounds on the inner rows, and any
   colour set on the header element gets covered by them. A pseudo layer
   sits behind everything and cannot be overridden.

   Sizes follow the reference: 140px tall at rest, 60px once stuck, with a
   1px bottom rule.
   ========================================================================== */

body.op-head-custom .ct-header:not(.ct-panel) ,
body.op-head-custom header#header ,
body.op-head-custom header.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  background: transparent !important;
  transition: transform 380ms var(--ease-out);
}

body.op-head-custom .ct-header:not(.ct-panel)::before ,
body.op-head-custom header#header::before ,
body.op-head-custom header.site-header::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--header-bg);
  border-bottom: 1px solid var(--header-rule);
  box-shadow: var(--header-shadow);
  opacity: 0;
  transition: opacity 200ms ease;   /* .2s ease, same as the reference */
  pointer-events: none;
}

body.op-head-custom .ct-header:not(.ct-panel).op-header--solid::before ,
body.op-head-custom header#header.op-header--solid::before ,
body.op-head-custom header.site-header.op-header--solid::before { opacity: 1; }

/* Blocksy paints backgrounds on inner rows; clear them so the layer shows */
body.op-head-custom .ct-header:not(.ct-panel) [data-row] ,
body.op-head-custom .ct-header:not(.ct-panel) [data-column] ,
body.op-head-custom .ct-header:not(.ct-panel) .ct-container ,
body.op-head-custom .ct-header:not(.ct-panel) .ct-container-fluid ,
body.op-head-custom .ct-header:not(.ct-panel) [data-row] > * {
  background-color: transparent !important;
  background-image: none !important;
  position: relative;
  z-index: 1;
}

/* Neutralise Blocksy's own sticky transform — two scripts moving the same
   element is what makes it judder */
body.op-head-custom .ct-header:not(.ct-panel)[data-sticky] ,
body.op-head-custom .ct-header:not(.ct-panel).ct-sticky-container ,
body.op-head-custom [data-sticky] > .ct-header:not(.ct-panel) {
  transform: none !important;
  animation: none !important;
}

/* Ink colour over the hero */
body.op-head-custom .ct-header:not(.ct-panel) a ,
body.op-head-custom .ct-header:not(.ct-panel) .ct-menu-link ,
body.op-head-custom .ct-header:not(.ct-panel) button ,
body.op-head-custom .ct-header:not(.ct-panel) .site-title ,
body.op-head-custom .ct-header:not(.ct-panel) .site-title a { color: var(--header-ink); }
body.op-head-custom .ct-header:not(.ct-panel) svg { fill: currentColor; }

/* Solid state: black ink, and the bar shrinks */
body.op-head-custom .ct-header:not(.ct-panel).op-header--solid { --header-ink: var(--ink); }

body.op-head-custom .ct-header:not(.ct-panel) [data-row] {
  transition: padding 380ms var(--ease-out);
  padding-block: var(--header-pad);
}
body.op-head-custom .ct-header:not(.ct-panel).op-header--solid [data-row] {
  padding-block: var(--header-pad-stuck);
}

/* The logo shrinks with it */
body.op-head-custom .ct-header:not(.ct-panel) .site-logo-container img ,
body.op-head-custom .ct-header:not(.ct-panel) .ct-logo-container img {
  transition: max-height 380ms var(--ease-out);
  max-height: var(--logo-h);
}
body.op-head-custom .ct-header:not(.ct-panel).op-header--solid .site-logo-container img ,
body.op-head-custom .ct-header:not(.ct-panel).op-header--solid .ct-logo-container img {
  max-height: var(--logo-h-stuck);
}

/* Pages without a hero used to reserve a band for the header here. Two
   things were wrong with it: the class it keys on is added by JavaScript,
   so the padding applied on every first paint and throughout the Customizer
   preview; and the selector names no theme class, so it slipped past the
   opt-in gate and kept applying even with Overprint's header styling off.
   Reserving space is only correct when Overprint is pinning the header,
   which is now the only case it covers. */
body.op-head-custom.op-head-sticky:not(.op-has-hero) .op-main,
body.op-head-custom.op-head-sticky:not(.op-has-hero) #main-container {
  padding-top: var(--header-h, 140px);
}
body.op-head-custom:not(.op-has-hero) .ct-header:not(.ct-panel)::before { opacity: 1; }
body.op-head-custom:not(.op-has-hero) .ct-header:not(.ct-panel) { --header-ink: var(--ink); }

/* ==========================================================================
   31. HEADER — size, colour, left alignment

   The logo and menu sit together on the left, the way the reference does.
   Colour and size come from the settings block at the top of this file.
   ========================================================================== */

body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) [data-row="middle"] > div ,
body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) .ct-container ,
body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) .ct-container-fluid {
  justify-content: flex-start !important;
  gap: var(--sp-5);
}

/* Push everything after the logo to sit next to it, not at the far right */
body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) [data-column="middle"] ,
body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) [data-column="end"] {
  margin-left: 0 !important;
  margin-right: auto;
}

body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) .ct-menu-link ,
body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) .menu-item > a {
  font-size: calc(var(--size--1) * var(--header-size));
  letter-spacing: var(--header-track);
  text-transform: uppercase;
  font-variation-settings: 'wght' 600;
  color: var(--header-ink);
}

body.op-head-custom .ct-header:not(.ct-panel) [data-row] { padding-block: var(--header-pad); }

/* Site title, used when no logo image is set */
body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) .site-title ,
body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) .site-title a {
  color: var(--header-ink);
  font-size: calc(var(--size-1) * var(--header-size));
  letter-spacing: -0.02em;
}

/* Underline that grows on hover, matching the filter bar */
body.op-head-custom .ct-header:not(.ct-panel) .menu-item > a { position: relative; }
body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) .menu-item > a::after {
  content: '';
  position: absolute;
  left: 0; bottom: -4px;
  width: 0; height: 2px;
  background: var(--m);
  transition: width var(--dur-med) var(--ease-out);
}
body.op-head-custom.op-head-custom .ct-header:not(.ct-panel) .menu-item > a:hover::after ,
body.op-head-custom .ct-header:not(.ct-panel) .current-menu-item > a::after { width: 100%; }

/* ==========================================================================
   32. PROJECT PAGE
   Structure mirrors the reference DOM.
   ========================================================================== */

/* --- Title band: 240px, image behind, title left ---------------------- */

.op-titleband {
  position: relative;
  height: var(--titleband-h, 240px);
  overflow: hidden;
  isolation: isolate;
  background: var(--paper-warm);
}

.op-titleband__media {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: calc(100% + 160px);
  z-index: 1;
}
.op-titleband__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.op-titleband__media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--titleband-veil, rgba(244, 242, 237, 0.72));
}

.op-titleband__holder {
  position: relative;
  z-index: 2;
  height: 100%;
  display: flex;
  align-items: var(--titleband-y, center);
  justify-content: var(--titleband-x, flex-start);
}

.op-titleband__title {
  margin: 0;
  /* A fixed cap here forced a wrap on any title longer than about twenty
     characters, whatever the band was wide enough to hold. It is a setting
     now, with "no limit" as an option, because how long a headline runs is a
     judgement about that headline rather than a property of the theme. */
  max-width: var(--titleband-w, none);
  line-height: var(--titleband-lh, 1.05);
}

/* --- Container ------------------------------------------------------- */

.op-single__inner { padding-block: var(--gap-section); }

/* --- Gallery carousel ------------------------------------------------- */

.op-gslider {
  position: relative;
  margin-bottom: var(--sp-5);
}

.op-gslider__viewport {
  overflow: hidden;
  position: relative;
}

.op-gslider__slides {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  will-change: transform;
  transition: transform 620ms cubic-bezier(0.65, 0, 0.35, 1);
}

.op-gslider__slide {
  flex: 0 0 100%;
  min-width: 0;
  position: relative;
}
.op-gslider__slide img {
  width: 100%;
  height: auto;
  display: block;
  cursor: zoom-in;
}
.op-gslider__cap {
  display: block;
  margin-top: var(--sp-2);
  opacity: 0.5;
}

.op-gslider__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 52px;
  height: 52px;
  border: 2px solid var(--ink);
  border-radius: 50%;
  background: var(--paper);
  cursor: pointer;
  display: grid;
  place-items: center;
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}
.op-gslider:hover .op-gslider__nav,
.op-gslider__nav:focus-visible { opacity: 1; }

.op-gslider__nav--prev { left: var(--sp-4); }
.op-gslider__nav--next { right: var(--sp-4); }

.op-gslider__nav span {
  width: 11px; height: 11px;
  border-left: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink);
}
.op-gslider__nav--prev span { transform: rotate(45deg)   translate(1px, -1px); }
.op-gslider__nav--next span { transform: rotate(-135deg) translate(1px, -1px); }

.op-gslider__nav:hover { background: var(--m); border-color: var(--m); }
.op-gslider__nav:hover span { border-color: var(--paper); }

@media (pointer: coarse) {
  .op-gslider__nav { opacity: 1; }
}

/* --- Alternative gallery modes ---------------------------------------- */

.op-project__media { display: grid; gap: var(--sp-4); margin-bottom: var(--sp-5); }
.op-project__fig { margin: 0; }
.op-project__fig img {
  width: 100% !important;
  max-width: 100%;
  height: auto;
  display: block;
  cursor: zoom-in;
}
.op-project__fig figcaption { margin-top: var(--sp-2); opacity: 0.5; }

.op-gal--grid.op-project__media  { grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
.op-gal--side.op-project__media  { grid-template-columns: 1fr; }
@media (min-width: 700px) { .op-gal--side.op-project__media { grid-template-columns: 1fr 1fr; } }
.op-gal--masonry.op-project__media { display: block; columns: 2; column-gap: var(--sp-4); }
.op-gal--masonry .op-project__fig { break-inside: avoid; margin-bottom: var(--sp-4); }

/* --- 75 / 25 columns --------------------------------------------------- */

.op-cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-5);
  padding-bottom: var(--gap-section);
}

@media (min-width: 900px) {
  .op-cols {
    grid-template-columns: 3fr 1fr;   /* 75 / 25 */
    gap: var(--sp-6);
    align-items: start;
  }
}

.op-cols__title {
  font-size: var(--step-2);
  margin: 0 0 var(--sp-4);
}

.op-cols__text p {
  max-width: 72ch;
  line-height: 1.7;
}

.op-btn {
  display: inline-flex;
  align-items: center;
  border: 2px solid var(--ink);
  padding: 0.85em 1.8em;
  margin-top: var(--sp-4);
  font-size: calc(var(--size--1) * var(--coef-button));
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-variation-settings: 'wght' 600;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.op-btn:hover {
  background: var(--accent, var(--m));
  border-color: var(--accent, var(--m));
  color: var(--paper);
}

.op-info { margin-bottom: var(--sp-5); }
.op-info__label {
  font-size: calc(var(--size--1) * var(--coef-button));
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-variation-settings: 'wght' 600;
  opacity: 0.4;
  margin: 0 0 var(--sp-2);
}
.op-info p { margin: 0; line-height: 1.7; }

/* --- Navigation -------------------------------------------------------- */

.op-nav {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sp-4);
  padding-block: var(--sp-5);
  border-top: 1px solid rgba(17, 17, 17, 0.15);
}

.op-nav__prev a,
.op-nav__next a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
}
.op-nav__next { justify-self: end; }
.op-nav__next .op-nav__info { text-align: right; }

.op-nav__info { display: grid; gap: 2px; }
.op-nav__cats { opacity: 0.4; }

.op-nav__title {
  font-size: var(--step-1);
  font-variation-settings: 'wght' 800;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 1.1;
  transition: color var(--dur-fast) var(--ease-out);
}
.op-nav__prev a:hover .op-nav__title,
.op-nav__next a:hover .op-nav__title { color: var(--m); }

.op-nav__chev {
  width: 12px; height: 12px;
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  flex: none;
  opacity: 0.5;
}
.op-nav__chev--l { transform: rotate(45deg); }
.op-nav__chev--r { transform: rotate(-135deg); }

.op-nav__index {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3px;
  width: 22px;
  height: 22px;
}
.op-nav__index span {
  background: var(--ink);
  transition: background var(--dur-fast) var(--ease-out);
}
.op-nav__index:hover span { background: var(--m); }

/* --- Related projects --------------------------------------------------- */

.op-related { padding-bottom: var(--gap-section); }

.op-related__heading {
  font-size: var(--step-2);
  margin-bottom: var(--sp-5);
}

.op-related__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr));
  gap: var(--sp-4);
}

.op-related__media {
  position: relative;
  aspect-ratio: 600 / 800;      /* the reference crop */
  overflow: hidden;
  background: var(--paper-warm);
}
.op-related__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 900ms var(--ease-out);
}
.op-related__item:hover .op-related__media img { transform: scale(1.05); }

.op-related__hover {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(17, 17, 17, 0.35);
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-out);
}
.op-related__item:hover .op-related__hover { opacity: 1; }

.op-related__view {
  border: 2px solid var(--paper);
  color: var(--paper);
  padding: 0.6em 1.4em;
  font-size: calc(var(--size--1) * var(--coef-button));
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-variation-settings: 'wght' 600;
  transform: translateY(8px);
  transition: transform var(--dur-med) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
.op-related__item:hover .op-related__view { transform: translateY(0); }
.op-related__view:hover {
  background: var(--paper);
  color: var(--ink);
}

.op-related__desc { padding-top: var(--sp-3); }

.op-related__title {
  font-size: var(--step-1);
  margin: 0 0 var(--sp-1);
  transition: color var(--dur-fast) var(--ease-out);
}
.op-related__item:hover .op-related__title { color: var(--accent, var(--m)); }

.op-related__cats {
  font-size: calc(var(--size--1) * var(--coef-button));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.4;
}

/* --- Mobile ------------------------------------------------------------- */

@media (max-width: 767px) {
  .op-titleband { height: 160px; }
  .op-icons { gap: var(--sp-5); }
  .op-nav { grid-template-columns: 1fr; gap: var(--sp-4); }
  .op-nav__next { justify-self: start; }
  .op-nav__next .op-nav__info { text-align: left; }
  .op-nav__index { display: none; }
  .op-gal--masonry.op-project__media { columns: 1; }
}

@media print {
  body.op-head-custom .ct-header, body.op-head-custom .op-icons, body.op-head-custom .op-nav, body.op-head-custom .op-related, body.op-head-custom .op-totop, body.op-head-custom footer { display: none !important; }
  .op-gslider__slides { transform: none !important; display: block; }
  .op-gslider__slide { break-inside: avoid; margin-bottom: 1rem; }
  .op-gslider__nav { display: none; }
}


/* ==========================================================================
   33. CUSTOMIZER-DRIVEN OVERRIDES

   Everything here reads a variable written by the Customizer, with a
   fallback so the theme still looks right if a setting was never touched.
   ========================================================================== */

/* --- Page titles ------------------------------------------------------- */

.op-archive__head {
  padding-top: var(--pagetitle-top, 4rem);
  padding-bottom: var(--pagetitle-bottom, 2rem);
  text-align: var(--pagetitle-align, left);
}

.op-archive__title {
  font-size: calc(var(--step-5) * var(--pagetitle-scale, 1));
  color: var(--pagetitle-ink, var(--ink));
  text-transform: var(--pagetitle-case, none);
  margin: 0;
}

.op-archive__sub,
.op-titleband__sub {
  margin: var(--sp-2) 0 0;
  opacity: 0.5;
}


/* --- Buttons ----------------------------------------------------------- */

.op-btn,
.op-slide__btn,
.op-related__view {
  border-radius: var(--btn-radius, 0);
  border-width: var(--btn-border, 2px);
  padding: var(--btn-py, 0.85em) var(--btn-px, 1.8em);
  letter-spacing: var(--btn-track, 0.16em);
  text-transform: var(--btn-case, uppercase);
}

/* --- Card hover -------------------------------------------------------- */

.op-card .op-plate img { transition: transform var(--card-hover-time, 0.42s) var(--ease-out); }

.op-card__shader { transition: opacity var(--card-hover-time, 0.42s) var(--ease-out); }
.op-card__link:hover .op-card__shader { opacity: calc(var(--card-shade, 22) / 100); }

.op-card__mark { opacity: 0; }
.op-card__link:hover .op-card__mark { opacity: var(--card-mark, 1); }

/* --- Project page ------------------------------------------------------ */

@media (min-width: 900px) {
  .op-project__info-inner,
  .op-cols__side { position: var(--info-position, sticky); top: var(--sp-6); }
}

.op-nav__info { display: var(--nav-info-display, grid); }

/* --- Back to top ------------------------------------------------------- */

.op-totop {
  display: var(--totop-display, grid);
  border-radius: var(--totop-radius, 0);
}

/* --- Interface toggles ------------------------------------------------- */

body.op-no-transition .op-veil { display: none !important; }
body.op-no-lightbox .op-zoomable { cursor: default; }


/* ==========================================================================
   34. EXPLICIT OVERRIDES

   Blocksy sets colours and sizes on h1–h6 directly. A declared value always
   beats an inherited one, so a colour set on a parent never reaches these
   headings. Everything the Customizer touches is therefore declared here,
   on the element itself, at the end of the file.
   ========================================================================== */

/* --- Hero and slide titles ------------------------------------------- */

.op-hero .op-hero__title,
.op-slider .op-slide__title {
  color: var(--slide-ink, var(--paper));
  font-size: calc(var(--step-6) * var(--slide-title-scale, 1));
  font-variation-settings: 'wght' var(--slide-weight, 800);
  line-height: var(--slide-lh, 0.92);
  letter-spacing: var(--slide-track, -0.035em);
  text-transform: var(--slide-case, none);
  max-width: var(--slide-w);
  line-height: var(--slide-lh, 0.92);
}

.op-hero .op-hero__sub,
.op-slider .op-slide__sub,
.op-hero .op-hero__cue {
  color: var(--slide-ink, var(--paper));
  font-size: calc(var(--step--1) * var(--slide-sub-scale, 1));
}

.op-hero .op-hero__cue { opacity: 0.9; }

/* --- Filter bar ------------------------------------------------------- */

.op-filter .op-filter__btn {
  font-size: calc(var(--size--1) * var(--coef-button) * var(--filter-size, 1));
  color: var(--filter-ink, var(--ink));
  opacity: var(--filter-dim, 0.45);
  font-variation-settings: 'wght' var(--filter-weight, 600);
  letter-spacing: var(--filter-track, 0.16em);
}

.op-filter .op-filter__btn:hover { opacity: calc(var(--filter-dim, 0.45) + 0.3); }

.op-filter .op-filter__btn.is-active {
  color: var(--filter-active, var(--ink));
  opacity: 1;
}

.op-filter .op-filter__btn::after {
  height: var(--filter-rule-h, 3px);
  background: var(--filter-rule, var(--m));
}

/* --- Card text -------------------------------------------------------- */

.op-card .op-card__title {
  font-size: calc(var(--step-1) * var(--card-title-scale, 1));
  color: var(--card-title-ink, var(--ink));
  text-transform: var(--card-title-case, uppercase);
}
.op-card__link:hover .op-card__title { color: var(--accent, var(--m)); }

.op-card .op-card__cats {
  font-size: calc(var(--size--1) * var(--coef-button) * var(--card-cat-scale, 1));
  color: var(--card-cat-ink, var(--ink));
  opacity: var(--card-cat-dim, 0.4);
}

/* --- Project title band ------------------------------------------------
   Its own variables rather than borrowing the page-title ones. The band is
   a flex box, so it needs flex values; the page heading is a text block and
   needs text-align values. Sharing one variable meant one of them was
   always reading something it could not use. */

.op-titleband .op-titleband__holder {
  justify-content: var(--titleband-x, flex-start);
  align-items: var(--titleband-y, center);
  padding-inline: var(--titleband-pad, 0);
}

.op-titleband .op-titleband__title {
  font-size: calc(var(--step-4) * var(--titleband-scale, 1)) !important;
  color: var(--titleband-ink, var(--ink));
  text-align: var(--titleband-text, left);
  text-transform: var(--titleband-case, none);
  font-variation-settings: 'wght' var(--titleband-weight, 800);
  letter-spacing: var(--titleband-track, -0.035em);
  line-height: var(--titleband-lh, 1.05);
  max-width: var(--titleband-w, none);
}

/* --- Page headings ----------------------------------------------------- */

.op-archive__head .op-archive__title {
  color: var(--pagetitle-ink, var(--ink));
  font-size: calc(var(--step-5) * var(--pagetitle-scale, 1));
  text-transform: var(--pagetitle-case, none);
  line-height: var(--pagetitle-lh, 1.05);
  max-width: var(--pagetitle-w, none);
}


/* ==========================================================================
   35. HEADER SKIN AND CARD HOVER STYLES
   ========================================================================== */

/* --- Skin ------------------------------------------------------------- */

body.op-header-dark  { --header-ink: var(--ink); }
body.op-header-light { --header-ink: var(--paper); }

/* Once the bar has a background it is always dark text */
body.op-head-custom .ct-header:not(.ct-panel).op-header--solid { --header-ink: var(--ink) !important; }

/* --- Hover: plates (default) ------------------------------------------ */
/* The misregistration itself is driven by JS; nothing extra needed here. */

/* --- Hover: zoom only -------------------------------------------------- */

body.op-hover-zoom .op-card .op-layer { display: none; }
body.op-hover-zoom .op-card .op-plate img { opacity: 1 !important; }
body.op-hover-zoom .op-card__link:hover .op-plate img { transform: scale(var(--card-zoom, 1.05)); }

/* --- Hover: greyscale until hovered ------------------------------------ */

body.op-hover-grey .op-card .op-layer { display: none; }
body.op-hover-grey .op-card .op-plate img {
  opacity: 1 !important;
  filter: grayscale(1);
  transition: filter var(--card-hover-time, 0.42s) var(--ease-out),
              transform var(--card-hover-time, 0.42s) var(--ease-out);
}
body.op-hover-grey .op-card__link:hover .op-plate img { filter: grayscale(0); }

/* --- Hover: card lifts -------------------------------------------------- */

body.op-hover-lift .op-card .op-layer { display: none; }
body.op-hover-lift .op-card .op-plate img { opacity: 1 !important; }
body.op-hover-lift .op-card {
  transition: transform var(--card-hover-time, 0.42s) var(--ease-out),
              box-shadow var(--card-hover-time, 0.42s) var(--ease-out);
}
body.op-hover-lift .op-card:hover {
  transform: translateY(-10px);
  box-shadow: 0 18px 40px rgba(17, 17, 17, 0.14);
}

/* --- Hover: nothing ----------------------------------------------------- */

body.op-hover-none .op-card .op-layer,
body.op-hover-none .op-card__shader,
body.op-hover-none .op-card__mark { display: none; }
body.op-hover-none .op-card .op-plate img { opacity: 1 !important; }

/* ==========================================================================
   36. NOT FOUND
   ========================================================================== */

.op-404 {
  min-height: 62vh;
  display: grid;
  place-content: center;
  text-align: center;
  gap: var(--sp-4);
  padding-block: var(--gap-section);
}

.op-404__code {
  font-size: var(--step-6);
  line-height: 0.9;
  margin: 0;
  color: var(--m);
}

.op-404__text {
  font-size: var(--step-1);
  max-width: 40ch;
  margin: 0 auto;
}


/* ==========================================================================
   37. IMAGE SIZING AND FINE POSITIONING
   ========================================================================== */

/* --- Card frames -------------------------------------------------------- */

.op-card .op-plate {
  background: var(--card-bg, var(--paper));
}

.op-card .op-plate {
  overflow: hidden;
}

.op-card .op-plate img {
  object-fit: var(--card-fit, cover);
  object-position: var(--card-x, 50%) var(--card-y, 50%);
  transform: scale(var(--card-base-zoom, 1));
  transform-origin: var(--card-x, 50%) var(--card-y, 50%);
}

/* Hover zoom multiplies the base zoom rather than replacing it, so the two
   settings do not fight each other. */
.op-card__link:hover .op-plate img {
  transform: scale(calc(var(--card-base-zoom, 1) * var(--card-zoom, 1)));
}

/* Fixed-height mode pins every frame to the same pixel height, whatever the
   column width. Aspect-ratio mode lets them breathe with the layout. */
body.op-cards-height .op-card--tall .op-plate,
body.op-cards-height .op-card--sq   .op-plate,
body.op-cards-height .op-card--wide .op-plate {
  aspect-ratio: auto;
  height: var(--card-h, 420px);
}

/* --- Project page gallery ----------------------------------------------- */

.op-project__fig,
.op-gslider__slide {
  background: var(--gallery-bg, transparent);
}

/* height, not max-height.
 *
 * max-height only ever shrinks: an image already shorter than the limit is
 * left alone, so a mixed set never lines up. Setting height forces every
 * frame to the same size and object-fit decides what happens inside it —
 * small images scale up, large ones scale down.
 *
 * Zero is translated to "auto" in PHP, so this is safe at any setting.
 *
 * The frame clips, so zoom can push past the edges the way pinching does on
 * a phone; object-position then decides which part survives. */
.op-project__fig,
.op-gslider__slide {
  overflow: hidden;
  text-align: center;
}

.op-project__fig img,
.op-gslider__slide img {
  height: var(--gallery-h, auto) !important;
  width: var(--gallery-w, 100%) !important;
  object-fit: var(--gallery-fit, contain);
  object-position: var(--gallery-x, 50%) var(--gallery-y, 50%);
  transform: scale(var(--gallery-zoom, 1));
  transform-origin: var(--gallery-x, 50%) var(--gallery-y, 50%);
}

/* --- Fine positioning ---------------------------------------------------- */
/* Transforms move the text without disturbing anything around it, so a nudge
   cannot accidentally reflow the rest of the page. */

.op-archive__head .op-archive__title,
.op-archive__head .op-archive__sub {
  transform: translate(var(--pagetitle-dx, 0), var(--pagetitle-dy, 0));
}

.op-titleband .op-titleband__title,
.op-titleband .op-titleband__sub {
  transform: translate(var(--titleband-dx, 0), var(--titleband-dy, 0));
}

.op-hero .op-hero__body,
.op-slider .op-slide__body > * {
  transform: translate(var(--slide-dx, 0), var(--slide-dy, 0));
}


/* ==========================================================================
   38. EDITOR-CONTROLLED GALLERY

   In this mode the block markup is rendered as WordPress produced it. The
   theme must not impose a height, a fit or a crop, or the settings chosen
   in the editor would be silently overwritten — which is the problem this
   mode exists to solve.
   ========================================================================== */

/* An image the editor sized keeps its own value; one that was never touched
   falls through to the theme setting.
 *
 * Blanket "height: auto" here used to override the theme for every image in
 * this mode, which broke the layering: a project set to "use the theme
 * setting" then had nothing to inherit from. Narrowing it to images WITH an
 * inline size restores the chain while still leaving editor choices alone. */

.op-project__blocks img[style*="aspect-ratio"],
.op-project__blocks img[style*="height"],
.op-project__blocks img[style*="width"] {
  height: auto;
  width: auto;
  max-width: 100%;
  object-fit: initial;
  transform: none;
}

/* Core writes aspect-ratio inline when Scale is chosen; fill that frame */
.op-project__blocks img[style*="aspect-ratio"] {
  height: 100% !important;
  object-fit: cover;
}

/* Everything the editor left alone answers to the theme, exactly as the
   other gallery modes do */
.op-project__blocks img:not([style*="aspect-ratio"]):not([style*="height"]):not([style*="width"]) {
  height: var(--gallery-h, auto);
  width: var(--gallery-w, 100%);
  object-fit: var(--gallery-fit, contain);
  object-position: var(--gallery-x, 50%) var(--gallery-y, 50%);
  transform: scale(var(--gallery-zoom, 1));
  transform-origin: var(--gallery-x, 50%) var(--gallery-y, 50%);
}

.op-project__blocks figure,
.op-project__blocks .wp-block-image { overflow: hidden; }

.op-project__blocks figure { margin-block: var(--sp-4); }
.op-project__blocks figcaption {
  font-size: var(--step--1);
  opacity: 0.5;
  margin-top: var(--sp-2);
}

/* Galleries keep the column count chosen in the editor */
.op-project__blocks .wp-block-gallery { margin-block: var(--sp-5); }

/* Clicking still opens the lightbox */
.op-project__blocks img { cursor: zoom-in; }


/* ==========================================================================
   39. NARROW SCREENS

   Sizes that work at 1440px rarely work at 320px. Rather than one set of
   values bent to cover both, the phone gets its own, and the coefficient
   system already in place scales the type.
   ========================================================================== */

@media (max-width: 767px) {

  .op-hero { min-height: var(--hero-height-m, 70vh); }

  .op-hero .op-hero__title,
  .op-slider .op-slide__title {
    font-size: calc(var(--step-6) * var(--slide-title-scale, 1) * var(--slide-scale-m, 1));
    max-width: none;
  }

  .op-titleband { height: var(--titleband-h-m, 160px); }

  .op-archive__head .op-archive__title {
    font-size: calc(var(--step-5) * var(--pagetitle-scale, 1) * var(--pagetitle-scale-m, 1));
  }

  /* Nudges used to be a single value, so one measured against a wide screen
     pushed text off a narrow one and had to be cancelled here. Each tier now
     carries its own nudge, defaulting to zero, so the phone value is either
     deliberate or nothing — cancelling it would only ignore what was asked
     for. The rule is gone rather than commented out; a disabled control that
     silently does nothing is worse than no control. */

  /* Fixed card heights make tall thin cards in a single column */
  body.op-cards-height .op-card--tall .op-plate,
  body.op-cards-height .op-card--sq   .op-plate,
  body.op-cards-height .op-card--wide .op-plate {
    height: var(--card-h-m, 300px);
  }
}


/* ==========================================================================
   40. HEADER ON PHONES AND TABLETS

   The header row is a flex line: logo, then whatever else. Blocksy pushes
   the trigger to the far edge by default; this brings it back beside the
   logo when that is what was chosen.
   ========================================================================== */

body.op-head-custom .ct-header:not(.ct-panel) [data-row] > div ,
body.op-head-custom .ct-header:not(.ct-panel) [data-row] {
  align-items: center;
}

body.op-head-custom.op-burger-left .ct-header:not(.ct-panel) [data-id="trigger"] ,
body.op-head-custom.op-burger-left .ct-header:not(.ct-panel) .ct-header-trigger ,
body.op-head-custom.op-burger-left .ct-header:not(.ct-panel) button[data-toggle-panel] {
  margin-left: var(--sp-4);
  margin-right: auto;
  order: 2;
}

body.op-head-custom.op-burger-right .ct-header:not(.ct-panel) [data-id="trigger"] ,
body.op-head-custom.op-burger-right .ct-header:not(.ct-panel) .ct-header-trigger ,
body.op-head-custom.op-burger-right .ct-header:not(.ct-panel) button[data-toggle-panel] {
  margin-left: auto;
  order: 9;
}

/* Logo stays first whichever side the button is on */
body.op-head-custom .ct-header:not(.ct-panel) [data-id="logo"] ,
body.op-head-custom .ct-header:not(.ct-panel) .site-branding ,
body.op-head-custom .ct-header:not(.ct-panel) .site-logo-container { order: 1; }

@media (max-width: 767px) {
  /* A header sized for a wide screen leaves too little room for content */
  body.op-head-custom .ct-header:not(.ct-panel) [data-row] {
    padding-block: var(--header-pad-m, 0.9rem);
  }

  body.op-head-custom .ct-header:not(.ct-panel) .site-logo-container img ,
  body.op-head-custom .ct-header:not(.ct-panel) .ct-logo-container img {
    max-height: var(--logo-h-m, 34px);
  }
}

/* ==========================================================================
   41. COMPONENTS

   Every dimension reads a custom property, so the shortcode attribute and
   the Customizer default reach the same place. Where a value would be wrong
   on a phone there is a second property for it.
   ========================================================================== */

/* --- Image comparison --------------------------------------------------- */

.op-compare {
  position: relative;
  width: var(--cmp-w, 100%);
  height: var(--cmp-h, auto);
  aspect-ratio: var(--cmp-ratio, auto);
  overflow: hidden;
  margin-inline: auto;
  touch-action: pan-y;
  user-select: none;
  background: var(--cmp-bg, var(--paper-warm, #e9e6df));
  cursor: ew-resize;
}

.op-compare__before,
.op-compare__after { position: absolute; inset: 0; }

.op-compare__before img,
.op-compare__after img {
  width: 100%;
  height: 100%;
  object-fit: var(--cmp-fit, cover);
  object-position: center;
  display: block;
  pointer-events: none;
}

/* The clip is what the handle moves. Percentages keep it correct at any size. */
.op-compare__clip {
  position: absolute;
  inset: 0;
  clip-path: inset(0 calc(100% - var(--cmp-pos, 50%)) 0 0);
  will-change: clip-path;
}

.op-compare--vertical .op-compare__clip {
  clip-path: inset(0 0 calc(100% - var(--cmp-pos, 50%)) 0);
}

.op-compare__handle {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--cmp-pos, 50%);
  width: var(--cmp-line, 2px);
  margin-left: calc(var(--cmp-line, 2px) / -2);
  background: var(--cmp-line-colour, var(--paper));
  border: 0;
  padding: 0;
  cursor: ew-resize;
  z-index: 4;
}

.op-compare--vertical .op-compare__handle {
  left: 0; right: 0; top: var(--cmp-pos, 50%); bottom: auto;
  width: auto; height: var(--cmp-line, 2px);
  margin-left: 0;
  margin-top: calc(var(--cmp-line, 2px) / -2);
  cursor: ns-resize;
}

.op-compare__handle::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: var(--cmp-knob, 46px);
  height: var(--cmp-knob, 46px);
  border-radius: 50%;
  background: var(--cmp-knob-bg, var(--paper));
  border: var(--cmp-line, 2px) solid var(--cmp-line-colour, var(--ink));
  box-shadow: 0 2px 14px rgba(17, 17, 17, 0.18);
}

.op-compare__label {
  position: absolute;
  bottom: var(--cmp-label-y, 1rem);
  z-index: 5;
  font-size: calc(var(--size--1) * var(--cmp-label-size, 1));
  letter-spacing: var(--cmp-label-track, 0.14em);
  text-transform: var(--cmp-label-case, uppercase);
  color: var(--cmp-label-ink, var(--paper));
  background: var(--cmp-label-bg, rgba(17, 17, 17, 0.6));
  padding: 0.45em 0.9em;
}
.op-compare__before .op-compare__label { left: var(--cmp-label-x, 1rem); }
.op-compare__after  .op-compare__label { right: var(--cmp-label-x, 1rem); }

@media (max-width: 767px) {
  .op-compare { height: var(--cmp-h-m, var(--cmp-h, auto)); }
  .op-compare__handle::after { width: var(--cmp-knob-m, 36px); height: var(--cmp-knob-m, 36px); }
}

/* --- Gallery strip ------------------------------------------------------- */

.op-strip {
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
  margin-block: var(--strip-my, var(--sp-5));
}
.op-strip::-webkit-scrollbar { display: none; }

.op-strip--bleed {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
}

.op-strip--snap { scroll-snap-type: x mandatory; }

.op-strip__track {
  display: flex;
  gap: var(--strip-gap, 0px);
  height: var(--strip-h, 70vh);
}

.op-strip__item {
  flex: 0 0 auto;
  height: 100%;
  margin: 0;
  scroll-snap-align: center;
  position: relative;
}

.op-strip__item img {
  height: 100%;
  width: auto;
  max-width: none;
  object-fit: var(--strip-fit, cover);
  display: block;
}

.op-strip__cap {
  position: absolute;
  left: var(--sp-3);
  bottom: var(--sp-3);
  color: var(--paper);
  background: rgba(17, 17, 17, 0.55);
  padding: 0.35em 0.7em;
  font-size: var(--step--1);
}

@media (max-width: 767px) {
  .op-strip__track { height: var(--strip-h-m, 40vh); }
}

/* --- Portfolio carousel --------------------------------------------------- */

.op-pcar { position: relative; }

.op-pcar__viewport { overflow: hidden; }

.op-pcar__track {
  display: flex;
  gap: var(--pcar-gap, 1.5rem);
  transition: transform 600ms cubic-bezier(0.65, 0, 0.35, 1);
  will-change: transform;
}

.op-pcar__item {
  flex: 0 0 calc(
    (100% - (var(--pcar-per, 4) - 1) * var(--pcar-gap, 1.5rem)) / var(--pcar-per, 4)
  );
  min-width: 0;
}

@media (max-width: 1000px) { .op-pcar { --pcar-per: var(--pcar-per-t, 2); } }
@media (max-width: 600px)  { .op-pcar { --pcar-per: var(--pcar-per-m, 1); } }

.op-pcar__media {
  position: relative;
  overflow: hidden;
  aspect-ratio: var(--pcar-ratio, var(--ratio-tall));
  height: var(--pcar-h, auto);
  background: var(--card-bg, var(--paper));
}
.op-pcar__media img {
  width: 100%;
  height: 100%;
  object-fit: var(--pcar-fit, cover);
  object-position: var(--card-x, 50%) var(--card-y, 50%);
  display: block;
  transition: transform var(--card-hover-time, 0.42s) var(--ease-out);
}
.op-pcar__item:hover .op-pcar__media img { transform: scale(var(--card-zoom, 1)); }

.op-pcar__shade {
  position: absolute;
  inset: 0;
  background: var(--accent, var(--m));
  mix-blend-mode: multiply;
  opacity: 0;
  transition: opacity var(--card-hover-time, 0.42s) var(--ease-out);
}
.op-pcar__item:hover .op-pcar__shade { opacity: calc(var(--card-shade, 22) / 100); }

.op-pcar__meta {
  padding-top: var(--sp-3);
  text-align: var(--pcar-align, center);
}
.op-pcar__title {
  font-size: calc(var(--step-1) * var(--card-title-scale, 1));
  margin: 0 0 var(--sp-1);
  text-transform: var(--card-title-case, uppercase);
}
.op-pcar__cats {
  margin: 0;
  font-size: calc(var(--size--1) * var(--coef-button));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: var(--card-cat-dim, 0.4);
}

.op-pcar__nav {
  position: absolute;
  top: var(--pcar-nav-y, 40%);
  z-index: 4;
  width: var(--pcar-nav-size, 48px);
  height: var(--pcar-nav-size, 48px);
  border: 2px solid var(--ink);
  border-radius: var(--pcar-nav-radius, 50%);
  background: var(--paper);
  display: grid;
  place-items: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-med) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
.op-pcar:hover .op-pcar__nav,
.op-pcar__nav:focus-visible { opacity: 1; }
.op-pcar__nav:disabled { opacity: 0 !important; pointer-events: none; }

.op-pcar__nav--prev { left: var(--pcar-nav-x, -18px); }
.op-pcar__nav--next { right: var(--pcar-nav-x, -18px); }

.op-pcar__nav span {
  width: 10px; height: 10px;
  border-left: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink);
}
.op-pcar__nav--prev span { transform: rotate(45deg)   translate(1px, -1px); }
.op-pcar__nav--next span { transform: rotate(-135deg) translate(1px, -1px); }
.op-pcar__nav:hover { background: var(--m); border-color: var(--m); }
.op-pcar__nav:hover span { border-color: var(--paper); }

.op-pcar__dots {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: var(--sp-4);
}
.op-pcar__dots button {
  width: 8px; height: 8px;
  border: 0;
  padding: 0;
  background: var(--ink);
  opacity: 0.25;
  cursor: pointer;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.op-pcar__dots button.is-on { opacity: 1; background: var(--m); }

@media (pointer: coarse) { .op-pcar__nav { opacity: 1; } }

/* --- Section dot navigation ------------------------------------------------ */

.op-snav {
  position: fixed;
  z-index: 60;
  display: grid;
  gap: var(--snav-gap, 1rem);
  top: var(--snav-top, 50%);
  transform: translateY(var(--snav-shift, -50%)) translateY(var(--snav-offset-y, 0px));
}

.op-snav--right { right: var(--snav-offset-x, 2rem); }
.op-snav--left  { left:  var(--snav-offset-x, 2rem); justify-items: start; }

.op-snav__dot {
  width: var(--snav-size, 9px);
  height: var(--snav-size, 9px);
  border-radius: 50%;
  border: 1px solid var(--snav-ink, var(--ink));
  background: transparent;
  padding: 0;
  cursor: pointer;
  position: relative;
  transition: background var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.op-snav__dot.is-on {
  background: var(--snav-active, var(--m));
  border-color: var(--snav-active, var(--m));
  transform: scale(var(--snav-active-scale, 1.4));
}

.op-snav__label {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  font-size: calc(var(--size--1) * var(--snav-label-size, 1));
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--snav-ink, var(--ink));
  background: var(--paper);
  padding: 0.3em 0.6em;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.op-snav--right .op-snav__label { right: calc(100% + 12px); }
.op-snav--left  .op-snav__label { left:  calc(100% + 12px); }
.op-snav__dot:hover .op-snav__label { opacity: 1; }

@media (max-width: 767px) {
  .op-snav { display: var(--snav-mobile, none); }
}

/* --- Overlay menu ----------------------------------------------------------- */

.op-ov__open {
  position: fixed;
  z-index: 120;
  top: var(--ov-btn-y, 50%);
  right: var(--ov-btn-x, 2rem);
  width: var(--ov-btn-size, 46px);
  height: var(--ov-btn-size, 46px);
  display: grid;
  align-content: center;
  gap: 6px;
  border: 0;
  background: transparent;
  cursor: pointer;
  padding: 0;
}
.op-ov__open span {
  display: block;
  height: 2px;
  width: 100%;
  background: var(--ov-btn-ink, var(--ink));
  transition: transform var(--dur-med) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out);
}

.op-ov {
  position: fixed;
  inset: 0;
  z-index: 130;
  background: var(--ov-bg, var(--ink));
  color: var(--ov-ink, var(--paper));
  display: grid;
  place-items: var(--ov-align, center);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--ov-time, 0.45s) var(--ease-out),
              visibility 0s linear var(--ov-time, 0.45s);
}
body.op-ov-open .op-ov {
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}
body.op-ov-open { overflow: hidden; }

.op-ov__inner {
  text-align: var(--ov-text-align, center);
  padding: var(--sp-6);
  display: grid;
  gap: var(--sp-5);
  justify-items: var(--ov-align, center);
}

.op-ov__logo img { max-height: var(--ov-logo-h, 60px); width: auto; }

.op-ov__nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--ov-item-gap, 0.4rem);
}

.op-ov__nav a {
  font-size: calc(var(--step-4) * var(--ov-size, 1));
  font-variation-settings: 'wght' var(--ov-weight, 800);
  letter-spacing: var(--ov-track, -0.02em);
  text-transform: var(--ov-case, uppercase);
  line-height: 1.05;
  color: var(--ov-ink, var(--paper));
  transition: color var(--dur-fast) var(--ease-out);
  display: inline-block;
}
.op-ov__nav a:hover { color: var(--ov-hover, var(--m)); }

.op-ov__text {
  max-width: 44ch;
  opacity: 0.6;
  font-size: calc(var(--step-0) * var(--ov-text-size, 1));
}

.op-ov__close {
  position: absolute;
  top: var(--ov-close-y, 2rem);
  right: var(--ov-close-x, 2rem);
  width: var(--ov-close-size, 40px);
  height: var(--ov-close-size, 40px);
  border: 0;
  background: transparent;
  cursor: pointer;
}
.op-ov__close span {
  position: absolute;
  top: 50%; left: 0;
  width: 100%; height: 2px;
  background: var(--ov-ink, var(--paper));
}
.op-ov__close span:nth-child(1) { transform: rotate(45deg); }
.op-ov__close span:nth-child(2) { transform: rotate(-45deg); }

@media (max-width: 767px) {
  .op-ov__nav a { font-size: calc(var(--step-3) * var(--ov-size-m, 1)); }
  .op-ov__open { top: var(--ov-btn-y-m, 1.2rem); right: var(--ov-btn-x-m, 1.2rem); }
}

/* --- Load more --------------------------------------------------------------- */

.op-more { margin: var(--gap-section) 0 0; text-align: center; }
.op-card.is-beyond { display: none; }

.op-ov__social {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: var(--sp-4);
  flex-wrap: wrap;
  justify-content: var(--ov-text-align, center);
}
.op-ov__social a {
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ov-ink, var(--paper));
  opacity: 0.6;
  transition: opacity var(--dur-fast) var(--ease-out);
}
.op-ov__social a:hover { opacity: 1; }

/* ==========================================================================
   42. CONTENT BLOCKS
   ========================================================================== */

/* --- Steps ---------------------------------------------------------------- */

.op-steps {
  display: grid;
  grid-template-columns: repeat(var(--steps-cols, 1), minmax(0, 1fr));
  gap: var(--steps-gap, 2.5rem);
  text-align: var(--steps-align, left);
  margin-block: var(--sp-5);
}
@media (max-width: 767px) {
  .op-steps { grid-template-columns: repeat(var(--steps-cols-m, 1), minmax(0, 1fr)); }
}

.op-step { display: grid; grid-template-columns: auto 1fr; gap: var(--step-gap, 1.25rem); align-items: start; }
.op-steps--plain .op-step { grid-template-columns: 1fr; }
.op-steps--plain .op-step__num { display: none; }

.op-step__num {
  font-size: calc(var(--step-3) * var(--step-num-size, 1));
  font-variation-settings: 'wght' var(--step-num-weight, 800);
  line-height: 0.85;
  color: var(--step-num-ink, var(--m));
  letter-spacing: -0.04em;
}

.op-step__title {
  font-size: calc(var(--step-1) * var(--step-title-size, 1));
  margin: 0 0 var(--sp-2);
  text-transform: var(--step-title-case, none);
}
.op-step__text p { margin: 0 0 0.6em; }
.op-step__text p:last-child { margin-bottom: 0; }

/* --- Icon box -------------------------------------------------------------- */

.op-iconbox {
  display: grid;
  gap: var(--ib-gap, 1rem);
  text-align: var(--ib-align, left);
  margin-block: var(--sp-4);
}
.op-iconbox--side { grid-template-columns: auto 1fr; align-items: start; }

.op-iconbox__mark {
  font-size: calc(var(--step-3) * var(--ib-mark-size, 1));
  line-height: 1;
  color: var(--ib-mark-ink, var(--m));
  display: block;
}
.op-iconbox__mark img { width: var(--ib-img, 56px); height: auto; display: block; }

.op-iconbox__title {
  font-size: calc(var(--step-1) * var(--ib-title-size, 1));
  margin: 0 0 var(--sp-2);
  text-transform: var(--ib-title-case, none);
}
.op-iconbox__text { opacity: var(--ib-text-dim, 0.75); }

/* --- Flip card -------------------------------------------------------------- */

.op-flip {
  perspective: 1200px;
  height: var(--flip-h, auto);
  aspect-ratio: var(--flip-ratio, 3 / 4);
  cursor: pointer;
}

.op-flip__inner {
  position: relative;
  width: 100%; height: 100%;
  transform-style: preserve-3d;
  transition: transform var(--flip-time, 0.7s) cubic-bezier(0.65, 0, 0.35, 1);
}
.op-flip:hover .op-flip__inner,
.op-flip:focus-visible .op-flip__inner { transform: rotateY(180deg); }
.op-flip--x:hover .op-flip__inner,
.op-flip--x:focus-visible .op-flip__inner { transform: rotateX(180deg); }

.op-flip__face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  overflow: hidden;
  display: grid;
  place-items: center;
}
.op-flip__face--front img { width: 100%; height: 100%; object-fit: cover; }
.op-flip__face--back {
  transform: rotateY(180deg);
  background: var(--flip-back-bg, var(--ink));
  color: var(--flip-back-ink, var(--paper));
  padding: var(--flip-pad, 2rem);
  text-align: center;
}
.op-flip--x .op-flip__face--back { transform: rotateX(180deg); }

.op-flip__title {
  position: absolute;
  left: 0; right: 0; bottom: var(--flip-title-y, 1.5rem);
  color: var(--flip-title-ink, var(--paper));
  font-size: calc(var(--step-1) * var(--flip-title-size, 1));
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

html.op-reduced .op-flip__inner { transition: none; }

/* --- Accordion --------------------------------------------------------------- */

.op-acc { border-top: var(--acc-line, 1px) solid var(--acc-line-ink, rgba(17,17,17,0.15)); }
.op-acc__item { border-bottom: var(--acc-line, 1px) solid var(--acc-line-ink, rgba(17,17,17,0.15)); }

.op-acc__head {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--acc-pad, 1.1rem) 0;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  color: var(--acc-ink, var(--ink));
  font-size: calc(var(--step-1) * var(--acc-size, 1));
  font-variation-settings: 'wght' var(--acc-weight, 700);
  letter-spacing: var(--acc-track, 0);
  text-transform: var(--acc-case, none);
  transition: color var(--dur-fast) var(--ease-out);
}
.op-acc__head:hover { color: var(--acc-hover, var(--m)); }

.op-acc__sign {
  position: relative;
  width: var(--acc-sign, 16px);
  height: var(--acc-sign, 16px);
  flex: none;
}
.op-acc__sign::before,
.op-acc__sign::after {
  content: '';
  position: absolute;
  background: currentColor;
  transition: transform var(--dur-med) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.op-acc__sign::before { top: 50%; left: 0; width: 100%; height: 2px; margin-top: -1px; }
.op-acc__sign::after  { left: 50%; top: 0; height: 100%; width: 2px; margin-left: -1px; }
.op-acc__item.is-open .op-acc__sign::after { transform: rotate(90deg); opacity: 0; }

.op-acc__inner { padding-bottom: var(--acc-pad, 1.1rem); opacity: var(--acc-text-dim, 0.78); }

/* --- Tabs ---------------------------------------------------------------------- */

.op-tabs { margin-block: var(--sp-5); }
.op-tabs--side { display: grid; grid-template-columns: minmax(140px, auto) 1fr; gap: var(--sp-5); }

.op-tabs__list {
  display: flex;
  gap: var(--tabs-gap, 2rem);
  border-bottom: var(--tabs-line, 1px) solid var(--tabs-line-ink, rgba(17,17,17,0.15));
  margin-bottom: var(--sp-4);
  flex-wrap: wrap;
}
.op-tabs--side .op-tabs__list {
  flex-direction: column;
  border-bottom: 0;
  border-right: var(--tabs-line, 1px) solid var(--tabs-line-ink, rgba(17,17,17,0.15));
  margin-bottom: 0;
  padding-right: var(--sp-4);
}

.op-tabs__tab {
  background: none;
  border: 0;
  padding: 0 0 var(--tabs-pad, 0.7rem);
  cursor: pointer;
  position: relative;
  color: var(--tabs-ink, var(--ink));
  opacity: var(--tabs-dim, 0.45);
  font-size: calc(var(--size--1) * var(--tabs-size, 1));
  letter-spacing: var(--tabs-track, 0.14em);
  text-transform: var(--tabs-case, uppercase);
  font-variation-settings: 'wght' var(--tabs-weight, 600);
  transition: opacity var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.op-tabs__tab:hover { opacity: 0.8; }
.op-tabs__tab.is-on { opacity: 1; color: var(--tabs-active, var(--ink)); }

.op-tabs__tab::after {
  content: '';
  position: absolute;
  left: 0; bottom: -1px;
  width: 0; height: var(--tabs-rule, 3px);
  background: var(--tabs-rule-ink, var(--m));
  transition: width var(--dur-med) var(--ease-out);
}
.op-tabs__tab.is-on::after { width: 100%; }
.op-tabs--side .op-tabs__tab::after { display: none; }

.op-tabs__panel[hidden] { display: none; }

/* --- Quotes ---------------------------------------------------------------------- */

.op-quotes { position: relative; overflow: hidden; margin-block: var(--sp-6); }
.op-quotes__track {
  display: flex;
  transition: transform 600ms cubic-bezier(0.65, 0, 0.35, 1);
}

.op-quote {
  flex: 0 0 100%;
  margin: 0;
  padding-inline: var(--quote-pad, 3rem);
  text-align: var(--quote-align, center);
  display: grid;
  gap: var(--sp-3);
  justify-items: var(--quote-justify, center);
}

.op-quote__face {
  width: var(--quote-face, 68px);
  height: var(--quote-face, 68px);
  border-radius: 50%;
  object-fit: cover;
}

.op-quote__text {
  margin: 0;
  font-size: calc(var(--step-2) * var(--quote-size, 1));
  line-height: 1.4;
  font-variation-settings: 'wght' var(--quote-weight, 400);
  color: var(--quote-ink, var(--ink));
  max-width: var(--quote-width, 48ch);
}
.op-quote__text p { margin: 0; }

.op-quote__by { display: grid; gap: 2px; }
.op-quote__name {
  font-size: calc(var(--size--1) * var(--quote-name-size, 1));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-variation-settings: 'wght' 700;
}
.op-quote__role { font-size: var(--step--1); opacity: 0.45; }

.op-quotes__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px; height: 40px;
  border: 2px solid var(--ink);
  border-radius: 50%;
  background: var(--paper);
  cursor: pointer;
  display: grid;
  place-items: center;
}
.op-quotes__nav--prev { left: 0; }
.op-quotes__nav--next { right: 0; }
.op-quotes__nav span {
  width: 9px; height: 9px;
  border-left: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink);
}
.op-quotes__nav--prev span { transform: rotate(45deg)   translate(1px, -1px); }
.op-quotes__nav--next span { transform: rotate(-135deg) translate(1px, -1px); }

.op-quotes__dots { display: flex; justify-content: center; gap: 8px; margin-top: var(--sp-4); }
.op-quotes__dots button {
  width: 8px; height: 8px; border: 0; padding: 0;
  background: var(--ink); opacity: 0.25; cursor: pointer;
}
.op-quotes__dots button.is-on { opacity: 1; background: var(--m); }

/* --- Progress bar ------------------------------------------------------------------ */

.op-bar { margin-block: var(--bar-my, 1.2rem); }

.op-bar__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--sp-2);
}
.op-bar__label {
  font-size: calc(var(--size--1) * var(--bar-label-size, 1));
  letter-spacing: var(--bar-track, 0.12em);
  text-transform: var(--bar-case, uppercase);
  font-variation-settings: 'wght' var(--bar-weight, 600);
}
.op-bar__value { font-size: var(--step--1); opacity: 0.5; font-variant-numeric: tabular-nums; }

.op-bar__track {
  height: var(--bar-h, 6px);
  background: var(--bar-track-bg, rgba(17,17,17,0.1));
  border-radius: var(--bar-radius, 0);
  overflow: hidden;
}
.op-bar__fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--bar-fill, var(--m));
  border-radius: inherit;
  transition: width var(--bar-time, 1.2s) cubic-bezier(0.22, 1, 0.36, 1);
}
html.op-reduced .op-bar__fill { transition: none; }

/* --- Pie ---------------------------------------------------------------------------- */

.op-pie {
  position: relative;
  width: var(--pie-size, 150px);
  height: var(--pie-size, 150px);
  display: grid;
  place-items: center;
  margin-inline: auto;
}
.op-pie svg { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.op-pie circle { fill: none; stroke-width: var(--pie-stroke, 6px); }
.op-pie__track { stroke: var(--pie-track, rgba(17,17,17,0.1)); }
.op-pie__fill {
  stroke: var(--pie-fill, var(--m));
  stroke-linecap: var(--pie-cap, butt);
  stroke-dasharray: 276.46;
  stroke-dashoffset: 276.46;
  transition: stroke-dashoffset var(--pie-time, 1.4s) cubic-bezier(0.22, 1, 0.36, 1);
}
html.op-reduced .op-pie__fill { transition: none; }

.op-pie__num {
  font-size: calc(var(--step-2) * var(--pie-num-size, 1));
  font-variation-settings: 'wght' 800;
  font-variant-numeric: tabular-nums;
}
.op-pie__label {
  position: absolute;
  top: calc(100% + var(--sp-2));
  font-size: var(--step--1);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.5;
  white-space: nowrap;
}

/* --- Person ------------------------------------------------------------------------- */

.op-person { margin-block: var(--sp-4); }
.op-person__media {
  aspect-ratio: var(--person-ratio, 3 / 4);
  overflow: hidden;
  margin-bottom: var(--sp-3);
  background: var(--paper-warm, #e9e6df);
}
.op-person__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.op-person__name {
  font-size: calc(var(--step-1) * var(--person-name-size, 1));
  margin: 0 0 2px;
  text-transform: var(--person-name-case, none);
}
.op-person__role {
  margin: 0;
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.45;
}
.op-person__bio { margin-top: var(--sp-2); opacity: 0.75; }

/* --- Note ---------------------------------------------------------------------------- */

.op-note {
  border-left: var(--note-rule, 4px) solid var(--note-ink, var(--c));
  background: var(--note-bg, rgba(0, 174, 239, 0.07));
  padding: var(--note-pad, 1.1rem 1.4rem);
  margin-block: var(--sp-4);
}
.op-note--warn { --note-ink: #E0A500; --note-bg: rgba(224, 165, 0, 0.09); }
.op-note--ok   { --note-ink: #1F9D55; --note-bg: rgba(31, 157, 85, 0.09); }
.op-note__title { display: block; margin-bottom: var(--sp-1); }
.op-note__text p:last-child { margin-bottom: 0; }

/* --- Countdown ------------------------------------------------------------------------ */

.op-count { text-align: var(--count-align, center); margin-block: var(--sp-5); }
.op-count__label {
  display: block;
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.5;
  margin-bottom: var(--sp-3);
}
.op-count__row {
  display: flex;
  justify-content: var(--count-justify, center);
  gap: var(--count-gap, 2.5rem);
  flex-wrap: wrap;
}
.op-count__unit { display: grid; gap: 4px; }
.op-count__num {
  font-size: calc(var(--step-4) * var(--count-size, 1));
  font-variation-settings: 'wght' 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--count-ink, var(--ink));
}
.op-count__name {
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.4;
}

/* --- Video ------------------------------------------------------------------------------ */

.op-video { margin-block: var(--sp-5); }
.op-video__frame { position: relative; aspect-ratio: var(--video-ratio, 16 / 9); }
.op-video__frame iframe,
.op-video__frame video,
.op-video__frame embed {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
  border: 0;
}
.op-video figcaption { margin-top: var(--sp-2); opacity: 0.5; }

/* --- Split ------------------------------------------------------------------------------- */

.op-split {
  display: grid;
  grid-template-columns: var(--split-cols, 1fr 1fr);
  gap: var(--split-gap, 0px);
  height: var(--split-h, 80vh);
  overflow: hidden;
}
.op-split__side { overflow: hidden; }
.op-split__side img {
  width: 100%;
  height: calc(100% + (var(--split-drift, 0.15) * 100%));
  object-fit: cover;
  display: block;
  will-change: transform;
}
@media (max-width: 767px) {
  .op-split { grid-template-columns: 1fr; height: var(--split-h-m, 50vh); }
  .op-split__side--r { display: var(--split-second-m, none); }
}

/* --- List ---------------------------------------------------------------------------------- */

.op-list {
  list-style: none;
  margin: var(--sp-4) 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(var(--list-cols, 1), minmax(0, 1fr));
  gap: var(--list-gap, 0.6rem) var(--sp-5);
}
@media (max-width: 767px) {
  .op-list { grid-template-columns: repeat(var(--list-cols-m, 1), minmax(0, 1fr)); }
}
.op-list__item {
  position: relative;
  padding-left: var(--list-indent, 1.6rem);
  font-size: calc(var(--step-0) * var(--list-size, 1));
}
.op-list__item::before {
  content: var(--list-marker, '—');
  position: absolute;
  left: 0;
  color: var(--list-marker-ink, var(--m));
}

/* --- Expandable ------------------------------------------------------------------------------- */

.op-more-block {
  border-top: 1px solid rgba(17,17,17,0.15);
  border-bottom: 1px solid rgba(17,17,17,0.15);
  margin-block: var(--sp-4);
}
.op-more-block__head {
  cursor: pointer;
  padding: var(--sp-3) 0;
  list-style: none;
  font-size: calc(var(--size--1) * var(--more-size, 1));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-variation-settings: 'wght' 600;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.op-more-block__head::-webkit-details-marker { display: none; }
.op-more-block__head::after { content: '+'; font-size: 1.3em; line-height: 1; }
.op-more-block[open] .op-more-block__head::after { content: '–'; }
.op-more-block__body { padding-bottom: var(--sp-3); opacity: 0.78; }

/* --- Type accents ------------------------------------------------------------------------------- */

.op-dropcap {
  float: left;
  font-size: calc(var(--step-5) * var(--dropcap-size, 1));
  line-height: 0.78;
  padding-right: var(--dropcap-gap, 0.12em);
  font-variation-settings: 'wght' var(--dropcap-weight, 800);
  color: var(--dropcap-ink, var(--ink));
}

.op-pull {
  font-size: calc(var(--step-2) * var(--pull-size, 1));
  line-height: 1.3;
  color: var(--pull-ink, var(--m));
  max-width: var(--pull-width, 22ch);
  margin-block: var(--sp-3);
  border-left: var(--pull-rule, 0) solid currentColor;
  padding-left: var(--pull-pad, 0);
}
@media (min-width: 900px) {
  .op-pull--right { float: right; margin-left: var(--sp-5); }
  .op-pull--left  { float: left;  margin-right: var(--sp-5); }
}

/* ==========================================================================
   42. CONTENT COMPONENTS

   Every value reads a custom property so a shortcode attribute and a
   Customizer default arrive at the same place. Column counts have tablet
   and phone variants throughout — a three-column row is unreadable at 375px
   and there is no single number that suits both.
   ========================================================================== */

/* --- C1 Animated list ---------------------------------------------------- */

.op-list ul, .op-list ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(var(--list-cols, 1), minmax(0, 1fr));
  gap: var(--list-gap, 0.6rem) var(--sp-5);
  text-align: var(--list-align, left);
  counter-reset: op-list;
}
@media (max-width: 767px) {
  .op-list ul, .op-list ol { grid-template-columns: repeat(var(--list-cols-m, 1), minmax(0, 1fr)); }
}

.op-list li {
  font-size: calc(var(--step-0) * var(--list-size, 1));
  padding-left: var(--list-indent, 1.6em);
  position: relative;
  line-height: 1.5;
}

.op-list--rule li::before {
  content: '';
  position: absolute;
  left: 0; top: 0.7em;
  width: var(--list-mark, 1em);
  height: 2px;
  background: var(--list-mark-ink, var(--m));
}
.op-list--dot li::before {
  content: '';
  position: absolute;
  left: 0.2em; top: 0.55em;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--list-mark-ink, var(--m));
}
.op-list--number li { counter-increment: op-list; }
.op-list--number li::before {
  content: counter(op-list, decimal-leading-zero);
  position: absolute;
  left: 0;
  color: var(--list-mark-ink, var(--m));
  font-size: 0.75em;
  letter-spacing: 0.1em;
}
.op-list--none li { padding-left: 0; }

/* --- C2 Steps ------------------------------------------------------------- */

.op-steps {
  display: grid;
  grid-template-columns: repeat(var(--steps-cols, 1), minmax(0, 1fr));
  gap: var(--steps-gap, 2rem);
  text-align: var(--steps-align, left);
  counter-reset: op-step;
}
@media (max-width: 767px) {
  .op-steps { grid-template-columns: repeat(var(--steps-cols-m, 1), minmax(0, 1fr)); }
}

.op-step {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--step-gap, 1.2rem);
  align-items: start;
  position: relative;
}

.op-step__num {
  font-size: calc(var(--step-2) * var(--step-num-size, 1));
  font-variation-settings: 'wght' var(--step-num-weight, 800);
  color: var(--step-num-ink, var(--m));
  line-height: 1;
  min-width: var(--step-num-w, 2.4ch);
}
.op-steps--nonum .op-step__num { display: none; }
.op-steps--nonum .op-step { grid-template-columns: 1fr; }

/* The connector only makes sense in a single column */
.op-steps:not(.op-steps--noline) .op-step:not(:last-child)::before {
  content: '';
  position: absolute;
  left: calc(var(--step-num-w, 2.4ch) / 2);
  top: 2.2em;
  bottom: calc(var(--steps-gap, 2rem) * -1 + 0.4em);
  width: var(--step-line, 1px);
  background: var(--step-line-ink, rgba(17, 17, 17, 0.18));
}
.op-steps[style*="--steps-cols:2"] .op-step::before,
.op-steps[style*="--steps-cols:3"] .op-step::before,
.op-steps[style*="--steps-cols:4"] .op-step::before { display: none; }

.op-step__title {
  font-size: calc(var(--step-1) * var(--step-title-size, 1));
  margin: 0 0 var(--sp-2);
  text-transform: var(--step-title-case, none);
}
.op-step__text p { margin: 0 0 0.6em; }

/* --- C3 Features ---------------------------------------------------------- */

.op-feats {
  display: grid;
  grid-template-columns: repeat(var(--feat-cols, 3), minmax(0, 1fr));
  gap: var(--feat-gap, 2.5rem);
  text-align: var(--feat-align, left);
}
@media (max-width: 1000px) { .op-feats { grid-template-columns: repeat(var(--feat-cols-t, 2), minmax(0, 1fr)); } }
@media (max-width: 600px)  { .op-feats { grid-template-columns: repeat(var(--feat-cols-m, 1), minmax(0, 1fr)); } }

.op-feats--side .op-feat { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-4); align-items: start; }

.op-feat__icon img {
  width: var(--feat-icon, 48px);
  height: var(--feat-icon, 48px);
  object-fit: contain;
  display: block;
  margin-bottom: var(--sp-3);
}
.op-feats--side .op-feat__icon img { margin-bottom: 0; }

.op-feat__title {
  font-size: calc(var(--step-1) * var(--feat-title-size, 1));
  margin: 0 0 var(--sp-2);
  text-transform: var(--feat-title-case, none);
}
.op-feat__text p { margin: 0 0 0.6em; opacity: var(--feat-text-dim, 0.75); }

/* --- C4 Facts -------------------------------------------------------------- */

.op-facts {
  display: grid;
  grid-template-columns: repeat(var(--facts-cols, 1), minmax(0, 1fr));
  gap: var(--facts-gap, 0);
  margin: 0;
}
@media (max-width: 767px) { .op-facts { grid-template-columns: repeat(var(--facts-cols-m, 1), minmax(0, 1fr)); } }

.op-facts__row {
  display: grid;
  grid-template-columns: var(--facts-label-w, 10rem) 1fr;
  gap: var(--sp-4);
  padding-block: var(--facts-pad, 0.9rem);
  border-bottom: var(--facts-rule, 1px) solid var(--facts-rule-ink, rgba(17, 17, 17, 0.14));
}
.op-facts--norule .op-facts__row { border-bottom: 0; }

@media (max-width: 600px) {
  .op-facts__row { grid-template-columns: 1fr; gap: var(--sp-1); }
}

.op-facts__label {
  font-size: calc(var(--size--1) * var(--facts-label-size, 1));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: var(--facts-label-dim, 0.45);
  margin: 0;
}
.op-facts__value { margin: 0; font-size: calc(var(--step-0) * var(--facts-value-size, 1)); }

/* --- C5 Cover boxes --------------------------------------------------------- */

.op-covers {
  display: grid;
  grid-template-columns: repeat(var(--cov-cols, 3), minmax(0, 1fr));
  gap: var(--cov-gap, 1.5rem);
}
@media (max-width: 1000px) { .op-covers { grid-template-columns: repeat(var(--cov-cols-t, 2), minmax(0, 1fr)); } }
@media (max-width: 600px)  { .op-covers { grid-template-columns: repeat(var(--cov-cols-m, 1), minmax(0, 1fr)); } }

.op-cover {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: var(--cov-ratio, 1 / 1);
  height: var(--cov-h, auto);
  background: var(--cov-bg, var(--ink));
  isolation: isolate;
}
@media (max-width: 767px) { .op-cover { height: var(--cov-h-m, auto); } }

.op-cover__img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  z-index: 1;
  transition: transform var(--cov-time, 0.5s) var(--ease-out),
              filter var(--cov-time, 0.5s) var(--ease-out);
}
.op-covers--zoom .op-cover:hover .op-cover__img { transform: scale(var(--cov-zoom, 1.08)); }

.op-cover__front,
.op-cover__back {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-content: center;
  padding: var(--sp-5);
  text-align: center;
  color: var(--cov-ink, var(--paper));
  transition: transform var(--cov-time, 0.5s) var(--ease-out),
              opacity var(--cov-time, 0.5s) var(--ease-out);
}

.op-cover__front {
  font-size: calc(var(--step-2) * var(--cov-title-size, 1));
  font-variation-settings: 'wght' 800;
  text-transform: var(--cov-title-case, uppercase);
  background: var(--cov-veil, rgba(17, 17, 17, 0.35));
}

.op-cover__back {
  background: var(--cov-back-bg, var(--m));
  gap: var(--sp-3);
}
.op-cover__back-title {
  font-size: calc(var(--step-2) * var(--cov-title-size, 1));
  font-variation-settings: 'wght' 800;
  text-transform: var(--cov-title-case, uppercase);
}
.op-cover__back-text { font-size: var(--step-0); opacity: 0.9; }

/* Slide is the default; the others are variants of the same two layers */
.op-covers--slide .op-cover__back  { transform: translateY(100%); }
.op-covers--slide .op-cover:hover .op-cover__back  { transform: translateY(0); }
.op-covers--slide .op-cover:hover .op-cover__front { transform: translateY(-100%); }

.op-covers--fade .op-cover__back { opacity: 0; }
.op-covers--fade .op-cover:hover .op-cover__back  { opacity: 1; }
.op-covers--fade .op-cover:hover .op-cover__front { opacity: 0; }

.op-covers--zoom .op-cover__back { opacity: 0; }
.op-covers--zoom .op-cover:hover .op-cover__back  { opacity: 1; }
.op-covers--zoom .op-cover:hover .op-cover__front { opacity: 0; }

@media (pointer: coarse) {
  .op-cover__back { transform: none; opacity: 1; background: transparent; }
  .op-cover__back-text { display: none; }
  .op-cover__front { opacity: 0; }
}

/* --- C6 Accordion ------------------------------------------------------------ */

.op-acc { display: grid; gap: var(--acc-gap, 0); }

.op-acc__item {
  border-bottom: var(--acc-rule, 1px) solid var(--acc-rule-ink, rgba(17, 17, 17, 0.16));
}
.op-acc--norule .op-acc__item { border-bottom: 0; }

.op-acc__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block: var(--acc-pad, 1.1rem);
  cursor: pointer;
  list-style: none;
}
.op-acc__head::-webkit-details-marker { display: none; }

.op-acc__title {
  font-size: calc(var(--step-1) * var(--acc-title-size, 1));
  font-variation-settings: 'wght' var(--acc-title-weight, 700);
  text-transform: var(--acc-title-case, none);
  color: var(--acc-title-ink, var(--ink));
  transition: color var(--dur-fast) var(--ease-out);
}
.op-acc__item[open] .op-acc__title,
.op-acc__head:hover .op-acc__title { color: var(--acc-active, var(--m)); }

.op-acc__mark {
  position: relative;
  width: var(--acc-mark, 16px);
  height: var(--acc-mark, 16px);
  flex: none;
}
.op-acc__mark span {
  position: absolute;
  background: var(--acc-mark-ink, var(--ink));
  transition: transform var(--dur-med) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.op-acc__mark span:nth-child(1) { top: 50%; left: 0; width: 100%; height: 2px; margin-top: -1px; }
.op-acc__mark span:nth-child(2) { left: 50%; top: 0; height: 100%; width: 2px; margin-left: -1px; }
.op-acc__item[open] .op-acc__mark span:nth-child(2) { transform: rotate(90deg); opacity: 0; }

.op-acc__body { padding-bottom: var(--acc-pad, 1.1rem); }
.op-acc__body p { margin: 0 0 0.7em; }

/* --- C7 Tabs -------------------------------------------------------------------- */

.op-tabs--top { display: grid; gap: var(--tabs-gap, 1.5rem); }
.op-tabs--side { display: grid; grid-template-columns: minmax(140px, var(--tabs-bar-w, 22%)) 1fr; gap: var(--tabs-gap, 2rem); }
@media (max-width: 767px) { .op-tabs--side { grid-template-columns: 1fr; } }

.op-tabs__bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tabs-btn-gap, 1.6rem);
  justify-content: var(--tabs-align, flex-start);
  border-bottom: var(--tabs-rule, 1px) solid var(--tabs-rule-ink, rgba(17, 17, 17, 0.14));
  padding-bottom: var(--sp-3);
}
.op-tabs--side .op-tabs__bar {
  flex-direction: column;
  border-bottom: 0;
  border-right: var(--tabs-rule, 1px) solid var(--tabs-rule-ink, rgba(17, 17, 17, 0.14));
  padding-bottom: 0;
  padding-right: var(--sp-4);
}

.op-tabs__btn {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-size: calc(var(--size--1) * var(--tabs-size, 1));
  letter-spacing: var(--tabs-track, 0.14em);
  text-transform: var(--tabs-case, uppercase);
  font-variation-settings: 'wght' var(--tabs-weight, 600);
  color: var(--tabs-ink, var(--ink));
  opacity: var(--tabs-dim, 0.45);
  position: relative;
  transition: opacity var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.op-tabs__btn:hover { opacity: 0.8; }
.op-tabs__btn.is-on { opacity: 1; color: var(--tabs-active, var(--ink)); }
.op-tabs__btn.is-on::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: calc(var(--sp-3) * -1 - 1px);
  height: var(--tabs-mark, 3px);
  background: var(--tabs-mark-ink, var(--m));
}
.op-tabs--side .op-tabs__btn.is-on::after { display: none; }

.op-tabs__panel { display: none; }
.op-tabs__panel.is-on { display: block; }

/* --- C8 Testimonials ------------------------------------------------------------- */

.op-quotes { position: relative; text-align: var(--quo-align, center); }
.op-quotes__viewport { overflow: hidden; }
.op-quotes__track { display: flex; transition: transform 600ms cubic-bezier(0.65, 0, 0.35, 1); }

.op-quote {
  flex: 0 0 100%;
  margin: 0;
  padding-inline: var(--quo-pad, 3rem);
  display: grid;
  gap: var(--sp-4);
  justify-items: var(--quo-align, center);
}

.op-quote__face {
  width: var(--quo-face, 64px);
  height: var(--quo-face, 64px);
  border-radius: 50%;
  object-fit: cover;
}

.op-quote__text {
  margin: 0;
  font-size: calc(var(--step-2) * var(--quo-size, 1));
  line-height: 1.35;
  max-width: var(--quo-width, 34ch);
  color: var(--quo-ink, var(--ink));
}
.op-quote__text p { margin: 0; }

.op-quote__by { display: grid; gap: 2px; }
.op-quote__name {
  font-size: calc(var(--size--1) * var(--quo-name-size, 1));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-variation-settings: 'wght' 600;
}
.op-quote__role { font-size: var(--step--1); opacity: 0.45; }

.op-quotes__nav {
  position: absolute;
  top: 45%;
  width: 40px; height: 40px;
  border: 2px solid var(--ink);
  border-radius: 50%;
  background: var(--paper);
  display: grid; place-items: center;
  cursor: pointer;
}
.op-quotes__nav--prev { left: 0; }
.op-quotes__nav--next { right: 0; }
.op-quotes__nav span { width: 9px; height: 9px; border-left: 2px solid var(--ink); border-bottom: 2px solid var(--ink); }
.op-quotes__nav--prev span { transform: rotate(45deg) translate(1px, -1px); }
.op-quotes__nav--next span { transform: rotate(-135deg) translate(1px, -1px); }

.op-quotes__dots { display: flex; justify-content: center; gap: 8px; margin-top: var(--sp-4); }
.op-quotes__dots button { width: 8px; height: 8px; border: 0; padding: 0; background: var(--ink); opacity: 0.25; cursor: pointer; }
.op-quotes__dots button.is-on { opacity: 1; background: var(--m); }

/* --- C9 Progress bars ------------------------------------------------------------- */

.op-bars {
  display: grid;
  grid-template-columns: repeat(var(--bars-cols, 1), minmax(0, 1fr));
  gap: var(--bars-gap, 1.4rem) var(--sp-6);
}
@media (max-width: 767px) { .op-bars { grid-template-columns: repeat(var(--bars-cols-m, 1), minmax(0, 1fr)); } }

.op-bar__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: var(--sp-2);
}
.op-bar__label {
  font-size: calc(var(--size--1) * var(--bar-label-size, 1));
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.op-bar__num { font-size: var(--step--1); opacity: 0.5; font-variant-numeric: tabular-nums; }

.op-bar__track {
  height: var(--bar-h, 6px);
  background: var(--bar-track, rgba(17, 17, 17, 0.12));
  border-radius: var(--bar-radius, 0);
  overflow: hidden;
}
.op-bar__fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--bar-fill, var(--m));
  border-radius: inherit;
  transition: width var(--bar-time, 1.1s) cubic-bezier(0.22, 1, 0.36, 1);
}
.op-bar.is-on .op-bar__fill { width: var(--bar-value, 0%); }

.op-bars--vertical { grid-auto-flow: column; align-items: end; }
.op-bars--vertical .op-bar { display: grid; justify-items: center; gap: var(--sp-2); }
.op-bars--vertical .op-bar__track { width: var(--bar-h, 8px); height: var(--bar-vh, 180px); }
.op-bars--vertical .op-bar__fill { width: 100%; height: 0; align-self: end; transition: height var(--bar-time, 1.1s) cubic-bezier(0.22, 1, 0.36, 1); }
.op-bars--vertical .op-bar.is-on .op-bar__fill { height: var(--bar-value, 0%); width: 100%; }
.op-bars--vertical .op-bar__head { flex-direction: column; align-items: center; }

/* --- C10 Pie ------------------------------------------------------------------------ */

.op-pie {
  display: grid;
  justify-items: center;
  gap: var(--sp-3);
  width: max-content;
  margin-inline: auto;
}

.op-pie__ring {
  width: var(--pie-size, 140px);
  height: var(--pie-size, 140px);
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: conic-gradient(
    var(--pie-fill, var(--m)) calc(var(--pie-now, 0) * 1%),
    var(--pie-track, rgba(17, 17, 17, 0.1)) 0
  );
  position: relative;
}
.op-pie__ring::after {
  content: '';
  position: absolute;
  inset: var(--pie-thick, 10px);
  border-radius: 50%;
  background: var(--pie-hole, var(--paper));
}
.op-pie__num {
  position: relative;
  z-index: 2;
  font-size: calc(var(--step-2) * var(--pie-num-size, 1));
  font-variation-settings: 'wght' 800;
  font-variant-numeric: tabular-nums;
}
.op-pie__label {
  font-size: calc(var(--size--1) * var(--pie-label-size, 1));
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.5;
}

/* --- C11 People ------------------------------------------------------------------------ */

.op-people {
  display: grid;
  grid-template-columns: repeat(var(--ppl-cols, 3), minmax(0, 1fr));
  gap: var(--ppl-gap, 2rem);
  text-align: var(--ppl-align, left);
}
@media (max-width: 1000px) { .op-people { grid-template-columns: repeat(var(--ppl-cols-t, 2), minmax(0, 1fr)); } }
@media (max-width: 600px)  { .op-people { grid-template-columns: repeat(var(--ppl-cols-m, 1), minmax(0, 1fr)); } }

.op-person__media { aspect-ratio: var(--ppl-ratio, 3 / 4); overflow: hidden; margin-bottom: var(--sp-3); background: var(--paper-warm, #e9e6df); }
.op-person__media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform var(--dur-slow) var(--ease-out); }
.op-person:hover .op-person__media img { transform: scale(var(--ppl-zoom, 1.04)); }

.op-person__name { font-size: calc(var(--step-1) * var(--ppl-name-size, 1)); margin: 0 0 2px; }
.op-person__role { margin: 0 0 var(--sp-2); font-size: var(--step--1); letter-spacing: 0.14em; text-transform: uppercase; opacity: 0.45; }
.op-person__bio p { margin: 0; font-size: var(--step--1); opacity: 0.75; }

/* --- C12 Pull quote and drop cap ---------------------------------------------------------- */

.op-pull {
  margin: var(--pull-my, 2.5rem) 0;
  padding-left: var(--pull-pad, 1.6rem);
  border-left: var(--pull-rule, 3px) solid var(--pull-rule-ink, var(--m));
  font-size: calc(var(--step-2) * var(--pull-size, 1));
  line-height: 1.3;
  text-align: var(--pull-align, left);
  max-width: var(--pull-width, 34ch);
}
.op-pull--norule { border-left: 0; padding-left: 0; }
.op-pull p { margin: 0; }

.op-dropcap {
  float: left;
  line-height: 0.82;
  margin: 0.05em 0.14em 0 0;
  font-size: calc(var(--step-5) * var(--cap-size, 1));
  font-variation-settings: 'wght' var(--cap-weight, 800);
  color: var(--cap-ink, var(--m));
}
.op-dropcap--box,
.op-dropcap--circle {
  display: grid;
  place-items: center;
  width: var(--cap-box, 2.4em);
  height: var(--cap-box, 2.4em);
  font-size: calc(var(--step-3) * var(--cap-size, 1));
  background: var(--cap-bg, var(--m));
  color: var(--cap-box-ink, var(--paper));
  margin-right: 0.4em;
  line-height: 1;
}
.op-dropcap--circle { border-radius: 50%; }

/* --- C13 Notice ----------------------------------------------------------------------------- */

.op-notice {
  display: flex;
  gap: var(--sp-4);
  align-items: flex-start;
  padding: var(--notice-pad, 1.1rem 1.4rem);
  margin-block: var(--notice-my, 1.6rem);
  border-left: var(--notice-rule, 4px) solid var(--notice-accent, var(--m));
  background: var(--notice-bg, rgba(17, 17, 17, 0.04));
  border-radius: var(--notice-radius, 0);
}
.op-notice--warn  { --notice-accent: #E8A33D; }
.op-notice--good  { --notice-accent: #3DA35D; }
.op-notice--plain { --notice-accent: rgba(17, 17, 17, 0.25); }

.op-notice__body { flex: 1; }
.op-notice__title { display: block; margin-bottom: 0.3em; }
.op-notice__body p { margin: 0 0 0.5em; font-size: calc(var(--step-0) * var(--notice-size, 1)); }
.op-notice__body p:last-child { margin-bottom: 0; }

.op-notice__close { position: relative; width: 20px; height: 20px; border: 0; background: none; cursor: pointer; flex: none; }
.op-notice__close span { position: absolute; top: 50%; left: 0; width: 100%; height: 2px; background: currentColor; opacity: 0.5; }
.op-notice__close span:nth-child(1) { transform: rotate(45deg); }
.op-notice__close span:nth-child(2) { transform: rotate(-45deg); }

/* --- C14 Countdown -------------------------------------------------------------------------- */

.op-countdown { display: grid; gap: var(--sp-3); justify-items: var(--cd-align, center); text-align: var(--cd-align, center); }

.op-countdown__label {
  font-size: calc(var(--size--1) * var(--cd-label-size, 1));
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.5;
}
.op-countdown__units { display: flex; gap: var(--cd-gap, 2rem); flex-wrap: wrap; justify-content: var(--cd-align, center); }
.op-countdown__unit { display: grid; gap: 4px; justify-items: center; }
.op-countdown__num {
  font-size: calc(var(--step-4) * var(--cd-size, 1));
  font-variation-settings: 'wght' 800;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--cd-ink, var(--ink));
}
.op-countdown__name { font-size: var(--step--1); letter-spacing: 0.14em; text-transform: uppercase; opacity: 0.4; }

@media (max-width: 600px) {
  .op-countdown__units { gap: var(--cd-gap-m, 1.2rem); }
  .op-countdown__num { font-size: calc(var(--step-3) * var(--cd-size-m, 1)); }
}

/* --- C15 Embed ------------------------------------------------------------------------------- */

.op-embed {
  width: var(--emb-w, 100%);
  margin-inline: auto;
  aspect-ratio: var(--emb-ratio, 16 / 9);
  overflow: hidden;
  border-radius: var(--emb-radius, 0);
  background: var(--ink);
}
.op-embed iframe,
.op-embed video,
.op-embed object { width: 100%; height: 100%; border: 0; display: block; }

/* --- C16 Split scroll -------------------------------------------------------------------------- */

.op-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--split-gap, 0px);
  height: var(--split-h, 100vh);
  overflow: hidden;
  position: relative;
}
@media (max-width: 767px) { .op-split { height: var(--split-h-m, 60vh); } }

.op-split__col { overflow: hidden; }
.op-split__col img { width: 100%; height: 100%; object-fit: cover; display: block; will-change: transform; }

.op-split__text {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  text-align: center;
  color: var(--split-ink, var(--paper));
  z-index: 3;
  pointer-events: none;
  padding: var(--sp-5);
}


/* ==========================================================================
   43. LEGACY CLASS ALIASES

   Pages written against the earlier block names keep their styling. The
   markup differs slightly, so these map the old hooks onto the new rules
   rather than duplicating the declarations.
   ========================================================================== */

.op-iconbox        { /* now .op-feat */ }
.op-iconbox__title { font-size: calc(var(--step-1) * var(--feat-title-size, 1)); margin: 0 0 var(--sp-2); }
.op-iconbox__text  { opacity: var(--feat-text-dim, 0.75); }
.op-iconbox__mark img { width: var(--feat-icon, 48px); height: var(--feat-icon, 48px); object-fit: contain; }

.op-note        { border-left: var(--notice-rule, 4px) solid var(--notice-accent, var(--m)); background: var(--notice-bg, rgba(17,17,17,0.04)); padding: var(--notice-pad, 1.1rem 1.4rem); margin-block: var(--notice-my, 1.6rem); }
.op-note__title { display: block; margin-bottom: 0.3em; }
.op-note__text p { margin: 0 0 0.5em; }

.op-video__frame { aspect-ratio: var(--video-ratio, var(--emb-ratio, 16 / 9)); overflow: hidden; }
.op-video__frame iframe { width: 100%; height: 100%; border: 0; }

.op-flip { aspect-ratio: var(--flip-ratio, var(--cov-ratio, 1 / 1)); height: var(--flip-h, var(--cov-h, auto)); }

/* Sub-classes the older markup emits that the newer rules do not name */
.op-acc__label    { font-size: calc(var(--step-1) * var(--acc-title-size, 1)); }
.op-flip__text    { font-size: var(--step-0); opacity: 0.9; }
.op-iconbox__body { display: grid; gap: var(--sp-2); }
.op-person__body  { display: grid; gap: 2px; }
.op-step__body    { display: grid; gap: var(--sp-2); }
.op-tabs__panels  { min-width: 0; }


/* ==========================================================================
   44. PAGE TITLE BAND

   A page with a featured image renders its heading inside the title band,
   which is otherwise a project-page component. Without this the heading on
   the Portfolio page silently answered to the Project page settings, which
   is not where anyone would look for it.

   Project pages keep the --titleband-* set; pages take the --pagetitle-*
   set, so each section of the Customizer governs what its name says.
   ========================================================================== */

.op-titleband--page .op-titleband__holder {
  justify-content: var(--pagetitle-x, flex-start);
  align-items: var(--pagetitle-y, center);
  padding-inline: var(--pagetitle-pad, 0);
}

.op-titleband--page .op-titleband__title {
  font-size: calc(var(--step-5) * var(--pagetitle-scale, 1)) !important;
  color: var(--pagetitle-ink, var(--ink)) !important;
  line-height: var(--pagetitle-lh, 1.05);
  max-width: var(--pagetitle-w, none);
  text-align: var(--pagetitle-align, left);
  text-transform: var(--pagetitle-case, none);
  font-variation-settings: 'wght' var(--pagetitle-weight, 800);
  letter-spacing: var(--pagetitle-track, -0.035em);
  transform: translate(var(--pagetitle-dx, 0), var(--pagetitle-dy, 0));
}

.op-titleband--page .op-titleband__sub {
  color: var(--pagetitle-ink, var(--ink));
  transform: translate(var(--pagetitle-dx, 0), var(--pagetitle-dy, 0));
}

.op-titleband--page { height: var(--pagetitle-band-h, var(--titleband-h, 240px)); }
.op-titleband--page .op-titleband__media::after {
  background: var(--pagetitle-veil, var(--titleband-veil, rgba(244, 242, 237, 0.72)));
}

@media (max-width: 767px) {
  .op-titleband--page { height: var(--pagetitle-band-h-m, var(--titleband-h-m, 160px)); }
  .op-titleband--page .op-titleband__title {
    font-size: calc(var(--step-5) * var(--pagetitle-scale, 1) * var(--pagetitle-scale-m, 1)) !important;
  }
}


/* ==========================================================================
   45. HEADER POSITION

   Sticky and overlay were bundled together, which meant turning one off
   took the other with it. They answer different questions, so they are now
   separate: whether the bar follows the scroll, and whether the opening
   image runs underneath it.

   These rules come last, so they win over section 30 without needing
   !important on the properties themselves.
   ========================================================================== */

/* --- Not sticky: the header scrolls away with everything else ---------- */

body.op-head-custom.op-head-static .ct-header:not(.ct-panel) ,
body.op-head-custom.op-head-static header#header ,
body.op-head-custom.op-head-static header.site-header {
  position: absolute;
}

/* Without a hero there is nothing to sit over, so it takes its own space */
body.op-head-custom.op-head-static:not(.op-has-hero) .ct-header:not(.ct-panel) ,
body.op-head-custom.op-head-static.op-head-inflow .ct-header:not(.ct-panel) {
  position: relative;
}

/* The background layer only belongs to the sticky version */
body.op-head-custom.op-head-static .ct-header:not(.ct-panel)::before { opacity: 0; }

body.op-head-custom.op-head-static:not(.op-has-hero) .ct-header:not(.ct-panel)::before ,
body.op-head-custom.op-head-static.op-head-inflow .ct-header:not(.ct-panel)::before { opacity: 1; }

/* Nothing to shrink into, so the bar keeps one height throughout */
body.op-head-custom.op-head-static .ct-header:not(.ct-panel) [data-row] {
  padding-block: var(--header-pad) !important;
}
body.op-head-custom.op-head-static .ct-header:not(.ct-panel) .site-logo-container img ,
body.op-head-custom.op-head-static .ct-header:not(.ct-panel) .ct-logo-container img {
  max-height: var(--logo-h) !important;
}

/* --- Not overlaying: the header gets a band of its own ----------------- */

body.op-head-custom.op-head-inflow .op-main,
body.op-head-custom.op-head-inflow #main-container { padding-top: 0; }

body.op-head-custom.op-head-inflow .ct-header:not(.ct-panel) {
  --header-ink: var(--ink);
}

/* Sticky but not overlaying still needs the content pushed clear */
body.op-head-custom.op-head-sticky.op-head-inflow .op-main,
body.op-head-custom.op-head-sticky.op-head-inflow #main-container {
  padding-top: var(--header-h, 100px);
}


/* ==========================================================================
   46. MENU PANEL

   Blocksy's off-canvas panel. Every other header rule in this file excludes
   it on purpose — it shares the ct-header class and pinning it to the top
   breaks the drawer. That exclusion also meant it was never styled, so this
   section covers it deliberately.

   The panel paints its own background on several nested elements, so those
   are cleared and one translucent layer is drawn behind everything instead.
   A solid colour would hide the page; a translucent one without blur makes
   the text hard to read against whatever is underneath.
   ========================================================================== */

body.op-head-custom.op-head-custom .ct-panel {
  --header-ink: var(--panel-ink, #fff);
  color: var(--panel-ink, #fff);
}

body.op-head-custom.op-head-custom .ct-panel ,
body.op-head-custom.op-head-custom .ct-panel .ct-panel-inner ,
body.op-head-custom.op-head-custom .ct-panel .ct-panel-content ,
body.op-head-custom.op-head-custom .ct-panel .ct-panel-actions ,
body.op-head-custom.op-head-custom .ct-panel [data-device] {
  background-color: transparent !important;
  background-image: none !important;
}

/* One layer behind the lot, so the blur applies once rather than per row */
body.op-head-custom.op-head-custom .ct-panel .ct-panel-inner {
  position: relative;
  isolation: isolate;
  width: var(--panel-w, 100%);
  margin-left: auto;
}

body.op-head-custom.op-head-custom .ct-panel .ct-panel-inner::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--panel-bg, rgba(17, 17, 17, 0.5));
  backdrop-filter: blur(var(--panel-blur, 14px));
  -webkit-backdrop-filter: blur(var(--panel-blur, 14px));
}

body.op-head-custom.op-head-custom .ct-panel .ct-panel-content {
  padding: var(--panel-pad, 2.5rem);
  display: flex;
  flex-direction: column;
  align-items: var(--panel-align, flex-start);
  gap: var(--panel-gap, 0.6rem);
}

/* --- Menu items -------------------------------------------------------- */

body.op-head-custom.op-head-custom .ct-panel .menu ,
body.op-head-custom.op-head-custom .ct-panel ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: var(--panel-align, flex-start);
  gap: var(--panel-gap, 0.6rem);
  width: 100%;
}

body.op-head-custom.op-head-custom .ct-panel a ,
body.op-head-custom.op-head-custom .ct-panel .ct-menu-link {
  color: var(--panel-ink, #fff);
  font-size: var(--panel-size, 1.6rem);
  font-variation-settings: 'wght' var(--panel-weight, 700);
  letter-spacing: var(--panel-track, 0.02em);
  text-transform: var(--panel-case, uppercase);
  line-height: 1.2;
  transition: color var(--dur-fast) var(--ease-out);
}

body.op-head-custom.op-head-custom .ct-panel a:hover ,
body.op-head-custom.op-head-custom .ct-panel .ct-menu-link:hover ,
body.op-head-custom.op-head-custom .ct-panel .current-menu-item > a {
  color: var(--panel-hover, var(--m));
}

/* Sub-items sit smaller so the hierarchy is visible without indent guides */
body.op-head-custom .ct-panel .sub-menu a { font-size: calc(var(--panel-size, 1.6rem) * 0.6); opacity: 0.75; }

/* --- Close button -------------------------------------------------------- */

body.op-head-custom.op-head-custom .ct-panel .ct-toggle-close ,
body.op-head-custom.op-head-custom .ct-panel button[data-toggle-panel] {
  color: var(--panel-close-ink, #fff);
  width: var(--panel-close, 26px);
  height: var(--panel-close, 26px);
}
body.op-head-custom.op-head-custom .ct-panel .ct-toggle-close svg ,
body.op-head-custom .ct-panel .ct-toggle-close .ct-icon { fill: currentColor; color: currentColor; }

@media (max-width: 767px) {
  body.op-head-custom .ct-panel .ct-panel-inner { width: 100%; }
  body.op-head-custom.op-head-custom .ct-panel a ,
  body.op-head-custom .ct-panel .ct-menu-link { font-size: var(--panel-size-m, 1.3rem); }
}

/* Blur is not universal; without it a translucent panel over a busy image
   is unreadable, so the fallback leans opaque. */
@supports not (backdrop-filter: blur(1px)) {
  body.op-head-custom .ct-panel .ct-panel-inner::before { background: var(--panel-bg-solid, rgba(17, 17, 17, 0.92)); }
}


/* ==========================================================================
   47. PANEL AND HEADER — who sits on top

   The header is pinned with a z-index of its own, so when the drawer opens
   the header's own button stays visible above it. That gives two close
   icons: the header's, and the panel's. It also drops the header button on
   top of the panel's label.

   The panel wins, and the header steps out of the way while it is open.

   Blocksy has used several markers for the open state across versions, so
   all the ones it is known to use are covered.
   ========================================================================== */

body.op-head-custom .ct-panel {
  z-index: 200 !important;
}

/* Header out of the way while the drawer is open */
body.op-head-custom.op-panel-open .ct-header:not(.ct-panel) {
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms var(--ease-out);
}

/* --- The panel's own label ---------------------------------------------- */

body.op-head-custom .ct-panel .ct-panel-heading ,
body.op-head-custom .ct-panel .ct-panel-title ,
body.op-head-custom .ct-panel [class*="panel-label"] {
  display: var(--panel-label, none);
  color: var(--panel-ink, #fff);
  font-size: calc(var(--size--1) * var(--panel-label-size, 1));
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.5;
  margin: 0 0 var(--sp-4);
}

/* --- Close button, positioned rather than wherever it landed ------------ */

body.op-head-custom .ct-panel .ct-panel-actions ,
body.op-head-custom .ct-panel .ct-toggle-close {
  position: absolute;
  top: var(--panel-close-y, 1.6rem);
  right: var(--panel-close-x, 1.6rem);
  left: auto;
  z-index: 3;
}

/* When the close button is asked to sit on the left, the label moves clear */
body.op-head-custom.op-panel-close-left .ct-panel .ct-panel-actions ,
body.op-head-custom.op-panel-close-left .ct-panel .ct-toggle-close {
  right: auto;
  left: var(--panel-close-x, 1.6rem);
}
body.op-head-custom.op-panel-close-left .ct-panel .ct-panel-content { padding-top: calc(var(--panel-pad, 2.5rem) + 2rem); }


/* ==========================================================================
   0. 你可以自己改嘅設定  /  USER SETTINGS

   呢一段係全部你會想調嘅數值。改完存檔，瀏覽器 Cmd+Shift+R 就見到。
   唔好改呢段以外嘅嘢，除非你知道自己做緊乜。
   ========================================================================== */




/* ==========================================================================
   47. OVERLAY SCOPE AND PANEL TIDYING

   Two menus doing the same job at the same width is the problem here: the
   theme's slide-in panel and the overlay both answer a button, so both draw
   a close control and both label themselves. Whichever one is chosen at a
   given width, the other stands down completely.
   ========================================================================== */

/* --- Where the overlay appears ----------------------------------------- */

.op-ov--narrow,
.op-ov--phone { display: none; }

@media (max-width: 1000px) {
  .op-ov__open.op-ov--narrow { display: grid; }
  .op-ov.op-ov--narrow       { display: grid; }
}

@media (max-width: 767px) {
  .op-ov__open.op-ov--phone { display: grid; }
  .op-ov.op-ov--phone       { display: grid; }
}

/* Where the overlay is in charge, the theme's own trigger and panel go away
   rather than sitting behind it with a second close button. */
body.op-ov-on .ct-header:not(.ct-panel) [data-id="trigger"],
body.op-ov-on .ct-header:not(.ct-panel) .ct-header-trigger,
body.op-head-custom.op-ov-on .ct-panel { display: none !important; }

@media (max-width: 1000px) {
  body.op-ov-narrow .ct-header:not(.ct-panel) [data-id="trigger"],
  body.op-ov-narrow .ct-header:not(.ct-panel) .ct-header-trigger,
  body.op-head-custom.op-ov-narrow .ct-panel { display: none !important; }
}

@media (max-width: 767px) {
  body.op-ov-phone .ct-header:not(.ct-panel) [data-id="trigger"],
  body.op-ov-phone .ct-header:not(.ct-panel) .ct-header-trigger,
  body.op-head-custom.op-ov-phone .ct-panel { display: none !important; }
}

/* --- Panel tidying ------------------------------------------------------- */

/* The theme prints a heading at the top of the panel. It lands under the
   close button, so it is hidden unless asked for. */
body.op-head-custom .ct-panel .ct-panel-actions > *:not(.ct-toggle-close):not(button),
body.op-head-custom .ct-panel .ct-panel-heading,
body.op-head-custom .ct-panel .ct-panel-title { display: var(--panel-label-display, none); }

/* Only one close control. A trigger cloned into the panel gives a second. */
body.op-head-custom .ct-panel [data-id="trigger"],
body.op-head-custom .ct-panel .ct-header-trigger { display: none !important; }

body.op-head-custom .ct-panel .ct-panel-actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  padding: var(--panel-pad, 2.5rem) var(--panel-pad, 2.5rem) 0;
}
