/* ═══════════════════════════════════════════════════════════
   ON CALL EVENTS — MIDDLE CSS (3D cloth hero, marquee, about, Core Collection, Linen Library, Gallery, contact)
   WordPress (WP Coder): paste this whole file into a CSS snippet.
   Do NOT add <style> tags — WP Coder adds them for you.
   ═══════════════════════════════════════════════════════════ */

/* base tokens (duplicated per part on purpose — harmless in WP) */
.oce {
  --oce-ivory: #faf7f0; --oce-ivory2: #f1ecdf; --oce-ink: #131110; --oce-ink2: #221e19;
  --oce-soft: #57503f; --oce-gold: #b98a2e; --oce-gold2: #dcb96e; --oce-champagne: #f2dcae;
  --oce-line: rgba(19,17,16,0.1);
  --oce-gold-ink: #8a6519; --oce-gold-head: #a1761f;
  --oce-serif: "Cormorant Garamond", Georgia, serif;
  --oce-sans: "Outfit", -apple-system, "Segoe UI", sans-serif;
  --oce-ease: cubic-bezier(0.22, 1, 0.36, 1);
  font-family: var(--oce-sans); font-weight: 300; color: var(--oce-ink);
  line-height: 1.65; font-size: 16px; -webkit-font-smoothing: antialiased;
}
.oce-m *, .oce-m *::before, .oce-m *::after { margin: 0; padding: 0; box-sizing: border-box; }
.oce h1, .oce h2, .oce h3 { font-family: var(--oce-serif); font-weight: 600; line-height: 1.06; letter-spacing: -0.01em; }
.oce h1 em, .oce h2 em { font-style: italic; color: var(--oce-gold-head); }
.oce a { color: inherit; text-decoration: none; }
.oce button { font-family: inherit; cursor: pointer; }
.oce ::selection { background: var(--oce-gold2); color: var(--oce-ink); }
.oce .oce-kicker {
  font-size: 0.7rem; letter-spacing: 0.34em; text-transform: uppercase;
  color: var(--oce-gold-ink); font-weight: 600; display: inline-flex; align-items: center; gap: 0.9rem;
}
.oce .oce-kicker::before { content: ""; width: 2.2rem; height: 1px; background: currentColor; opacity: 0.5; }
.oce .oce-btn {
  position: relative; display: inline-block; padding: 1rem 2.6rem; border-radius: 100px;
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
  border: 1px solid transparent; overflow: hidden; will-change: transform;
  transition: transform 0.5s var(--oce-ease), box-shadow 0.5s var(--oce-ease), background 0.3s, color 0.3s;
}
.oce .oce-btn--gold {
  background: linear-gradient(120deg, var(--oce-gold) 0%, var(--oce-gold2) 55%, var(--oce-gold) 100%);
  background-size: 200% 100%; color: #fff; box-shadow: 0 14px 34px -12px rgba(185,138,46,0.6);
}
.oce .oce-btn--gold:hover { background-position: 100% 0; box-shadow: 0 20px 44px -12px rgba(185,138,46,0.72); }
.oce .oce-reveal { opacity: 0; transform: translateY(34px); transition: opacity 1s var(--oce-ease), transform 1s var(--oce-ease); }
.oce .oce-reveal.oce-in { opacity: 1; transform: none; }

.oce .oce-btn--ghost { border-color: rgba(250,247,240,0.4); color: var(--oce-ivory); }
.oce .oce-btn--ghost:hover { background: var(--oce-ivory); color: var(--oce-ink); border-color: var(--oce-ivory); }

.oce .oce-kicker--light { color: var(--oce-gold2); }
.oce .oce-btn--full { width: 100%; border: 0; }

/* ── Hero with 3D cloth canvas ────────────────────────────── */
.oce-hero {
  position: relative; min-height: 100svh; display: flex; align-items: center; justify-content: center;
  text-align: center; color: var(--oce-ivory); overflow: hidden;
  touch-action: pan-y; /* touch: vertical swipes scroll, horizontal drags play with the cloth */
  -webkit-user-select: none; user-select: none; /* dragging the silk shouldn't select the headline */
  background:
    radial-gradient(1100px 520px at 72% -6%, rgba(185, 138, 46, 0.16), transparent 60%),
    linear-gradient(165deg, #1a1712 0%, #262019 48%, #131110 100%);
}
/* the canvas belongs to the hero and scrolls away with it, so the gold silk
   stays at the top of the page; it ignores the pointer entirely (content
   stays clickable) */
.oce-hero__canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; z-index: 5; pointer-events: none; }
.oce-hero__veil {
  position: absolute; inset: 0; pointer-events: none; z-index: 6;
  background: radial-gradient(60% 55% at 50% 46%, rgba(19,17,16,0.55), rgba(19,17,16,0.05) 62%, rgba(19,17,16,0.35) 100%);
}
.oce-hero__grain {
  position: absolute; inset: 0; pointer-events: none; z-index: 7; opacity: 0.05; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.oce-hero__inner { position: relative; z-index: 10; max-width: 62rem; padding: 7.5rem 1.5rem 4.5rem; }
.oce-hero__eyebrow {
  font-size: 0.7rem; letter-spacing: 0.42em; text-transform: uppercase;
  color: var(--oce-gold2); font-weight: 500; margin-bottom: 1.8rem;
  opacity: 0; animation: oceFade 1.4s var(--oce-ease) 0.35s forwards;
}
.oce-hero__title { font-size: clamp(3.1rem, 9vw, 6.9rem); font-weight: 500; }
.oce-hero__line { display: block; overflow: hidden; padding-bottom: 0.22em; margin-bottom: -0.22em; }
.oce-hero__line > span { display: inline-block; transform: translateY(112%); animation: oceRise 1.2s var(--oce-ease) forwards; }
.oce-hero__line:nth-child(2) > span { animation-delay: 0.16s; }
.oce-hero__accent {
  font-style: italic;
  background: linear-gradient(115deg, var(--oce-gold2) 10%, var(--oce-champagne) 45%, var(--oce-gold2) 90%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.oce-hero__sub {
  max-width: 37rem; margin: 1.9rem auto 2.8rem; color: rgba(250, 247, 240, 0.72);
  font-size: 1.06rem; font-weight: 300;
  opacity: 0; animation: oceFade 1.4s var(--oce-ease) 0.6s forwards;
}
.oce-hero__actions {
  display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;
  opacity: 0; animation: oceFade 1.4s var(--oce-ease) 0.8s forwards;
}
.oce-hero__scroll {
  position: absolute; bottom: 2rem; left: 50%; transform: translateX(-50%); z-index: 10;
  width: 1.4rem; height: 2.4rem; border: 1px solid rgba(250,247,240,0.35); border-radius: 100px;
}
.oce-hero__scroll span {
  position: absolute; top: 0.45rem; left: 50%; width: 3px; height: 6px; margin-left: -1.5px;
  border-radius: 3px; background: var(--oce-gold2); animation: oceScrollDot 1.9s ease-in-out infinite;
}
@keyframes oceRise { to { transform: translateY(0); } }
@keyframes oceFade { to { opacity: 1; } }
@keyframes oceScrollDot { 0% { transform: translateY(0); opacity: 1; } 70% { transform: translateY(0.85rem); opacity: 0; } 100% { opacity: 0; } }

/* ── Marquee divider ─────────────────────────────────────── */
.oce-marquee {
  background: var(--oce-ink); color: rgba(250,247,240,0.82); overflow: hidden;
  padding: 0.95rem 0; font-family: var(--oce-serif); font-style: italic;
  font-size: 1.08rem; letter-spacing: 0.08em;
  border-top: 1px solid rgba(220,185,110,0.25); border-bottom: 1px solid rgba(220,185,110,0.25);
}
.oce-marquee__track { display: flex; width: max-content; animation: oceMarquee 38s linear infinite; }
.oce-marquee__track span { white-space: nowrap; }
@keyframes oceMarquee { to { transform: translateX(-50%); } }

/* ── Statement / about ───────────────────────────────────── */
.oce-statement { background: var(--oce-ivory); padding: clamp(5.5rem, 11vw, 9rem) clamp(1.25rem, 6vw, 6rem); }
.oce-statement__grid { max-width: 1360px; margin: 0 auto; display: grid; grid-template-columns: 1.25fr 1fr; gap: clamp(2.5rem, 6vw, 6rem); align-items: start; }
.oce-statement h2 { font-size: clamp(2.6rem, 5.4vw, 4.4rem); margin-top: 1.4rem; }
.oce-statement__copy { padding-top: 1rem; }
.oce-statement__copy p { color: var(--oce-soft); margin-bottom: 1.2rem; max-width: 30rem; }
.oce-statement__copy strong { font-weight: 500; color: var(--oce-ink); }
.oce-stats { max-width: 1360px; margin: clamp(3rem, 7vw, 5.5rem) auto 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem; border-top: 1px solid var(--oce-line); padding-top: 2.6rem; text-align: left; }
/* #a1761f: the base gold is 2.83:1 on ivory, which misses even the 3:1 that
   large text is allowed. These four numbers are the biggest gold on the page. */
.oce-stat__num { font-family: var(--oce-serif); font-size: clamp(2.6rem, 5vw, 3.8rem); font-weight: 600; color: var(--oce-gold-head); display: block; line-height: 1; }
.oce-stat__label { font-size: 0.7rem; letter-spacing: 0.26em; text-transform: uppercase; color: var(--oce-soft); }

/* ── Core collection (dark band) ─────────────────────────── */
.oce-core {
  background:
    radial-gradient(900px 460px at 85% 0%, rgba(185,138,46,0.14), transparent 60%),
    linear-gradient(170deg, #191612, #24201a 55%, #131110);
  color: var(--oce-ivory);
  padding: clamp(5.5rem, 11vw, 9rem) clamp(1.25rem, 6vw, 6rem);
}
.oce-core__head { max-width: 1360px; margin: 0 auto clamp(2.8rem, 6vw, 4.5rem); display: flex; justify-content: space-between; align-items: flex-end; gap: 2rem; flex-wrap: wrap; }
.oce-core__head h2 { color: var(--oce-ivory); font-size: clamp(2.6rem, 5.4vw, 4.4rem); margin-top: 1.4rem; }
.oce-core__head h2 em { color: var(--oce-gold2); }
.oce-core__lede { color: rgba(250,247,240,0.65); max-width: 24rem; padding-bottom: 0.6rem; }
/* desktop: 4 columns — four singles on top, two double-wide tents below (4×2);
   tablet / half screen: 2 columns, every card single (3 rows of 2);
   mobile: 1 column */
.oce-core__grid { max-width: 1360px; margin: 0 auto; display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.3rem; }
.oce-piece--wide { grid-column: span 2; }
@media (max-width: 1160px) {
  .oce-core__grid { grid-template-columns: repeat(2, 1fr); }
  .oce-piece--wide { grid-column: auto; }
}
@media (max-width: 640px) {
  .oce-core__grid { grid-template-columns: 1fr; }
}
/* wide tent cards get an editorial side-by-side layout on desktop */
@media (min-width: 1161px) {
  .oce-piece--wide { display: grid; grid-template-columns: 250px 1fr; column-gap: 2.4rem; align-items: start; }
  .oce-piece--wide .oce-piece__art { grid-row: 1 / span 4; height: 140px; margin: 0; align-self: center; justify-self: center; }
}
.oce-piece {
  position: relative; border: 1px solid rgba(250,247,240,0.12); border-radius: 22px;
  padding: 2.2rem 2rem 2rem; background: rgba(255,255,255,0.03); overflow: hidden;
  transition: transform 0.15s ease-out, border-color 0.4s, background 0.4s, box-shadow 0.5s var(--oce-ease);
  transform-style: preserve-3d; will-change: transform;
}
.oce-piece:hover { border-color: rgba(220,185,110,0.5); background: rgba(255,255,255,0.05); box-shadow: 0 30px 70px -30px rgba(0,0,0,0.7); }
.oce-piece__index {
  position: absolute; top: 1.5rem; right: 1.7rem;
  font-family: var(--oce-serif); font-style: italic; font-size: 1rem; color: rgba(220,185,110,0.55);
}
.oce-piece__art { height: 96px; margin-bottom: 1.4rem; display: flex; align-items: flex-end; }
.oce-piece__art svg { height: 100%; width: auto; overflow: visible; }
.oce-piece__art path, .oce-piece__art ellipse, .oce-piece__art line, .oce-piece__art rect {
  fill: none; stroke: var(--oce-gold2); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 400; stroke-dashoffset: 400;
}
.oce-piece.oce-in path, .oce-piece.oce-in ellipse, .oce-piece.oce-in line, .oce-piece.oce-in rect {
  animation: oceDraw 2.2s var(--oce-ease) forwards;
}
@keyframes oceDraw { to { stroke-dashoffset: 0; } }
.oce-piece h3 { font-size: 1.65rem; color: var(--oce-ivory); margin-bottom: 0.35rem; }
.oce-piece__spec { font-size: 0.72rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--oce-gold2); font-weight: 600; margin-bottom: 0.8rem; }
.oce-piece p { font-size: 0.92rem; color: rgba(250,247,240,0.62); }
.oce-piece__pair {
  margin-top: 1.2rem; padding-top: 1rem; border-top: 1px solid rgba(250,247,240,0.1);
  font-size: 0.78rem; color: rgba(250,247,240,0.66); display: flex; align-items: center; gap: 0.5rem;
}
.oce-piece__pair::before { content: "\2726"; color: var(--oce-gold2); font-size: 0.65rem; }
/* ── "How many do I need?" guide ── */
.oce-guide {
  max-width: 1360px; margin: clamp(2.5rem, 5vw, 3.5rem) auto 0;
  border: 1px solid rgba(250,247,240,0.14); border-radius: 22px;
  background: rgba(255,255,255,0.03); padding: clamp(1.5rem, 3.5vw, 2.4rem);
}
.oce-guide__title { font-size: clamp(1.6rem, 3vw, 2.2rem); color: var(--oce-ivory); }
.oce-guide__title em { font-style: italic; color: var(--oce-gold2); }
.oce-guide__lede { color: rgba(250,247,240,0.66); max-width: 40rem; margin-top: 0.7rem; }
/* four columns stop fitting on a narrow phone; let the table scroll rather
   than squeezing the numbers into two lines each */
.oce-guide__scroll { overflow-x: auto; margin: 1.5rem 0 1.1rem; }
.oce-guide__table { width: 100%; min-width: 30rem; border-collapse: collapse; }
.oce-guide__table th, .oce-guide__table td { text-align: left; padding: 0.8rem 0.9rem; }
.oce-guide__table thead th {
  font-family: var(--oce-sans); font-size: 0.64rem; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--oce-gold2); font-weight: 600;
  border-bottom: 1px solid rgba(250,247,240,0.18); white-space: nowrap;
  vertical-align: bottom;
}
/* the seats-per-table note rides under its column heading */
.oce-guide__table thead th span {
  display: block; margin-top: 0.3rem; font-size: 0.6rem; letter-spacing: 0.12em;
  color: rgba(250,247,240,0.5); font-weight: 400; text-transform: none;
}
.oce-guide__table tbody th {
  font-family: var(--oce-serif); font-size: 1.55rem; font-weight: 600;
  color: var(--oce-gold2); line-height: 1;
}
.oce-guide__table tbody td { color: rgba(250,247,240,0.84); font-size: 0.95rem; white-space: nowrap; }
.oce-guide__table tbody tr + tr th, .oce-guide__table tbody tr + tr td {
  border-top: 1px solid rgba(250,247,240,0.1);
}
.oce-guide__note { color: rgba(250,247,240,0.6); font-size: 0.88rem; max-width: 46rem; }

.oce-core__cta { max-width: 1360px; margin: clamp(2.5rem, 5vw, 3.5rem) auto 0; text-align: center; }

/* ── Linen library ───────────────────────────────────────── */
.oce-linens { background: var(--oce-ivory); padding: clamp(5.5rem, 11vw, 9rem) clamp(1.25rem, 6vw, 6rem); }
/* The Linen Library is the page's one asymmetric band. Every other section
   opens with a centred or split head at the same size; this one runs left,
   larger, with the lede directly under it — so a visitor scrolling past knows
   they have arrived somewhere different. It is also the section that carries
   the thing no competitor can copy, so it is worth the emphasis. */
.oce-linens__head { max-width: 1360px; margin: 0 auto clamp(2rem, 4vw, 2.8rem); text-align: left; }
.oce-linens__head h2 { font-size: clamp(3rem, 7vw, 5.6rem); margin-top: 1.4rem; max-width: 14ch; }
.oce-linens__sub { color: var(--oce-soft); max-width: 34rem; margin: 1.4rem 0 0; font-size: 1.02rem; }
.oce-filters { display: flex; flex-wrap: wrap; gap: 0.6rem; justify-content: center; margin: 0 auto 2.6rem; max-width: 1360px; }

/* Text filters, not pills. The Gallery below keeps the pill row; these two
   sections used to be indistinguishable from one another. */
.oce-filters--text { justify-content: flex-start; gap: 0.35rem 1.9rem; margin-bottom: 2rem; }
.oce-filters--text .oce-filter {
  border: 0; border-radius: 0; padding: 0.6rem 0; background: none;
  font-size: 0.95rem; letter-spacing: 0.02em; color: var(--oce-soft);
  position: relative;
}
.oce-filters--text .oce-filter::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0.45rem; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: right;
  transition: transform 0.4s var(--oce-ease);
}
.oce-filters--text .oce-filter:hover { color: var(--oce-gold-ink); transform: none; border-color: transparent; }
.oce-filters--text .oce-filter:hover::after { transform: scaleX(1); transform-origin: left; }
.oce-filters--text .oce-filter.oce-active {
  background: none; color: var(--oce-ink); border-color: transparent; font-weight: 600;
}
.oce-filters--text .oce-filter.oce-active::after { transform: scaleX(1); background: var(--oce-gold); }
/* ── Shape language ──
   The pill is reserved for committing actions — the things that send, submit
   or take you somewhere. A filter is not one of those: it changes what you are
   looking at and you can change it straight back. It gets a soft-cornered
   rectangle instead, which reads as one of a set rather than as a button.
   See DESIGN.md, The Shape Tells You What It Does. */
.oce-filter {
  /* 44px is the smallest reliable finger target; these were 38px tall */
  min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  padding: 0.62rem 1.35rem; border-radius: 6px; border: 1px solid var(--oce-line);
  background: transparent; color: var(--oce-soft); font-size: 0.79rem; font-weight: 500; letter-spacing: 0.06em;
  transition: background 0.3s, color 0.3s, border-color 0.3s, transform 0.3s var(--oce-ease);
}
.oce-filter:hover { border-color: var(--oce-gold); color: var(--oce-gold); transform: translateY(-2px); }
.oce-filter.oce-active { background: var(--oce-ink); color: var(--oce-ivory); border-color: var(--oce-ink); }
.oce-linens__grid { max-width: 1360px; margin: 0 auto; display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.3rem; }
.oce-linen {
  background: #fff; border: 1px solid var(--oce-line); border-radius: 20px; overflow: hidden;
  transition: transform 0.5s var(--oce-ease), box-shadow 0.5s var(--oce-ease);
}
.oce-linen:hover { transform: translateY(-6px); box-shadow: 0 26px 60px -26px rgba(19,17,16,0.28); }
.oce-linen.oce-hidden { display: none; }
.oce-linen__head { padding: 1.5rem 1.6rem 0.85rem; display: flex; justify-content: space-between; align-items: baseline; gap: 0.75rem; }
.oce-linen__head h3 { font-size: 1.35rem; }
/* Metadata, not a control. This was a filled pill sitting directly under a row
   of filter pills that said the same words, so visitors tapped it and nothing
   happened. It has no container at all now — it is a caption, and it should
   look like one. The colour moves to #8a6519 as well: the base gold reads
   2.83:1 on white, under the 4.5:1 small text needs. */
.oce-linen__tag {
  font-size: 0.62rem; letter-spacing: 0.2em; text-transform: uppercase; font-weight: 600;
  color: var(--oce-gold-ink); background: none; padding: 0; border-radius: 0; white-space: nowrap;
}
.oce-linen__body { padding: 0 1.6rem 1.6rem; }
.oce-linen__desc { font-size: 0.88rem; color: var(--oce-soft); margin-bottom: 1rem; }
.oce-swatches { display: flex; flex-wrap: wrap; gap: 0.55rem; }
.oce-swatch { position: relative; display: grid; place-items: center; }
.oce-swatch__dot {
  width: 2.15rem; height: 2.15rem; border-radius: 50%; border: 2px solid #fff;
  box-shadow: 0 0 0 1px var(--oce-line), 0 3px 8px rgba(19,17,16,0.15);
  transition: transform 0.3s var(--oce-ease);
}
.oce-swatch:hover .oce-swatch__dot { transform: scale(1.3); }
.oce-swatch__tip {
  position: absolute; bottom: calc(100% + 0.55rem); left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: var(--oce-ink); color: var(--oce-ivory); font-size: 0.67rem; font-weight: 500;
  letter-spacing: 0.04em; padding: 0.35rem 0.7rem; border-radius: 8px; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity 0.25s, transform 0.25s var(--oce-ease); z-index: 5;
}
.oce-swatch__tip::after { content: ""; position: absolute; top: 100%; left: 50%; margin-left: -4px; border: 4px solid transparent; border-top-color: var(--oce-ink); }
.oce-swatch:hover .oce-swatch__tip, .oce-swatch:focus-within .oce-swatch__tip { opacity: 1; transform: translateX(-50%) translateY(0); }
.oce-list { list-style: none; display: grid; gap: 0.45rem; }
.oce-list li { display: flex; justify-content: space-between; align-items: center; gap: 1rem; font-size: 0.89rem; padding: 0.55rem 0.9rem; border-radius: 10px; background: var(--oce-ivory); }
/* #8a6519, not the base gold: small text needs 4.5:1 and #b98a2e gives 2.83:1 */
.oce-list .oce-qty { font-size: 0.67rem; font-weight: 600; letter-spacing: 0.08em; color: var(--oce-gold-ink); white-space: nowrap; }

/* ── Gallery ─────────────────────────────────────────────── */
.oce-gallery { background: var(--oce-ivory2); padding: clamp(5.5rem, 11vw, 9rem) clamp(1.25rem, 6vw, 6rem); }
.oce-gallery__head { max-width: 1360px; margin: 0 auto clamp(2.1rem, 4.4vw, 3.1rem); text-align: center; }
.oce-gallery__head h2 { font-size: clamp(2.6rem, 5.4vw, 4.4rem); margin-top: 1.4rem; }
.oce-gallery__sub { color: var(--oce-soft); max-width: 38rem; margin: 1.1rem auto 0; }
.oce-gfilters { display: flex; flex-wrap: wrap; gap: 0.6rem; justify-content: center; margin: 0 auto 2.4rem; max-width: 1360px; }
.oce-gallery__note { max-width: 1360px; margin: 1.6rem auto 0; text-align: center; color: var(--oce-soft); font-size: 0.9rem; }
.oce-gallery__note:empty { display: none; }

/* The grid is composed, not tiled: the script splits the photos into rows that
   always add up to 12 columns, so no row is ever left ragged — whatever the
   photo count is, and whichever filter is on. */
.oce-gallery__grid {
  max-width: 1360px; margin: 0 auto; display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-auto-rows: clamp(4rem, 5.6vw, 5.5rem);
  gap: clamp(0.55rem, 0.9vw, 1.1rem);
}
.oce-shot--w12 { grid-column: span 12; grid-row: span 5; }
.oce-shot--w7  { grid-column: span 7;  grid-row: span 5; }
.oce-shot--w6  { grid-column: span 6;  grid-row: span 4; }
.oce-shot--w5  { grid-column: span 5;  grid-row: span 5; }
.oce-shot--w4  { grid-column: span 4;  grid-row: span 4; }

.oce-shot {
  position: relative; display: block; width: 100%; height: 100%; padding: 0;
  border: 0; border-radius: 20px; overflow: hidden; cursor: zoom-in;
  background: #ded5c2; color: inherit; -webkit-tap-highlight-color: transparent;
  box-shadow: 0 2px 6px -2px rgba(19,17,16,0.16);
  transition: box-shadow 0.6s var(--oce-ease), transform 0.6s var(--oce-ease);
}
.oce-shot:hover { transform: translateY(-4px); box-shadow: 0 34px 72px -30px rgba(19,17,16,0.45); }
.oce-shot__img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 1.2s var(--oce-ease);
}

.oce-shot:hover .oce-shot__img, .oce-shot:focus-visible .oce-shot__img { transform: scale(1.055); }
.oce-shot__veil {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(16,13,11,0.92) 0%, rgba(16,13,11,0.62) 22%, rgba(16,13,11,0.22) 46%, rgba(16,13,11,0) 72%);
  opacity: 0.9; transition: opacity 0.5s var(--oce-ease);
}
.oce-shot:hover .oce-shot__veil { opacity: 1; }
/* a hairline gold frame draws itself in on hover */
.oce-shot::after {
  content: ""; position: absolute; inset: 0.65rem; border: 1px solid rgba(242,220,174,0.6);
  border-radius: 13px; pointer-events: none; opacity: 0; transform: scale(1.015);
  transition: opacity 0.5s var(--oce-ease), transform 0.5s var(--oce-ease);
}
.oce-shot:hover::after, .oce-shot:focus-visible::after { opacity: 1; transform: none; }
.oce-shot__meta {
  position: absolute; left: 0; right: 0; bottom: 0; pointer-events: none;
  padding: clamp(1rem, 1.6vw, 1.5rem); display: grid; gap: 0.5rem; justify-items: start; text-align: left;
  transform: translateY(6px); transition: transform 0.5s var(--oce-ease);
}
.oce-shot:hover .oce-shot__meta { transform: none; }
/* Also metadata. It sits on the dark veil at the foot of a photo, so it can
   carry itself as bare type — no fill, no pill, just the occasion in light
   gold above the caption. */
.oce-shot__tag {
  font-size: 0.58rem; letter-spacing: 0.22em; text-transform: uppercase; font-weight: 600;
  color: var(--oce-gold2); background: none; padding: 0; border-radius: 0;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.6);
}
.oce-shot__title {
  font-family: var(--oce-serif); font-weight: 600; line-height: 1.18; color: #fff;
  font-size: clamp(1.05rem, 1.45vw, 1.42rem); text-shadow: 0 1px 14px rgba(0,0,0,0.4);
}
.oce-shot__zoom {
  position: absolute; top: 1rem; right: 1rem; width: 2.3rem; height: 2.3rem; border-radius: 50%;
  display: grid; place-items: center; pointer-events: none;
  background: rgba(250,247,240,0.16); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  opacity: 0; transform: scale(0.8); transition: opacity 0.4s var(--oce-ease), transform 0.4s var(--oce-ease);
}
.oce-shot:hover .oce-shot__zoom, .oce-shot:focus-visible .oce-shot__zoom { opacity: 1; transform: none; }
.oce-shot__zoom svg { width: 1rem; height: 1rem; fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round; }
/* stand-in art — the badge disappears by itself once real photos load */
.oce-shot__ph {
  position: absolute; top: 1rem; left: 1rem; font-size: 0.53rem; letter-spacing: 0.18em;
  text-transform: uppercase; font-weight: 600; color: var(--oce-ivory);
  background: rgba(19,17,16,0.5); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  /* a status marker, not a control and not a caption: square-cornered so it
     belongs to neither family */
  padding: 0.3rem 0.62rem; border-radius: 3px; pointer-events: none;
}

@media (max-width: 1100px) {
  .oce-gallery__grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: auto; }
  .oce-shot, .oce-shot--w7, .oce-shot--w6, .oce-shot--w5, .oce-shot--w4 { grid-column: span 1; grid-row: auto; height: auto; aspect-ratio: 4 / 3; }
  .oce-shot--w12 { grid-column: span 2; grid-row: auto; height: auto; aspect-ratio: 21 / 9; }
}
@media (max-width: 620px) {
  .oce-gallery__grid { grid-template-columns: 1fr; }
  .oce-shot, .oce-shot--w12, .oce-shot--w7, .oce-shot--w6, .oce-shot--w5, .oce-shot--w4 { grid-column: span 1; height: auto; aspect-ratio: 4 / 3; }
}

/* ── Gallery lightbox ────────────────────────────────────── */
.oce-lb { position: fixed; inset: 0; z-index: 9990; display: grid; place-items: center; padding: clamp(1rem, 4vw, 3.5rem); }
.oce-lb[hidden] { display: none; }
.oce-lb__scrim {
  position: absolute; inset: 0; border: 0; background: rgba(11,9,8,0.94);
  backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  opacity: 0; transition: opacity 0.45s var(--oce-ease);
}
.oce-lb--on .oce-lb__scrim { opacity: 1; }
.oce-lb__figure {
  position: relative; z-index: 2; max-width: min(1180px, 100%);
  display: grid; justify-items: center; gap: clamp(0.9rem, 2vw, 1.4rem);
  opacity: 0; transform: scale(0.965) translateY(14px);
  transition: opacity 0.5s var(--oce-ease), transform 0.55s var(--oce-ease);
}
.oce-lb--on .oce-lb__figure { opacity: 1; transform: none; }
.oce-lb__img {
  display: block; max-width: 100%; max-height: 72svh; object-fit: contain;
  border-radius: 14px; background: #191512; box-shadow: 0 50px 120px -40px rgba(0,0,0,0.95);
  transition: opacity 0.28s ease;
}
.oce-lb--swap .oce-lb__img { opacity: 0.15; }
.oce-lb__cap { display: grid; justify-items: center; gap: 0.6rem; text-align: center; }
.oce-lb__tag {
  font-size: 0.6rem; letter-spacing: 0.22em; text-transform: uppercase; font-weight: 600;
  color: var(--oce-gold2);
}
.oce-lb__tag:empty { display: none; }
.oce-lb__title { font-family: var(--oce-serif); font-size: clamp(1.25rem, 2.4vw, 1.9rem); color: var(--oce-ivory); line-height: 1.2; }
.oce-lb__count { font-size: 0.72rem; letter-spacing: 0.2em; color: rgba(250,247,240,0.5); font-variant-numeric: tabular-nums; }
.oce-lb__x, .oce-lb__nav {
  position: absolute; z-index: 3; display: grid; place-items: center; border-radius: 50%;
  border: 1px solid rgba(250,247,240,0.2); background: rgba(250,247,240,0.07); color: var(--oce-ivory);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  transition: background 0.3s, border-color 0.3s, transform 0.4s var(--oce-ease), opacity 0.3s;
}
.oce-lb__x:hover, .oce-lb__nav:hover { background: var(--oce-gold2); border-color: var(--oce-gold2); color: var(--oce-ink); transform: scale(1.08); }
.oce-lb__x { top: clamp(0.9rem, 2.4vw, 1.8rem); right: clamp(0.9rem, 2.4vw, 1.8rem); width: 2.9rem; height: 2.9rem; }
.oce-lb__nav { top: 50%; margin-top: -1.7rem; width: 3.4rem; height: 3.4rem; }
.oce-lb__nav--prev { left: clamp(0.5rem, 2.2vw, 2rem); }
.oce-lb__nav--next { right: clamp(0.5rem, 2.2vw, 2rem); }
.oce-lb__nav[disabled] { opacity: 0.25; pointer-events: none; }
.oce-lb__x svg, .oce-lb__nav svg { width: 1.15rem; height: 1.15rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
@media (max-width: 620px) {
  .oce-lb__nav { top: auto; bottom: clamp(0.8rem, 3vw, 1.5rem); margin-top: 0; width: 3rem; height: 3rem; }
  .oce-lb__img { max-height: 58svh; }
}

@media (max-width: 900px) {
  .oce-statement__grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  .oce .oce-reveal, .oce-hero__eyebrow, .oce-hero__sub, .oce-hero__actions { opacity: 1; transform: none; }
  .oce-hero__line > span { transform: none; }
  .oce-marquee__track { animation: none; }
  /* the scroll hint pulses forever; this block is the only one that covers it
     when the page is rendered without the header component's blanket rule */
  .oce-hero__scroll span { animation: none; }
  .oce-piece__art path, .oce-piece__art ellipse, .oce-piece__art line, .oce-piece__art rect { stroke-dashoffset: 0; animation: none; }
  .oce-shot__meta { transform: none; }
  .oce-shot:hover { transform: none; }
  .oce-shot:hover .oce-shot__img { transform: none; }
  .oce-lb__figure { opacity: 1; transform: none; transition: none; }
  .oce-lb__scrim { opacity: 1; transition: none; }
}

/* ── Contact ─────────────────────────────────────────────── */
.oce-contact { background: var(--oce-ivory); padding: clamp(4.5rem, 9vw, 7.5rem) clamp(1.25rem, 6vw, 6rem); }
.oce-contact__panel {
  max-width: 1360px; margin: 0 auto;
  display: grid; grid-template-columns: 1fr 1.1fr; gap: clamp(2rem, 5vw, 4.5rem);
  background:
    radial-gradient(800px 400px at 90% 0%, rgba(220,185,110,0.16), transparent 60%),
    linear-gradient(160deg, #1a1712, #262019);
  color: var(--oce-ivory); border-radius: 30px; padding: clamp(2.2rem, 5vw, 4.5rem);
  box-shadow: 0 40px 90px -40px rgba(19,17,16,0.5);
}
.oce-contact__copy h2 { color: var(--oce-ivory); font-size: clamp(2.4rem, 4.6vw, 3.8rem); margin: 1.2rem 0 1rem; }
.oce-contact__copy h2 em { color: var(--oce-gold2); }
.oce-contact__copy p { color: rgba(250,247,240,0.7); max-width: 26rem; }
.oce-contact__form { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem 1rem; align-content: start; }
/* min-width: 0 is load-bearing. Without it a column refuses to shrink below the
   widest box inside it, and the date box reports a wide minimum on iOS, which
   shoved the whole right column out past the edge of the dark panel. */
/* The column stretches to the tallest box in its row; this makes the box inside
   stretch with it, so two boxes side by side always end on the same line. A
   date box measures itself a couple of pixels taller than a text box and no
   fixed height would hold across browsers. */
.oce-field {
  position: relative; grid-column: span 2; min-width: 0; padding-top: 1.15rem;
  display: flex; flex-direction: column;
}
.oce-field input, .oce-field textarea, .oce-field select { flex: 1 1 auto; }
.oce-field--half { grid-column: span 1; }
/* this one's hint is a whole sentence, so it gets room to rise onto two lines */
.oce-field--note { padding-top: 2.5rem; }
.oce-field input, .oce-field textarea, .oce-field select {
  width: 100%; min-width: 0; padding: 1.05rem 1.2rem; border-radius: 14px;
  background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.14);
  color: var(--oce-ivory); font-family: inherit; font-size: 0.95rem; font-weight: 300;
  line-height: 1.4;
  transition: border-color 0.3s, background 0.3s; resize: vertical;
}
/* one height for every single-line box, so a row never comes out ragged.
   Left alone, a date box measures itself differently from a text box. */
.oce-field input, .oce-field select { min-height: 3.5rem; }
.oce-field select {
  appearance: none; -webkit-appearance: none; cursor: pointer; color-scheme: dark;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23dcb96e' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 1.15rem center; background-size: 0.72rem;
  padding-right: 2.6rem;
}
.oce-field select option { background: #221e17; color: var(--oce-ivory); }
.oce-field input:focus, .oce-field textarea:focus, .oce-field select:focus { outline: none; border-color: var(--oce-gold2); background: rgba(255,255,255,0.1); }
/* the honeypot: a real field, kept out of sight and out of the tab order.
   Only a robot ever puts anything in it. */
.oce-field--trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
/* A label sits inside its box until the box has something in it, then rises to
   the empty strip above. It used to come to rest ON the box's top edge, on a
   little patch of flat colour — but the panel behind it is a gradient, so the
   patch never matched, and a label too wide for its box wrapped and hung down
   across the box below. Above the box there is nothing to collide with. */
.oce-field label {
  position: absolute; left: 1.2rem; top: 2.2rem; font-size: 0.9rem;
  max-width: calc(100% - 2.4rem);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: rgba(250,247,240,0.72); pointer-events: none; transition: all 0.25s var(--oce-ease);
}
/* the message box is tall, so its hint may use a second line while resting */
.oce-field textarea + label { white-space: normal; }
.oce-field--note label { top: 3.55rem; }
.oce-field input:focus + label, .oce-field input:not(:placeholder-shown) + label,
.oce-field textarea:focus + label, .oce-field textarea:not(:placeholder-shown) + label,
.oce-field .oce-label--static {
  top: 0; left: 0.15rem; max-width: 100%; white-space: nowrap;
  font-size: 0.64rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--oce-gold2); background: none; padding: 0; border-radius: 0;
}
.oce-field--note textarea:focus + label,
.oce-field--note textarea:not(:placeholder-shown) + label { white-space: normal; }
/* left to itself a date box ignores the width it is given and sits off-centre */
.oce-field input[type="date"] {
  color-scheme: dark; -webkit-appearance: none; appearance: none;
  display: block; text-align: left;
}
/* The way out for someone who has stalled at the message box. It is a way
   through rather than a second thing to press, so it takes no container at
   all and stays plain text beside the pill below it. The underline is there
   so the link is not marked by its color alone. */
.oce-form__hint {
  grid-column: span 2; font-size: 0.82rem; line-height: 1.55;
  color: rgba(250,247,240,0.66);
}
.oce-form__hint a {
  color: var(--oce-gold2); border-bottom: 1px solid rgba(220,185,110,0.45);
  transition: border-color 0.25s, color 0.25s;
}
.oce-form__hint a:hover, .oce-form__hint a:focus-visible { border-bottom-color: var(--oce-gold2); }
.oce-form__hint a:focus-visible { outline: 2px solid var(--oce-gold2); outline-offset: 3px; }
.oce-contact__form .oce-btn { grid-column: span 2; margin-top: 0.4rem; }
.oce-form__note { grid-column: span 2; font-size: 0.85rem; color: var(--oce-gold2); min-height: 1.2em; text-align: center; line-height: 1.5; }
.oce-form__note--bad { color: #f0a99f; }
.oce-contact__form.oce-sending { opacity: 0.55; pointer-events: none; }

/* the thank-you that replaces the form once a request is away */
.oce-thanks { text-align: center; align-self: center; display: grid; gap: 0.9rem; justify-items: center; padding: 1rem 0; }
.oce-thanks__mark {
  width: 3.6rem; height: 3.6rem; border-radius: 50%; display: grid; place-items: center;
  background: rgba(220,185,110,0.14); border: 1px solid rgba(220,185,110,0.5);
}
.oce-thanks__mark svg { width: 1.5rem; height: 1.5rem; fill: none; stroke: var(--oce-gold2); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.oce-thanks h3 { font-size: clamp(1.7rem, 3vw, 2.3rem); color: var(--oce-ivory); }
.oce-thanks p { color: rgba(250,247,240,0.72); max-width: 26rem; }

@media (max-width: 860px) {
  .oce-contact__panel { grid-template-columns: 1fr; }
}

/* On a phone, two boxes to a row leaves neither wide enough for its own label,
   so every field takes the full width. 16px text is the size below which iOS
   zooms the whole page in the moment a box is tapped. */
@media (max-width: 620px) {
  .oce-contact__form { grid-template-columns: 1fr; }
  .oce-field, .oce-field--half,
  .oce-contact__form .oce-btn, .oce-form__note, .oce-form__hint { grid-column: span 1; }
  .oce-field input, .oce-field textarea, .oce-field select { font-size: 16px; }
}


/* ── Setup Studio (3D builder) ───────────────────────────── */
.oce-builder {
  background:
    radial-gradient(900px 460px at 12% 0%, rgba(185,138,46,0.12), transparent 60%),
    linear-gradient(170deg, #191612, #24201a 55%, #131110);
  color: var(--oce-ivory);
  padding: clamp(5.5rem, 11vw, 9rem) clamp(1.25rem, 6vw, 6rem);
}
/* Split head like the Core Collection, so the Studio no longer opens the same
   way the Gallery directly above it does. */
.oce-builder__head {
  max-width: 1360px; margin: 0 auto clamp(2.4rem, 5vw, 3.5rem);
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 2rem; flex-wrap: wrap; text-align: left;
}
.oce-builder__head h2 { color: var(--oce-ivory); font-size: clamp(2.4rem, 5vw, 4rem); margin-top: 1.4rem; }
.oce-builder__head h2 em { color: var(--oce-gold2); }
.oce-builder__sub { color: rgba(250,247,240,0.65); max-width: 26rem; margin: 0; padding-bottom: 0.5rem; }
.oce-builder__frame { max-width: 1360px; margin: 0 auto; display: grid; grid-template-columns: 1.7fr 1fr; gap: 1.3rem; align-items: stretch; }
/* The stage no longer sets a height. It stretches to whatever the control
   panel next to it needs, because the panel was running roughly 330px
   longer and leaving a slab of empty band beside the 3D view. min-height
   keeps it honest if the panel is ever short. The 3D code watches the
   element for size changes; see the ResizeObserver in builder/studio.js. */
.oce-builder__stage {
  position: relative; border-radius: 22px; overflow: hidden;
  border: 1px solid rgba(19,17,16,0.1); background: var(--oce-ivory2);
  min-height: clamp(420px, 58vh, 640px);
}
.oce-builder__stage canvas { width: 100%; height: 100%; display: block; touch-action: pan-y; }
.oce-builder__hint {
  position: absolute; left: 1.1rem; bottom: 0.9rem; pointer-events: none;
  font-size: 0.64rem; letter-spacing: 0.2em; text-transform: uppercase; color: rgba(19,17,16,0.66);
}
.oce-builder__zoom { position: absolute; right: 1rem; bottom: 0.8rem; display: flex; gap: 0.4rem; }
.oce-builder__zoom button {
  position: relative;
  width: 2.2rem; height: 2.2rem; border-radius: 50%; border: 1px solid rgba(19,17,16,0.22);
  background: rgba(255,255,255,0.78); color: #131110; font-size: 1.15rem; line-height: 1;
  transition: background 0.3s, transform 0.3s var(--oce-ease);
}
/* drawn at 35px, tappable at 45px */
.oce-builder__zoom button::before { content: ""; position: absolute; inset: -5px; border-radius: 50%; }
.oce-builder__zoom button:hover { background: #fff; transform: scale(1.08); }
.oce-builder__loading {
  position: absolute; inset: 0; display: grid; place-items: center; background: var(--oce-ivory2);
  color: #57503f; font-family: var(--oce-serif); font-style: italic; font-size: 1.3rem; text-align: center; padding: 1rem;
}
/* ── the panel is linen, not a second dark block ──
   It used to be white at 3% over the dark band, which meant it barely
   separated from the band at all: the section read as dark, dark, then a
   sudden near-white card at the bottom. It is now the same linen as the
   3D view beside it, so the dark band frames two light surfaces instead
   of hiding one of them, and the seat card at its foot is a step down in
   tone rather than a jump up.

   Everything inside therefore switches to light-ground colours. On linen
   the accent gold must be #8a6519 or #a1761f, never the light #dcb96e —
   see The Two Golds Rule. Spacing is tighter than before too; the panel
   had to lose about 80px to meet the stage in the middle. */
.oce-builder__panel {
  border: 1px solid var(--oce-line); border-radius: 22px; padding: 1.35rem 1.35rem;
  background: var(--oce-ivory2); color: var(--oce-ink);
  display: flex; flex-direction: column; gap: 0.85rem;
}
.oce-bgroup h3 {
  font-family: var(--oce-sans); font-size: 0.64rem; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--oce-gold-ink); font-weight: 600; margin-bottom: 0.42rem;
}
.oce-brow { display: flex; flex-wrap: wrap; gap: 0.45rem; }
/* ── Studio controls: finger-sized hit areas ──
   These are the smallest controls on the site and they sit in its most
   interaction-heavy panel. Rather than inflate the visuals, each one keeps its
   drawn size and grows an invisible ::before out to at least 44px. ::before,
   not ::after — .oce-bdot--none already uses ::after for its slash. */
.oce-bchip {
  position: relative;
  min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  /* a choice among a set, same family as the gallery filters — not a pill */
  padding: 0.42rem 0.95rem; border-radius: 6px; border: 1px solid rgba(19,17,16,0.16);
  background: transparent; color: var(--oce-soft); font-size: 0.78rem; font-weight: 500;
  transition: border-color 0.25s, color 0.25s, background 0.25s;
}
/* Text takes the darker gold, the border takes the lighter one. Chip
   labels are 0.78rem, which is small text and needs 4.5:1 — #a1761f only
   reaches 3.47 on linen. A border is a graphic and needs 3:1, so it can
   keep the lighter gold. See The Two Golds Rule. */
.oce-bchip:hover { border-color: var(--oce-gold-head); color: var(--oce-gold-ink); }
/* Selected inverts to solid ink, the way the Linen Library filters do on
   their light band — not to gold. Gold on this panel is already carrying
   the six group labels and the seat card's numeral, and a gold fill on
   top of that is how this palette tips into the overwrought-luxury look
   the owner rejected. Ink is the stronger contrast move anyway. */
.oce-bchip.oce-on { background: transparent; border-color: var(--oce-ink); color: var(--oce-ivory); font-weight: 600; }
.oce-bdot {
  position: relative;
  width: 1.7rem; height: 1.7rem; border-radius: 50%;
  /* white ring plus an ink hairline, so a white swatch still has an edge
     against linen — on the dark panel the ring alone was enough */
  border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(19,17,16,0.22);
  transition: transform 0.25s var(--oce-ease), box-shadow 0.25s;
}
/* drawn at 27px, tappable at 45px */
.oce-bdot::before { content: ""; position: absolute; inset: -9px; border-radius: 50%; }
.oce-bdot:hover { transform: scale(1.2); }
.oce-bdot.oce-on { box-shadow: 0 0 0 2px var(--oce-gold-head); transform: scale(1.12); }
.oce-bstep {
  position: relative;
  width: 2rem; height: 2rem; border-radius: 50%; border: 1px solid rgba(19,17,16,0.2);
  background: transparent; color: var(--oce-ink); font-size: 1.05rem; line-height: 1;
  transition: border-color 0.25s, color 0.25s, transform 0.25s var(--oce-ease);
}
/* drawn at 32px, tappable at 44px */
.oce-bstep::before { content: ""; position: absolute; inset: -6px; border-radius: 50%; }
.oce-bstep:hover:not(:disabled) { border-color: var(--oce-gold-head); color: var(--oce-gold-ink); transform: scale(1.1); }
.oce-bstep:disabled { opacity: 0.3; cursor: default; }
.oce-bstep__val {
  min-width: 7.5rem; text-align: center; align-self: center;
  font-size: 0.85rem; color: var(--oce-soft); letter-spacing: 0.02em;
}
.oce-bdot--none { background: transparent; position: relative; }
.oce-bdot--none::after {
  content: ""; position: absolute; left: 14%; top: 45%; width: 72%; height: 2px;
  background: rgba(19,17,16,0.5); transform: rotate(-45deg);
}
.oce-builder__summary {
  font-size: 0.85rem; color: rgba(250,247,240,0.68); line-height: 1.5;
  border-top: 1px solid rgba(250,247,240,0.12); padding-top: 1rem; min-height: 2.6em; margin-top: auto;
}
@media (max-width: 1020px) {
  .oce-builder__frame { grid-template-columns: 1fr; }
  /* Stacked, so there is nothing to match heights with. min-height has to
     be cleared as well as height set: it is the larger of the two above
     this breakpoint, and left in place it would override the shorter
     height meant for a phone. */
  .oce-builder__stage { min-height: 0; height: clamp(340px, 52vh, 520px); }
}

/* each section's content sits in front of that section's own backdrop */
.oce-statement__grid, .oce-stats,
.oce-core__head, .oce-core__grid, .oce-core__cta,
.oce-linens__head, .oce-filters, .oce-linens__grid,
.oce-gallery__head, .oce-gfilters, .oce-gallery__grid,
.oce-builder__head, .oce-builder__frame,
.oce-contact__panel { position: relative; }

/* iOS paints a translucent block over whatever you tap. Over a gold gradient
   that reads as a white box around the button, which is what it looked like on
   a phone. Turned off here; the controls carry their own hover and pressed
   states, so nothing is lost. */
.oce a, .oce button, .oce [tabindex], .oce summary, .oce label {
  -webkit-tap-highlight-color: transparent;
}

/* A pointer should not leave a ring behind either. Tapping sets :focus, and the
   browser draws its own box for that - dark on a light ground, light on a dark
   one, which is the black square seen around a colour swatch and the white one
   around a gold button. The keyboard ring below is untouched.

   Written as :focus:not(:focus-visible) deliberately: a browser too old to know
   :focus-visible treats the whole selector as invalid and drops the rule, so
   the default ring stays rather than vanishing for everyone. */
.oce :focus:not(:focus-visible) { outline: none; }

/* keyboard focus is always visible (WCAG 2.4.7) */
.oce a:focus-visible, .oce button:focus-visible, .oce input:focus-visible,
.oce textarea:focus-visible, .oce [tabindex]:focus-visible {
  /* #a1761f: 3.8:1 on ivory, 3.95:1 on the dark bands. The base gold #b98a2e
     was 2.91:1 and WCAG 1.4.11 wants 3:1 of a focus indicator. */
  outline: 3px solid #a1761f; outline-offset: 2px; border-radius: 4px;
}

/* iOS press-and-hold on a link opens a preview menu, and while it is open the
   system draws a white card behind the link and highlights the text inside it.
   Over a gold gradient that reads as a white box around the button. Nothing can
   restyle that card - the only control is whether the menu appears.

   These three are actions, not addresses: "open in new tab" means nothing for
   Get a Quote or a tel: link you already tap to dial. So the menu is turned off
   for them, which takes the white card with it. Plain text links - the nav
   items, the footer links, the footer phone and email - deliberately keep it,
   because copying a link or a number there is genuinely useful.

   user-select goes too: long-press was also selecting the button's label. */
.oce .oce-btn, .oce .oce-nav__cta, .oce .oce-callbar__btn {
  -webkit-touch-callout: none;
  -webkit-user-select: none; user-select: none;
}

/* ══ Interactive motion ═══════════════════════════════════════════
   Everything above eases already. What was missing is reward: the
   hovers changed state politely and then stopped. These add a second
   beat to each one.

   A second curve joins --oce-ease here. --oce-ease is the house
   signature: fast start, long soft settle, used for anything large or
   elegant. --oce-spring overshoots a few percent past its target and
   comes back, which reads as playful rather than expensive, so it is
   kept to small controls a visitor pokes at - swatches, filters,
   chips. Large surfaces never spring; a lifting card that bounces
   looks cheap. DESIGN.md, Motion, records both.

   Pointer variables (--oce-mx / --oce-my / --oce-tilt-*) are written
   by the script. Their defaults here are what a keyboard visitor and
   a phone get, and they resolve to a centred, untilted card - so the
   effect is additive and its absence is not a broken state. */
.oce {
  --oce-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --oce-quick: cubic-bezier(0.2, 0.6, 0.3, 1);
}

/* ── Linen cards ──
   Three things happen at once: the card lifts and turns a few degrees
   toward the cursor, a warm light follows the pointer across it, and
   the swatches inside come up in sequence rather than together. The
   tilt is deliberately small - past about 6deg the text edges soften
   and it stops looking like a card and starts looking like an effect. */
.oce-linen {
  --oce-mx: 50%; --oce-my: 50%;
  --oce-tilt-x: 0deg; --oce-tilt-y: 0deg;
  position: relative;
  transition: transform 0.55s var(--oce-ease), box-shadow 0.55s var(--oce-ease),
              border-color 0.45s linear;
}
/* while the pointer is actually moving over the card the transform has
   to keep up with it, so the long settle is traded for a short one and
   handed back on the way out */
.oce-linen.oce-live { transition-duration: 0.12s, 0.55s, 0.45s; }

.oce-linen::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  border-radius: inherit; opacity: 0;
  background: radial-gradient(20rem 20rem at var(--oce-mx) var(--oce-my),
              rgba(221,178,88,0.20), rgba(221,178,88,0.07) 42%, transparent 68%);
  transition: opacity 0.5s var(--oce-ease);
}
.oce-linen > * { position: relative; z-index: 2; }

.oce-linen:hover, .oce-linen:focus-within {
  transform: perspective(1100px)
             rotateX(var(--oce-tilt-x)) rotateY(var(--oce-tilt-y))
             translateY(-10px) scale(1.015);
  border-color: rgba(185,138,46,0.55);
  box-shadow: 0 30px 66px -28px rgba(19,17,16,0.34),
              0 10px 26px -18px rgba(185,138,46,0.5);
}
.oce-linen:hover::before, .oce-linen:focus-within::before { opacity: 1; }

/* the stagger: each swatch starts a beat after the one before it, so
   the row ripples left to right instead of jumping as a block */
.oce-linen .oce-swatch {
  transition: transform 0.45s var(--oce-spring);
  transition-delay: calc(var(--oce-i, 0) * 35ms);
}
.oce-linen:hover .oce-swatch { transform: translateY(-4px); }

/* ── Swatches ──
   The dot already grew on hover. Now it overshoots slightly and throws
   a soft gold ring, which makes a colour feel picked rather than
   pointed at. */
.oce-swatch__dot { transition: transform 0.42s var(--oce-spring), box-shadow 0.42s var(--oce-ease); }
.oce-swatch:hover .oce-swatch__dot, .oce-swatch:focus-within .oce-swatch__dot {
  transform: scale(1.34);
  box-shadow: 0 0 0 1px var(--oce-line), 0 6px 16px rgba(19,17,16,0.22),
              0 0 0 6px rgba(185,138,46,0.16);
}

/* ── Buttons ──
   A band of light crosses the button once per hover. It is a single
   pass, not a loop: a repeating shine on a call to action reads as an
   advertisement. The press state matters as much - without it a tap on
   a phone gives no feedback at all between touch and page change. */
.oce .oce-btn::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 45%;
  left: -60%; pointer-events: none; z-index: 0;
  background: linear-gradient(100deg, transparent,
              rgba(255,255,255,0.42) 50%, transparent);
  transform: skewX(-18deg);
  transition: left 0.75s var(--oce-ease);
}
.oce .oce-btn:hover::after, .oce .oce-btn:focus-visible::after { left: 125%; }
.oce .oce-btn > * { position: relative; z-index: 1; }
.oce .oce-btn:hover { transform: translateY(-3px); }
.oce .oce-btn:active { transform: translateY(-1px) scale(0.975); transition-duration: 0.09s; }

/* ghost buttons take the same sweep, dimmer - a white band at full
   strength on a transparent button just looks like a rendering fault */
.oce .oce-btn--ghost::after { background: linear-gradient(100deg, transparent, rgba(255,255,255,0.16) 50%, transparent); }

/* ── Rectangle filters ──
   The fill rises from the bottom edge rather than switching on, which
   tells you which one you are about to choose while you are still
   deciding. */
.oce-filters:not(.oce-filters--text) .oce-filter { position: relative; overflow: hidden; z-index: 0; }
.oce-filters:not(.oce-filters--text) .oce-filter::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--oce-gold); opacity: 0.13;
  transform: translateY(101%);
  transition: transform 0.4s var(--oce-ease);
}
.oce-filters:not(.oce-filters--text) .oce-filter:hover::before { transform: none; }
.oce-filters:not(.oce-filters--text) .oce-filter:active { transform: translateY(0) scale(0.97); transition-duration: 0.09s; }

/* the text filters get their underline earlier and a nudge, so the two
   filter styles feel like the same family moving at the same speed */
.oce-filters--text .oce-filter { transition: color 0.3s, transform 0.4s var(--oce-spring); }
.oce-filters--text .oce-filter:hover { transform: translateY(-2px); }

/* ── Gallery tiles ──
   Deeper lift and a longer image push, so a photograph feels like it
   is coming toward you rather than sliding. */
.oce-shot:hover { transform: translateY(-7px) scale(1.008); box-shadow: 0 40px 84px -32px rgba(19,17,16,0.5); }
.oce-shot:hover .oce-shot__img, .oce-shot:focus-visible .oce-shot__img { transform: scale(1.075); }
.oce-shot:active { transform: translateY(-3px) scale(0.995); transition-duration: 0.1s; }

/* ── Dark collection panels ── */
.oce-piece { transition: transform 0.55s var(--oce-ease), border-color 0.45s linear, background 0.45s linear, box-shadow 0.55s var(--oce-ease); }
.oce-piece:hover { transform: translateY(-6px); box-shadow: 0 36px 80px -30px rgba(0,0,0,0.78), 0 0 40px -18px rgba(185,138,46,0.4); }

/* ── Reduced motion ──
   DESIGN.md: any new motion extends this block. Everything above is
   reward, not information - every hover here also changes colour or
   shadow, so switching the movement off costs a visitor nothing. */
@media (prefers-reduced-motion: reduce) {
  .oce-linen, .oce-linen:hover, .oce-linen:focus-within { transform: none; }
  .oce-linen::before { display: none; }
  .oce-linen:hover .oce-swatch { transform: none; }
  .oce .oce-btn::after { display: none; }
  .oce .oce-btn:hover, .oce .oce-btn:active { transform: none; }
  .oce-filters:not(.oce-filters--text) .oce-filter::before { transform: none; opacity: 0.09; }
  .oce-filters:not(.oce-filters--text) .oce-filter:hover::before { opacity: 0.2; }
  .oce-filters--text .oce-filter:hover { transform: none; }
  .oce-shot:hover, .oce-shot:active { transform: none; }
  .oce-shot:hover .oce-shot__img, .oce-shot:focus-visible .oce-shot__img { transform: none; }
  .oce-piece:hover { transform: none; }
  .oce-swatch:hover .oce-swatch__dot, .oce-swatch:focus-within .oce-swatch__dot { transform: none; }
}

/* ══ Two corrections to the block above ═══════════════════════════
   1. Why the shadow and outline snapped while the lift eased.

   Every linen card and every collection panel is also a scroll
   reveal, and the reveal rule near the top of this file is:

     .oce .oce-reveal { transition: opacity 1s, transform 1s; }

   Two class names beat one. That rule outranked `.oce-linen` and
   `.oce-piece`, so it did not merely add to their transitions - it
   replaced them. transform survived because the reveal happens to
   list it, which is why the lift and the tilt eased normally. Nothing
   else was on the list, so box-shadow and border-color had no
   transition at all and changed instantly.

   The fix is to match the reveal's weight and name every property
   these elements animate, opacity included, so the scroll reveal
   still fades in. transform runs at the hover duration rather than
   the reveal's 1s: one property can hold only one duration at a time,
   and a hover that takes a full second feels broken, while a reveal
   that takes 0.55s just looks brisk.

   This is the trap CLAUDE.md describes under component conventions -
   the file looks correct, the page is wrong, and nothing errors. */
.oce .oce-linen, .oce .oce-linen.oce-reveal {
  transition: opacity 0.9s var(--oce-ease),
              transform 0.55s var(--oce-ease),
              box-shadow 0.55s var(--oce-ease),
              border-color 0.45s linear;
}
.oce .oce-linen.oce-live, .oce .oce-linen.oce-reveal.oce-live {
  transition-duration: 0.9s, 0.12s, 0.55s, 0.45s;
}
.oce .oce-piece, .oce .oce-piece.oce-reveal {
  transition: opacity 0.9s var(--oce-ease),
              transform 0.55s var(--oce-ease),
              box-shadow 0.55s var(--oce-ease),
              border-color 0.45s linear,
              background 0.45s linear;
}

/* 2. The colour swatches put a blinking text cursor in the page.

   A swatch is a focusable span carrying its colour name as real text
   for screen readers. Clicking one gave the browser a focused element
   full of selectable text and it did the reasonable thing: it placed
   a caret. Nobody needs to select the word "Beige" out of a tooltip,
   so the text stops being selectable and the pointer stops being an
   I-beam. The name stays fully available to assistive technology -
   user-select changes what a mouse can grab, not what is read out. */
.oce-swatch {
  cursor: default;
  -webkit-user-select: none; user-select: none;
}
.oce-swatch__tip { -webkit-user-select: none; user-select: none; }


/* ── the gallery's "see all" control ──
   The archive outgrew the page: scrolling past every photo became a chore
   on the way to the quote form, so the grid opens on two rows and offers
   the rest. Script shows this only when a full extra row is hidden behind
   it; below that it stays out of the way entirely.

   It takes the 6px rectangle, not the pill. This changes what you are
   looking at and is reversible, which is the Choose family — the pill
   belongs to committing actions. It deliberately matches the filter row
   above the grid, so the gallery's controls read as one set. */
.oce-gallery__more {
  display: flex; justify-content: center;
  margin-top: clamp(1.7rem, 3.2vw, 2.6rem);
}
.oce-gmore {
  min-height: 44px; padding: 0.7rem 1.9rem;
  border-radius: 6px; border: 1px solid rgba(19,17,16,0.18);
  background: transparent;
  font-family: var(--oce-sans); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--oce-soft);
  transition: border-color 0.25s, color 0.25s, background 0.25s,
              transform 0.4s var(--oce-spring);
}
.oce-gmore:hover {
  border-color: var(--oce-gold-head); color: var(--oce-gold-ink);
  background: rgba(185,138,46,0.08);
}
.oce-gmore:active { transform: scale(0.97); }
.oce-gmore[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .oce-gmore { transition: border-color 0.25s, color 0.25s, background 0.25s; }
  .oce-gmore:active { transform: none; }
}


/* ═══════════════════════════════════════════════════════════
   THE SETUP STUDIO PANEL

   The panel used to end in a grey run-on line of fragments joined by
   dots, with the seat count buried at the tail behind an "approximately".
   The single most useful number in the tool was the least visible thing
   on screen.

   It now ends in a place card: an ivory band laid across the panel, with
   a drawn plan of the setup, the seat count as a number you can read
   across a room, and the setup written as an actual sentence.

   Two rules governed every choice here.

   The card runs edge to edge rather than sitting inset with its own
   rounded corners. A rounded card inside a rounded card is the one
   arrangement this system never uses — it reads as a component that
   escaped rather than a section of the panel.

   Gold stays a thread. The plan is drawn in it, the seat label is set
   in it, and nothing else new is. Every new hover and press state below
   works in the golds already defined at the top of this file, and the
   plan's strokes take the darker gold because they sit on ivory.
   ═══════════════════════════════════════════════════════════ */

/* The card reaches the panel's inner edge, so the panel has to clip it
   to its own corner radius. Nothing inside the panel needs to overflow:
   the largest thing that grows is a colour dot at 1.2x, which stays
   well inside the 1.5rem padding. */
.oce-builder__panel { overflow: hidden; }

/* ── the control groups arrive in sequence ──
   Six identical labelled rows appearing at once read as a form. Arriving
   one after another reads as a panel assembling itself, which is what the
   Studio actually is. Keyed off .oce-in, so if the reveal script never
   runs the groups are simply visible from the start. */
@keyframes oce-group-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
.oce-builder__frame.oce-in .oce-bgroup {
  animation: oce-group-in 0.7s var(--oce-ease) backwards;
}
.oce-builder__frame.oce-in .oce-bgroup:nth-child(1) { animation-delay: 0.08s; }
.oce-builder__frame.oce-in .oce-bgroup:nth-child(2) { animation-delay: 0.15s; }
.oce-builder__frame.oce-in .oce-bgroup:nth-child(3) { animation-delay: 0.22s; }
.oce-builder__frame.oce-in .oce-bgroup:nth-child(4) { animation-delay: 0.29s; }
.oce-builder__frame.oce-in .oce-bgroup:nth-child(5) { animation-delay: 0.36s; }
.oce-builder__frame.oce-in .oce-bgroup:nth-child(6) { animation-delay: 0.43s; }

/* ── chips fill rather than flip ──
   Selecting one used to swap its background instantly. The gold now wipes
   across from the left and the label darkens a beat later, once there is
   something behind it dark text can sit on. */
.oce-bchip { z-index: 0; overflow: hidden; }
.oce-bchip::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: var(--oce-ink);
  transform: scaleX(0); transform-origin: left center;
  transition: transform 0.45s var(--oce-ease), background 0.25s;
}
.oce-bchip.oce-on { background: transparent; color: var(--oce-ivory); font-weight: 600; }
.oce-bchip.oce-on::after { transform: scaleX(1); }
.oce-bchip.oce-on { transition: border-color 0.25s, color 0.2s 0.16s; }
/* Hovering an unselected chip used to slide the same gold panel in from
   the left at low opacity. Two problems: it previewed a commitment the
   visitor had not made, and it put a moving part under the cursor on
   every one of six rows. Hover now warms in place — border and text to
   gold over a faint wash — and the slide is reserved for the chip that
   actually gets chosen. */
.oce-bchip:not(.oce-on):hover { background: rgba(185, 138, 46, 0.1); }
.oce-bchip:active { transform: scale(0.97); }

/* ── dots and steppers get weight ──
   These are the controls a visitor pokes at directly, so they take the
   spring curve. A selected colour now throws its own small gold glow
   instead of only wearing a ring. */
.oce-bdot {
  transition: transform 0.4s var(--oce-spring), box-shadow 0.3s var(--oce-ease);
}
.oce-bdot.oce-on {
  transform: scale(1.14);
  box-shadow: 0 0 0 2px var(--oce-gold-head), 0 6px 16px -6px rgba(185, 138, 46, 0.7);
}
.oce-bdot:active { transform: scale(0.94); }
.oce-bstep {
  transition: border-color 0.25s, color 0.25s, background 0.25s,
              transform 0.4s var(--oce-spring);
}
.oce-bstep:hover:not(:disabled) { background: rgba(185, 138, 46, 0.1); }
.oce-bstep:active:not(:disabled) { transform: scale(0.88); }

/* ── the place card ──
   The first version of this stacked a small label over a centred number
   over a centred sentence — the standard statistic block — and it read
   as bland because that shape says dashboard, not event. It also carried
   a drawn floor plan, which was answering a question the 3D preview
   beside it already answers better, and answering it worse.

   What is left is the one thing the 3D view cannot tell you at a glance:
   how many people sit down. It is set as a phrase rather than a figure —
   a large gold numeral with its noun on the same baseline, ranged left,
   the way a number is set on a printed card. */
.oce-plate {
  position: relative;
  /* auto pushes the card to the foot of the panel; the negative sides reach
     the panel's inner edge, so this figure tracks the panel's padding and
     must be changed with it */
  margin: auto -1.35rem 0;
  padding: 1.35rem 1.35rem 1.4rem;
  /* One step deeper than the panel it sits in, not brighter than it. The
     card used to be near-white on a dark panel, which is what made it
     arrive as a jolt; now the panel is linen and the card is the aged
     linen under it. Same hue, one value down — the rule the gallery band
     already follows. */
  background: #eae2cf;
  color: var(--oce-ink);
}
/* Two things at once, on one layer so it stays under the text.

   The warm bloom is the same light the contact panel carries, so the card
   reads as lit rather than as a white rectangle cut out of the panel.

   Under it, a woven grid at the scale of real cloth. The card is meant to
   be linen, and every other surface in this system is flat, so this is
   the one place a texture belongs. Kept under 5% so it reads as fabric at
   arm's length and as nothing at all up close. */
.oce-plate::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(460px 220px at 88% -14%, rgba(185, 138, 46, 0.2), transparent 66%),
    repeating-linear-gradient(90deg, rgba(19, 17, 16, 0.045) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(0deg, rgba(19, 17, 16, 0.045) 0 1px, transparent 1px 3px);
}
.oce-plate > * { position: relative; }

/* ── the number ── */
.oce-plate__seat {
  position: relative; margin: 0; padding-bottom: 0.9rem;
  display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap;
}
/* Gold at the numeral and fading out across the card, so it reads as
   light thrown by the figure rather than as a divider drawn under it. */
.oce-plate__seat::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  transform-origin: left center;
  /* the numeral's own gold, not the base fill gold — #b98a2e only reaches
     2.42:1 on the card and went faint; this reads as the same ink */
  background: linear-gradient(90deg, var(--oce-gold-head) 0%, rgba(161, 118, 31, 0.12) 100%);
}
.oce-plate.oce-live .oce-plate__seat::after {
  animation: oce-rule-sweep 0.75s var(--oce-ease);
}
@keyframes oce-rule-sweep {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
.oce-plate__seatnum {
  font-family: var(--oce-serif); font-weight: 500;
  font-size: clamp(3.5rem, 9vw, 4.8rem); line-height: 0.82;
  letter-spacing: -0.035em; color: var(--oce-gold-head);
  /* digits share a width, so a count from 8 to 90 does not jiggle */
  font-variant-numeric: tabular-nums;
}
.oce-plate__seatlabel {
  font-family: var(--oce-sans); font-size: 0.66rem; font-weight: 600;
  letter-spacing: 0.34em; text-transform: uppercase; color: var(--oce-soft);
}

/* ── the sentence ──
   Set in the sans at body weight, not the serif. Cormorant never sets
   body copy in this system and a place card is not a good enough reason
   to make it the second exception. Written at .oce specificity because
   the original summary rule set a top border, a minimum height and an
   auto top margin that all belong to the card now. */
.oce .oce-builder__summary {
  margin: 0.95rem 0 0; max-width: 24rem;
  font-family: var(--oce-sans); font-size: 0.92rem; font-weight: 300;
  line-height: 1.6; color: var(--oce-soft); text-align: left;
  border-top: 0; padding-top: 0; min-height: 0;
}
/* the count repeated for screen readers, never drawn */
.oce-plate__spoken {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* When the 3D libraries never arrive, the count comes from code that never
   ran. studio.js sets the sentence to an honest fallback; the number is
   hidden rather than left showing a confident zero. */
.oce-plate--off .oce-plate__seat { display: none; }

@media (prefers-reduced-motion: reduce) {
  .oce-builder__frame.oce-in .oce-bgroup { animation: none; }
  .oce-plate.oce-live .oce-plate__seat::after { animation: none; }
  .oce-bchip::after { transition: none; }
  /* colour and border still change, so every control keeps a visible
     selected state; only the movement goes */
  .oce-bdot, .oce-bstep { transition: border-color 0.25s, color 0.25s, box-shadow 0.25s; }
  .oce-bchip:active, .oce-bdot:active, .oce-bstep:active:not(:disabled) { transform: none; }
}


/* ═══════════════════════════════════════════════════════════
   THE CLOTH FLIGHT

   Pressing "Send to My Quote" lifts the place card off the panel as a
   piece of linen, carries it down the page and lays it over the message
   box, where the setup is already written.

   Everything that moves here is driven frame by frame from
   builder/studio-ui.js, because the thing it has to keep up with — a
   smooth scroll — has a duration only the browser knows. There are no
   keyframes to fall out of step with it. What lives here is the cloth's
   material: what it is made of, what colour it is, and what it sits on
   top of.

   The sheet is many narrow vertical strips of the same linen side by side.
   Each one moves along its own length, so a wave running across them
   bends the whole sheet and leaves its top and bottom edges undulating,
   and no gap can ever open between two strips. Every background layer
   below is either vertical or a fine repeat — a left-to-right gradient
   would restart in each strip and draw a visible band per strip. The
   light travelling across the sheet comes from the wave instead.
   ═══════════════════════════════════════════════════════════ */
.oce-cloth {
  position: fixed; inset: 0; z-index: 9980;
  /* over the fixed header, under the lightbox, and never in the way of a
     pointer, a scroll or the tab order */
  pointer-events: none; overflow: hidden;
}
.oce-cloth * { box-sizing: border-box; }
.oce-cloth__sheet {
  position: absolute; transform-origin: 50% 50%;
  will-change: transform; backface-visibility: hidden;
  /* a place card has cut corners, not torn ones */
  border-radius: 3px;
  /* the aged linen of the card it came off, woven at the scale of real
     cloth */
  background:
    repeating-linear-gradient(90deg, rgba(19, 17, 16, 0.05) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(0deg, rgba(19, 17, 16, 0.05) 0 1px, transparent 1px 3px),
    linear-gradient(180deg, #efe8d7 0%, #eae2cf 52%, #e2d9c3 100%);
  /* the shadow swings with the card because it is cast by the card, and the
     card is what is turning. Warm ink, per the rule that a shadow carries
     the colour of what casts it. */
  box-shadow: 0 18px 34px -12px rgba(19, 17, 16, 0.42);
}
/* The sheen that crosses the card as it turns.

   It is inset to the card exactly and slid by moving the background, not
   the element. It used to be an element three times the card's width so it
   could travel - and with nothing clipping it, it hung out past the card
   and painted a second translucent card across the page at the same angle.
   That is what the owner saw on his phone. A background cannot escape the
   box it is painted in, so this cannot happen again. */
.oce-cloth__light {
  position: absolute; inset: 0; pointer-events: none; border-radius: inherit;
  background: linear-gradient(104deg,
    rgba(19, 17, 16, 0) 26%,
    rgba(19, 17, 16, 0.10) 38%,
    rgba(255, 253, 247, 0.44) 50%,
    rgba(19, 17, 16, 0.08) 62%,
    rgba(19, 17, 16, 0) 74%);
  background-size: 300% 100%;
}

/* What is written on the cloth: the seat count and the sentence, lifted off
   the card. An empty sheet was the original mistake — the visitor watched a
   bare rectangle cross the page while the thing they had just built stayed
   behind. This layer sits above the strips and is never cut up among them,
   so the writing stays sharp all the way down while the linen ripples under
   it. It is a copy for looking at only: its ids and live region are stripped
   in the script, and the whole flight is aria-hidden. */
.oce-cloth__face {
  position: absolute; inset: 0; z-index: 2;
  display: flex; align-items: center;
  transform-origin: 50% 50%; will-change: transform;
}
/* the copy gives up the card's own surface and texture — the strips beneath
   are the surface now, and two linen textures over each other muddies both */
.oce-cloth__face .oce-plate {
  margin: 0; width: 100%; background: none;
}
.oce-cloth__face .oce-plate::before { display: none; }

/* The message box takes one beat as the cloth comes off it — its border
   warms to gold and the fill lifts, then it goes back. The transition is
   already on the field above, so the class is the whole animation. */
.oce-field.oce-laid textarea {
  border-color: var(--oce-gold2); background: rgba(255, 255, 255, 0.1);
}

@media (prefers-reduced-motion: reduce) {
  /* The flight never starts when reduced motion is asked for — this is the
     belt to that braces, in case a cloth is in the air when the setting is
     changed. The line still lands in the box either way. */
  .oce-cloth { display: none; }
  .oce-field.oce-laid textarea { transition: none; }
}
