/* Dynamic Display — kiosk styles. Designed for a 1920×1080 TV but fluid via
   clamp(). The WebGL canvas sits behind a crisp HTML overlay. .dd-lite swaps the
   canvas for an animated CSS gradient when the GPU can't keep up. */

/* Cormorant/Inter dress the hotel screens (reception, rooms, event boards).
   Fraunces/Barlow Condensed belong to the restaurant screens only — a warmer,
   more editorial voice for a room where people are eating, deliberately not
   the corporate hotel typography. One request for all four. */
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Inter:wght@400;500;600;700&family=Fraunces:opsz,wght@9..144,400;9..144,600;9..144,700&family=Barlow+Condensed:wght@400;500;600&display=swap');

:root {
  --dd-bg-a: #0b1e3a;
  --dd-bg-b: #07101f;
  --dd-bg-accent: #c9a14a;
  --dd-ink: #f5f1e9;
  --dd-ink-soft: rgba(245, 241, 233, 0.66);
  --dd-line: rgba(245, 241, 233, 0.14);
  --dd-live: #38d39f;
  --dd-glass-ink: #dce6f2;
  --serif: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #07101f;
  color: var(--dd-ink);
  font-family: var(--sans);
  cursor: none;
  -webkit-font-smoothing: antialiased;
}

#dd-bg,
.dd-fallback {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}

.dd-fallback {
  display: none;
  background: linear-gradient(135deg, var(--dd-bg-a), var(--dd-bg-b) 55%, #050a14);
  background-size: 220% 220%;
  animation: dd-pan 26s ease-in-out infinite;
}
.dd-fallback::after {
  content: '';
  position: absolute;
  inset: -20%;
  /* Every color-mix() below is paired with a plain rgba() on the line above it.
     color-mix() is Chromium 111+, so TV browsers drop it and would otherwise get
     no colour at all; they fall back to the default brand gold/green rather than
     the per-hotel colour, which CSS cannot tint a var() with on its own. */
  background: radial-gradient(closest-side, rgba(201, 161, 74, 0.3), transparent 70%);
  background: radial-gradient(closest-side, color-mix(in srgb, var(--dd-bg-accent) 30%, transparent), transparent 70%);
  opacity: 0.5;
  animation: dd-float 18s ease-in-out infinite;
}
@keyframes dd-pan { 0%, 100% { background-position: 0% 0%; } 50% { background-position: 100% 100%; } }
@keyframes dd-float { 0%, 100% { transform: translate(-8%, -6%); } 50% { transform: translate(10%, 8%); } }

.dd-stage {
  position: relative;
  z-index: 2;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: clamp(28px, 4vw, 72px);
}

.dd-status {
  position: fixed;
  bottom: 18px;
  right: 22px;
  z-index: 5;
  font-size: 13px;
  letter-spacing: 0.04em;
  color: var(--dd-ink-soft);
  background: rgba(0, 0, 0, 0.35);
  padding: 6px 12px;
  border-radius: 999px;
}

/* ----------------------------------------------------------------- header */

.dd-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
.dd-logo {
  width: clamp(120px, 12vw, 240px);
  height: clamp(48px, 5vw, 86px);
  background-position: left center;
  background-repeat: no-repeat;
  background-size: contain;
}
.dd-logo-text { font-family: var(--serif); font-size: clamp(28px, 3vw, 46px); letter-spacing: 0.02em; }

/* -------------------------------------------------------------- room view */

.dd-room { flex: 1; display: flex; flex-direction: column; }
.dd-room-name {
  margin-top: clamp(20px, 4vh, 64px);
  display: flex;
  align-items: baseline;
  gap: 24px;
}
.dd-room-name-main { font-family: var(--serif); font-size: clamp(48px, 7vw, 120px); line-height: 1; }
/* "Salle Atlas" above "Salon A", on the door of a divided room. The part is
   the hero because it is what the guest was told to look for; the room is the
   context that makes it findable. A separate line rather than a chip beside
   the name: on a portrait door these two read as a title and its subtitle,
   and baseline-aligning them would make the room look like an afterthought
   tacked onto the part. */
.dd-room-of {
  flex-basis: 100%;
  font-size: clamp(15px, 1.5vw, 24px);
  color: var(--dd-ink-soft);
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.dd-room-name { flex-wrap: wrap; }
.dd-room-floor {
  font-size: clamp(16px, 1.6vw, 26px);
  color: var(--dd-ink-soft);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.dd-event-card {
  margin-top: auto;
  padding: clamp(28px, 3.5vw, 64px);
  border: 1px solid var(--dd-line);
  border-radius: 22px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.02));
  backdrop-filter: blur(8px);
}
.dd-event-card.is-live { border-color: rgba(56, 211, 159, 0.55); border-color: color-mix(in srgb, var(--dd-live) 55%, transparent); }

.dd-state-badge {
  display: inline-block;
  font-size: clamp(13px, 1.1vw, 18px);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  padding: 7px 16px;
  border-radius: 999px;
  margin-bottom: 22px;
}
.dd-state-badge.is-live { background: var(--dd-live); color: #04231a; }
.dd-state-badge.is-upcoming { background: rgba(255, 255, 255, 0.12); color: var(--dd-ink); }

.dd-event-title { font-family: var(--serif); font-size: clamp(44px, 5.6vw, 104px); line-height: 1.02; }
.dd-event-client { margin-top: 12px; font-size: clamp(22px, 2.2vw, 40px); color: var(--dd-ink-soft); }
.dd-event-meta { margin-top: 28px; display: flex; flex-wrap: wrap; gap: 14px; }
.dd-chip {
  font-size: clamp(16px, 1.5vw, 26px);
  padding: 10px 20px;
  border-radius: 999px;
  border: 1px solid var(--dd-line);
  letter-spacing: 0.02em;
}
.dd-chip--time { font-variant-numeric: tabular-nums; }
.dd-chip--status { font-weight: 600; }

.dd-available { margin: auto 0; display: flex; flex-direction: column; align-items: flex-start; gap: 22px; }
.dd-available-dot {
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--dd-live);
  box-shadow: 0 0 0 0 rgba(56, 211, 159, 0.7);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--dd-live) 70%, transparent);
  animation: dd-pulse 2.4s ease-out infinite;
}
.dd-available-text { font-family: var(--serif); font-size: clamp(54px, 8vw, 140px); color: var(--dd-ink); }
@keyframes dd-pulse {
  0% { box-shadow: 0 0 0 0 rgba(56, 211, 159, 0.6); box-shadow: 0 0 0 0 color-mix(in srgb, var(--dd-live) 60%, transparent); }
  100% { box-shadow: 0 0 0 26px transparent; }
}

/* Free room: room name only, centred as the hero (no availability label). */
.dd-room--solo { justify-content: center; }
.dd-room-solo { margin: auto 0; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 18px; width: 100%; }
.dd-room-solo-of {
  font-size: clamp(16px, 1.8vw, 30px);
  color: var(--dd-ink-soft);
  letter-spacing: 0.22em;
  text-transform: uppercase;
}
.dd-room-solo-name { font-family: var(--serif); font-size: clamp(64px, 11vw, 200px); line-height: 1; color: var(--dd-ink); }
.dd-room-solo-floor { font-size: clamp(18px, 2vw, 34px); color: var(--dd-ink-soft); letter-spacing: 0.18em; text-transform: uppercase; }

/* Multilingual welcome inside the signature's navy band (room screens). Gold
   script, sits above the room name; cycles languages on its own loop. */
/* Handwritten "hello" greeting (Apple style): an SVG of gold stroke paths drawn
   on with stroke-dashoffset. Sized to the band; cycles languages on its own loop. */
.dd-sig-welcome {
  position: relative;
  width: 100%;
  height: clamp(120px, 17vh, 240px);
  display: flex;
  align-items: center;
  justify-content: center;
  will-change: opacity;
}
.dd-sig-welcome svg {
  height: 100%;
  width: auto;
  max-width: 82%;
  overflow: visible;
}
.dd-sig-welcome svg path {
  stroke: #e6c168;
  fill: none;
}

/* ----------------------------------------------------------- board view */

.dd-board { flex: 1; display: flex; flex-direction: column; }
.dd-board-title { font-family: var(--serif); font-size: clamp(28px, 3vw, 52px); color: var(--dd-ink); }
.dd-board-list { margin-top: clamp(20px, 3vh, 48px); display: flex; flex-direction: column; gap: clamp(10px, 1.4vh, 20px); flex: 1; }
.dd-board-row {
  display: grid;
  grid-template-columns: clamp(160px, 16vw, 280px) 1fr auto auto;
  align-items: center;
  gap: 28px;
  padding: clamp(14px, 1.8vh, 26px) clamp(18px, 2vw, 34px);
  border: 1px solid var(--dd-line);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.03);
}
.dd-board-row.is-live { border-color: rgba(56, 211, 159, 0.5); border-color: color-mix(in srgb, var(--dd-live) 50%, transparent); background: rgba(56, 211, 159, 0.08); background: color-mix(in srgb, var(--dd-live) 8%, transparent); }
.dd-board-time { font-size: clamp(22px, 2vw, 38px); font-variant-numeric: tabular-nums; color: var(--dd-ink); }
.dd-board-event { font-family: var(--serif); font-size: clamp(26px, 2.6vw, 46px); line-height: 1.06; }
.dd-board-client { font-size: clamp(15px, 1.3vw, 22px); color: var(--dd-ink-soft); margin-top: 4px; }
.dd-board-room { text-align: right; }
.dd-board-room-name { font-size: clamp(20px, 1.8vw, 32px); }
.dd-board-room-floor { font-size: clamp(13px, 1.1vw, 18px); color: var(--dd-ink-soft); letter-spacing: 0.12em; text-transform: uppercase; }
.dd-board-live {
  font-size: clamp(12px, 1vw, 16px); letter-spacing: 0.2em; text-transform: uppercase;
  color: #04231a; background: var(--dd-live); padding: 6px 14px; border-radius: 999px;
}

/* ----------------------------------------------------------- idle widgets */

.dd-idle { position: relative; flex: 1; }
.dd-card { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
.dd-card-title { font-size: clamp(18px, 1.8vw, 30px); letter-spacing: 0.22em; text-transform: uppercase; color: var(--dd-ink-soft); margin-bottom: 28px; }

.dd-clock-time { font-family: var(--serif); font-size: clamp(120px, 20vw, 360px); line-height: 0.92; font-variant-numeric: tabular-nums; }
.dd-clock-bar { width: clamp(220px, 26vw, 460px); height: 4px; background: var(--dd-line); border-radius: 999px; margin: 22px 0 18px; overflow: hidden; }
.dd-clock-bar i { display: block; height: 100%; background: var(--dd-bg-accent); transform-origin: left; transition: transform 0.9s linear; }
.dd-clock-date { font-size: clamp(24px, 2.6vw, 46px); color: var(--dd-ink-soft); text-transform: capitalize; }

.dd-w-now { display: flex; align-items: center; gap: 28px; }
.dd-w-icon { font-size: clamp(60px, 9vw, 150px); }
.dd-w-temp { font-family: var(--serif); font-size: clamp(90px, 14vw, 240px); line-height: 1; }
.dd-w-label { font-size: clamp(22px, 2.4vw, 40px); color: var(--dd-ink-soft); margin-top: 10px; }
.dd-w-days { display: flex; gap: clamp(20px, 3vw, 56px); margin-top: 40px; }
.dd-w-day { display: flex; flex-direction: column; align-items: center; gap: 8px; font-size: clamp(16px, 1.5vw, 26px); }
.dd-w-di { font-size: clamp(28px, 3vw, 50px); }
.dd-w-mm { color: var(--dd-ink-soft); font-variant-numeric: tabular-nums; }

.dd-prayer-grid { display: flex; gap: clamp(16px, 2.4vw, 48px); }
.dd-prayer {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: clamp(20px, 2.4vw, 44px) clamp(18px, 2vw, 40px);
  border: 1px solid var(--dd-line); border-radius: 18px; min-width: clamp(120px, 12vw, 200px);
}
.dd-prayer.is-next { border-color: var(--dd-bg-accent); background: rgba(201, 161, 74, 0.12); background: color-mix(in srgb, var(--dd-bg-accent) 12%, transparent); }
.dd-prayer-name { font-size: clamp(18px, 1.7vw, 30px); letter-spacing: 0.1em; text-transform: uppercase; color: var(--dd-ink-soft); }
.dd-prayer-time { font-family: var(--serif); font-size: clamp(40px, 5vw, 86px); line-height: 1; font-variant-numeric: tabular-nums; }

.dd-promo { }
.dd-promo-img { width: clamp(280px, 40vw, 720px); height: clamp(180px, 26vw, 460px); border-radius: 22px; background-size: cover; background-position: center; margin-bottom: 32px; }
.dd-promo-title { font-family: var(--serif); font-size: clamp(40px, 5vw, 92px); line-height: 1.02; }
.dd-promo-text { font-size: clamp(22px, 2.2vw, 40px); color: var(--dd-ink-soft); margin-top: 14px; max-width: 70vw; }

/* ------------------------------------------------------- Global idle board */
/* Every configured widget shown at once in a responsive grid (no rotation).
   The multilingual "hello" handwriting used to live at the top of this board;
   it's now the persistent .dd-hello-chip, `position: fixed` and independent
   of this layout. IdleBoard only ever renders during the main phase — the one
   moment Hello is in its large .dd-hello-chip--hero state — so its top offset
   here is a fixed reservation sized to clear the hero badge (top clamp(30px,
   4vh,60px) + height clamp(180px,26vh,360px), plus breathing room) rather
   than something the grid centres into. Sized as EXTRA clearance beyond
   .dd-stage's own top padding (clamp(28px,4vw,72px)), not on top of it — and
   no larger than that, since whatever this takes comes straight out of the
   cards' height and the prayer card needs ~520px to show all five times. */
.dd-idle-board { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; gap: clamp(20px, 3vh, 48px); padding: clamp(200px, 28vh, 380px) clamp(26px, 4vw, 84px) clamp(22px, 4vh, 64px); justify-content: center; }
.dd-idle-grid { flex: 1; display: flex; flex-wrap: wrap; align-items: stretch; justify-content: center; gap: clamp(16px, 2vw, 36px); }
.dd-idle-grid .dd-card { position: static; inset: auto; flex: 1 1 clamp(260px, 28vw, 420px); background: rgba(255, 255, 255, 0.04); border: 1px solid var(--dd-line); border-radius: 24px; padding: clamp(20px, 3vh, 44px) clamp(20px, 2vw, 40px); }
.dd-idle-grid .dd-card-title { font-size: clamp(15px, 1.4vw, 24px); margin-bottom: 18px; }
.dd-idle-grid .dd-clock-time { font-size: clamp(56px, 8vw, 132px); }
.dd-idle-grid .dd-clock-bar { width: clamp(160px, 18vw, 300px); margin: 16px 0 14px; }
.dd-idle-grid .dd-clock-date { font-size: clamp(16px, 1.6vw, 28px); }
.dd-idle-grid .dd-w-now { gap: 18px; }
.dd-idle-grid .dd-w-icon { font-size: clamp(48px, 6vw, 96px); }
.dd-idle-grid .dd-w-temp { font-size: clamp(56px, 8vw, 128px); }
.dd-idle-grid .dd-w-label { font-size: clamp(18px, 1.8vw, 30px); }
.dd-idle-grid .dd-w-days { gap: clamp(14px, 1.6vw, 32px); margin-top: 24px; }
.dd-idle-grid .dd-w-day { font-size: clamp(14px, 1.2vw, 20px); }
.dd-idle-grid .dd-w-di { font-size: clamp(22px, 2.2vw, 38px); }
.dd-idle-grid .dd-prayer-grid { flex-wrap: wrap; gap: clamp(10px, 1.2vw, 22px); justify-content: center; }
/* Five prayer times wrap to three rows here, making this the tallest card in
   the grid — and so the one that decides whether the board fits. Its padding
   is tighter than the full-screen .dd-prayer above for that reason. */
.dd-idle-grid .dd-prayer { padding: clamp(12px, 1.5vh, 24px) clamp(14px, 1.4vw, 26px); gap: 8px; min-width: clamp(100px, 10vw, 160px); }
.dd-idle-grid .dd-prayer-name { font-size: clamp(13px, 1.2vw, 20px); }
.dd-idle-grid .dd-prayer-time { font-size: clamp(22px, 2.6vw, 44px); }
.dd-idle-grid .dd-promo-img { width: 100%; height: clamp(140px, 18vh, 260px); margin-bottom: 20px; }
.dd-idle-grid .dd-promo-title { font-size: clamp(26px, 3vw, 54px); }
.dd-idle-grid .dd-promo-text { font-size: clamp(16px, 1.6vw, 28px); max-width: none; }

/* ------------------------------------------------------------- hello badge */
/* Persistent multilingual "hello" handwriting, alive for the whole reception
   idle session (widget grid + every facility slide), never rebuilt between
   them — only its size morphs. Always centred, top-anchored, ice-toned (never
   gold). No pill/panel — just the bold handwritten strokes directly over the
   background. Two sizes, same element:
   .dd-hello-chip on its own = the quiet mark shown during facility slides;
   .dd-hello-chip--hero = the prominent hero shown on the main widget-grid
   display. Toggling the modifier class (IdlePresenter) morphs between them
   via the transition below instead of ever rebuilding the SVG. */
.dd-hello-chip {
  position: fixed;
  top: clamp(20px, 2.6vh, 42px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  height: clamp(84px, 10vh, 136px);
  /* .dd-hello-word (the SVG's parent) is absolutely positioned, so it can't
     contribute to this box's auto-width — an explicit width is required or
     the container (and everything in it) collapses to zero and vanishes. */
  width: clamp(240px, 32vw, 560px);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  will-change: height, width, top;
  transition: height 0.9s cubic-bezier(0.4, 0, 0.2, 1), width 0.9s cubic-bezier(0.4, 0, 0.2, 1),
    top 0.9s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Two (at most) stacked, absolutely-positioned word layers — the outgoing
   language crossfades out while the incoming one draws on top of it, so the
   badge is never left blank between languages (see createHelloWriter). Layout
   only here: shared with .dd-sig-welcome (room signature band), which keeps
   its own gold/thin styling below, untouched. */
.dd-hello-word { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }

.dd-hello-chip .dd-hello-word svg { height: 100%; width: auto; max-width: 100%; overflow: visible; filter: drop-shadow(0 6px 18px rgba(2, 6, 16, 0.5)); }
.dd-hello-chip .dd-hello-word svg path {
  stroke: var(--dd-glass-ink);
  fill: none;
  stroke-width: 16;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke-width 0.9s ease;
}

/* Sized in concert with .dd-idle-board's top padding, which reserves the room
   this occupies — the widget cards below need ~620px for the prayer times, so
   the hero can't grow past roughly a quarter of the frame without pushing
   them off the bottom. Change one, re-check the other. */
.dd-hello-chip--hero {
  top: clamp(30px, 4vh, 60px);
  height: clamp(180px, 26vh, 360px);
  width: clamp(520px, 62vw, 1180px);
}
.dd-hello-chip--hero .dd-hello-word svg path { stroke-width: 26; }

/* ---------------------------------------------------------- facility masonry */
/* Brezza / Zitoune / pool / gym slides: a mosaic of photos + autoplaying
   videos filling the frame, tile order and size re-shuffled by masonry.js on
   every activation. Pure grid/flex — no per-frame JS or canvas work, kept
   deliberately GPU-cheap for the Raspberry Pi. */
/* Top padding clears the small (chip-state) Hello badge, which is centred at
   the top of the screen during facility slides — no longer a right-side
   reservation now that the badge lives top-centre instead of the top-right
   corner. Sized as EXTRA clearance only — .dd-stage already contributes its
   own top padding (clamp(28px,4vw,72px)), so this doesn't need to cover the
   badge's full offset on its own, just the remainder. */
.dd-masonry-wrap { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; gap: clamp(14px, 2.2vh, 30px); padding-top: clamp(70px, 9vh, 120px); }
.dd-masonry-head { position: relative; display: flex; flex-direction: column; gap: 8px; padding-bottom: clamp(10px, 1.4vh, 20px); }
.dd-masonry-head::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: clamp(64px, 7vw, 120px);
  height: 3px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--dd-bg-accent), transparent);
}
.dd-masonry-title { font-family: var(--serif); font-weight: 600; font-size: clamp(38px, 5.2vw, 88px); line-height: 1; color: var(--dd-ink); }
.dd-masonry-subtitle { font-size: clamp(15px, 1.5vw, 26px); letter-spacing: 0.22em; text-transform: uppercase; color: var(--dd-bg-accent); }

/* Justified rows, sized by masonry.js: it sets each row's height and each
   tile's width so the row is flush to the frame and every tile keeps its true
   aspect ratio (see renderMasonrySlide). The gap here is read back by that
   code — .dd-masonry-row must use the same value. */
.dd-masonry {
  flex: 1;
  min-height: 0; /* let the frame shrink inside the flex column, so its height is definite */
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(10px, 1.2vw, 20px);
  overflow: hidden;
}
.dd-masonry-row {
  display: flex;
  justify-content: center; /* a trailing row that doesn't fill the width stays centred */
  gap: clamp(10px, 1.2vw, 20px);
  width: 100%;
}
.dd-masonry-tile {
  position: relative;
  flex: 0 0 auto;  /* width is set inline from the row height × the item's ratio — never flexed */
  height: 100%;
  border-radius: 20px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--dd-line);
  box-shadow: 0 10px 28px rgba(2, 6, 16, 0.28);
}
.dd-masonry-tile::after {
  /* Faint vignette so every tile reads with the same depth regardless of the
     photo/video underneath — no per-tile shadow filters needed. */
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(7, 16, 31, 0) 60%, rgba(7, 16, 31, 0.28) 100%);
  pointer-events: none;
}
/* cover is a formality here — the tile is sized to the media's own ratio, so
   there's nothing to crop. It only guards against a ratio the server couldn't
   read (rounding, or dimensions missing from the attachment metadata). */
.dd-masonry-tile video,
.dd-masonry-img { display: block; width: 100%; height: 100%; object-fit: cover; background-size: cover; background-position: center; }

/* ------------------------------------------------- Park Mall signature board */
/* Full-bleed white panel above the WebGL background, one event at a time.
   Mirrors the marketing TV "Park Mall (signature)" theme: navy ink, gold bar,
   bottom navy band carrying the room name as hero. */
.dd-sig-overlay { position: fixed; inset: 0; z-index: 3; }
.dd-sig {
  position: absolute;
  inset: 0;
  background: #ffffff;
  color: #1b2147;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.dd-sig-head { padding: clamp(28px, 3.2vw, 64px) clamp(32px, 3.6vw, 72px); }
.dd-sig-logo { width: clamp(160px, 16vw, 300px); height: clamp(60px, 7vw, 120px); background-position: left center; background-repeat: no-repeat; background-size: contain; }
.dd-sig-logo-text { font-family: var(--serif); font-size: clamp(30px, 3.4vw, 54px); color: #1b2147; }
.dd-sig-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 6vw clamp(40px, 6vh, 110px);
  gap: clamp(12px, 1.6vh, 26px);
}
.dd-sig-event-logo { width: min(54vw, 60vh); height: clamp(220px, 34vh, 460px); background-position: center; background-repeat: no-repeat; background-size: contain; }
/* The client's mark is the hero of the reception board, and that board is read
   from the far end of a lobby — 50% larger there than on a room door, which is
   read from a metre away and has a welcome band competing for the same card.
   background-size stays `contain`, so a wide wordmark and a round crest both
   grow to fill the box without either being cropped. */
.dd-sig--board .dd-sig-event-logo { width: min(81vw, 90vh); height: clamp(330px, 51vh, 690px); }
.dd-sig-event { font-family: var(--serif); font-weight: 700; font-size: clamp(48px, 6.2vw, 132px); line-height: 1.04; color: #1b2147; }
.dd-sig-client { font-size: clamp(24px, 2.6vw, 50px); color: #5b6079; }
.dd-sig-band {
  position: relative;
  background: #1b2147;
  color: #ffffff;
  padding: clamp(18px, 2vw, 34px) 6vw clamp(16px, 1.8vw, 28px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(4px, 0.6vh, 10px);
  text-align: center;
}
.dd-sig-band::before {
  content: '';
  position: absolute;
  top: -0.35vw;
  left: 50%;
  transform: translateX(-50%);
  width: 55%;
  height: 0.8vw;
  background: linear-gradient(90deg, #b8893b 0%, #e6c168 50%, #b8893b 100%);
}
.dd-sig-room { font-family: var(--serif); font-weight: 800; font-size: clamp(32px, 3.8vw, 72px); line-height: 1.05; letter-spacing: 0.01em; }
.dd-sig-meta { font-size: clamp(16px, 1.7vw, 30px); opacity: 0.9; letter-spacing: 0.04em; }

/* ----------------------------------------------- full-bleed media showcase */
/* The picture layer shared by the restaurant TV and the manual media screen
   (assets/js/showcase.js): one photo or video filling the frame, Ken Burns
   drift applied per-frame by GSAP transforms, the next one crossfading over
   the last. This stylesheet only sizes and dresses the layers.
   What each screen puts ON TOP of it is what differs — see the two modifier
   blocks below. */
.dd-showcase {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  background: #0a0d14; /* something behind the frame for the instant before it decodes — never a flash of white */
}
.dd-showcase--empty { justify-content: center; background: transparent; } /* unconfigured: let the ambient background show, like the pairing screen */

.dd-showcase-frame { position: absolute; inset: 0; overflow: hidden; }
.dd-showcase-media {
  position: absolute;
  inset: -4%; /* extra bleed so the Ken Burns drift never reveals a hard edge */
  width: 108%;
  height: 108%;
  object-fit: cover;
  will-change: transform;
}
.dd-showcase-scrim { position: absolute; inset: 0; pointer-events: none; }

/* --- manual media screen ---------------------------------------------------
   An operator-chosen poster loop. No chrome at all, so no scrim either: the
   image is the message and darkening it would only degrade what they picked.
   `contain` is offered for artwork that must not be cropped — it also has to
   cancel the bleed the drift relies on, or the letterboxed image would be
   pushed off-centre. */
.dd-showcase--media .dd-showcase-scrim { display: none; }
.dd-showcase--media { background: #000; }
.dd-showcase--contain .dd-showcase-media {
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* --- restaurant screen -----------------------------------------------------
   A TV physically inside one restaurant. Deliberately its OWN visual world,
   not the hotel's: where the reception board is cool navy, gold and Cormorant,
   this is warm espresso, copper and Fraunces — a room where people are eating,
   not a lobby. The photograph is the whole background; the chrome is a name
   set bottom-left against a copper rule, and the menu as a physical cream
   letterpress card laid slightly askew over the image, rather than the dark
   glass panels used elsewhere in this stylesheet. */
.dd-showcase--restaurant {
  --r-paper: #f2e8dc;
  --r-ink: #211611;
  --r-copper: #c8763f;
  background: #140d09; /* warm black, matching what the photography sits in */
}
.dd-showcase--restaurant .dd-showcase-scrim {
  /* Warm, not neutral: the bottom band carries the type, the diagonal wash
     and the vignette pull the whole frame toward candlelight so any photo —
     not just ones already shot moody — sits comfortably behind cream text. */
  background:
    linear-gradient(0deg, rgba(14, 8, 5, 0.9) 0%, rgba(16, 10, 6, 0.52) 32%, rgba(20, 13, 9, 0) 62%),
    linear-gradient(100deg, rgba(18, 11, 7, 0.46) 0%, rgba(18, 11, 7, 0) 46%),
    radial-gradient(120% 90% at 50% 45%, rgba(255, 214, 170, 0.07) 0%, rgba(10, 6, 4, 0.34) 100%);
}

.dd-restaurant-chrome {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: clamp(28px, 4vw, 80px);
  padding: 0 clamp(44px, 5vw, 104px) clamp(52px, 7vh, 104px);
}

/* Name block: a copper rule runs down the left edge, editorial rather than
   centred — the reception board centres everything, this deliberately doesn't. */
.dd-restaurant-head {
  max-width: 56%;
  padding-left: clamp(20px, 2vw, 34px);
  border-left: 2px solid var(--r-copper);
}
.dd-restaurant-title {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: clamp(52px, 7vw, 128px);
  line-height: 0.94;
  letter-spacing: -0.015em;
  color: var(--r-paper);
  text-shadow: 0 2px 30px rgba(0, 0, 0, 0.45);
}
.dd-restaurant-tagline {
  margin-top: clamp(12px, 1.8vh, 22px);
  font-family: 'Barlow Condensed', 'Inter', sans-serif;
  font-weight: 500;
  font-size: clamp(19px, 1.9vw, 34px);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--r-copper);
}
.dd-restaurant-hours {
  margin-top: clamp(6px, 1vh, 12px);
  font-family: 'Barlow Condensed', 'Inter', sans-serif;
  font-size: clamp(16px, 1.5vw, 26px);
  letter-spacing: 0.12em;
  color: rgba(242, 232, 220, 0.72);
  font-variant-numeric: tabular-nums;
}

/* The menu as a printed card: cream stock, a hairline rule inside its edge,
   tilted a hair as if set down on the table. This is the piece that makes a
   restaurant screen unmistakably not a hotel screen. */
.dd-restaurant-menu {
  position: relative;
  flex: 0 0 auto;
  min-width: clamp(280px, 25vw, 440px);
  padding: clamp(26px, 2.8vw, 44px) clamp(28px, 3vw, 46px) clamp(28px, 3vw, 46px);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 40%),
    var(--r-paper);
  color: var(--r-ink);
  border-radius: 3px; /* paper, not a glass pill */
  box-shadow: 0 26px 70px rgba(0, 0, 0, 0.5), 0 2px 0 rgba(255, 255, 255, 0.25) inset;
  transform: rotate(-0.5deg);
}
.dd-restaurant-menu::before {
  content: '';
  position: absolute;
  inset: clamp(9px, 0.7vw, 14px);
  border: 1px solid rgba(33, 22, 17, 0.22);
  pointer-events: none;
}
.dd-restaurant-menu { position: relative; }
.dd-restaurant-menu-label {
  font-family: 'Barlow Condensed', 'Inter', sans-serif;
  font-size: clamp(13px, 1.1vw, 19px);
  font-weight: 600;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--r-copper);
  text-align: center;
  margin-bottom: clamp(16px, 1.8vh, 26px);
}
.dd-restaurant-menu-row { display: flex; align-items: baseline; margin-top: clamp(11px, 1.5vh, 19px); }
.dd-restaurant-menu-row:first-of-type { margin-top: 0; }
/* Dotted leader between dish and price, the printed-menu convention. It's a
   generated box on the row (reordered between the two), so it stretches to fit
   any name length without extra markup. */
.dd-restaurant-menu-row::after {
  content: '';
  order: 2;
  flex: 1 1 auto;
  min-width: 14px;
  margin: 0 9px;
  border-bottom: 1px dotted rgba(33, 22, 17, 0.4);
  transform: translateY(-5px);
}
.dd-restaurant-menu-text { order: 1; min-width: 0; }
.dd-restaurant-menu-name {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: clamp(19px, 1.85vw, 29px);
  line-height: 1.15;
  white-space: nowrap;
}
.dd-restaurant-menu-note {
  font-family: 'Barlow Condensed', 'Inter', sans-serif;
  font-size: clamp(14px, 1.2vw, 20px);
  letter-spacing: 0.06em;
  color: rgba(33, 22, 17, 0.6);
  white-space: nowrap;
}
.dd-restaurant-menu-price {
  order: 3;
  white-space: nowrap;
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 700;
  font-size: clamp(19px, 1.75vw, 28px);
  font-variant-numeric: tabular-nums;
  color: var(--r-copper);
}

/* -------------------------------------------------------- bureaux directory */
/* A TV in the business center lobby, listing every occupied office by floor —
   the digital twin of a printed directory board. Deliberately its own visual
   world, the way the restaurant screen is its own: a directory is read at a
   glance in daylight, so unlike every other screen here (dark navy, WebGL
   glow) this one is bright — warm paper, navy ink, a gold rule. Full-bleed
   and opaque via position:fixed, same technique as .dd-restaurant/.dd-sig, so
   it simply covers the dark ambient background rather than needing it disabled. */
.dd-bureaux {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  background: linear-gradient(175deg, #fdfbf6 0%, #f3ecdc 100%);
  color: #1c2340;
  padding: clamp(36px, 4.4vh, 76px) clamp(44px, 4.5vw, 96px);
  overflow: hidden;
}
.dd-bureaux-head {
  display: flex;
  align-items: center;
  gap: clamp(20px, 2.4vw, 40px);
  padding-bottom: clamp(20px, 2.6vh, 40px);
  margin-bottom: clamp(24px, 3vh, 48px);
  border-bottom: 2px solid #c9a14a;
  flex: 0 0 auto;
}
.dd-bureaux-logo { flex: 0 0 auto; width: clamp(120px, 12vw, 220px); height: clamp(48px, 5vw, 84px); background-position: left center; background-repeat: no-repeat; background-size: contain; }
.dd-bureaux-title { font-family: var(--serif); font-weight: 600; font-size: clamp(34px, 3.6vw, 64px); line-height: 1.05; color: #1c2340; }

/* Same handwritten "hello" flourish as the reception idle board
   (createHelloWriter) — brand continuity across every screen type — but set
   into the header row itself rather than fixed top-center: that placement is
   tuned for the idle board's empty top band, and would fight a long
   business-center title here. Gold ink, not the ice-blue used on the dark
   screens — this one's read against warm paper. */
.dd-bureaux-hello {
  position: relative;
  margin-left: auto;
  flex: 0 0 auto;
  width: clamp(150px, 15vw, 280px);
  height: clamp(46px, 4.6vw, 80px);
}
.dd-bureaux-hello .dd-hello-word svg { height: 100%; width: auto; max-width: 100%; overflow: visible; }
.dd-bureaux-hello .dd-hello-word svg path { stroke: #a9843f; fill: none; stroke-width: 22; stroke-linecap: round; stroke-linejoin: round; }

/* Two fixed columns (see splitTwoColumns in bureaux.js for why not CSS
   `columns:`), with every row forced to the SAME height in JS
   (layoutEqualRows) regardless of how many tenants that floor lists — the
   board reads as uniform signwritten slots, not one box per floor sized to
   that floor's own tenant count. .dd-bureaux-cols is the fixed viewport
   both GSAP animates and layoutEqualRows measures against; the two
   .dd-bureaux-col children hold the floor blocks directly — there's no
   extra scale layer here, because overflow is now handled per floor (see
   .dd-bureaux-floor-fit below), not for the board as a whole. */
.dd-bureaux-cols {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: flex-start;
  gap: clamp(36px, 4vw, 72px);
}
.dd-bureaux-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* Height is set inline by layoutEqualRows once the panel's real available
   height is known — identical for every floor, in every column. */
.dd-bureaux-floor {
  display: flex;
  align-items: stretch;
  gap: clamp(16px, 1.8vw, 30px);
  border-bottom: 1px solid rgba(28, 35, 64, 0.1);
  /* Belt-and-suspenders alongside .dd-bureaux-floor-fit's own overflow:
     hidden — this box's height comes from a JS-assigned inline style, so if
     any engine's flex/stretch implementation ever fails to give the fit
     child that same definite height (which is what makes ITS overflow:
     hidden actually clip), this outer boundary still stops a row's content
     from bleeding into the row above or below it instead of just vanishing
     into an unclipped, overlapping mess. */
  overflow: hidden;
}

/* Fraunces, not Cormorant Garamond: the rest of the board reads as a
   printed page, but the floor number is a plaque, not a caption — this is
   the same variable, characterful display face already loaded for the
   restaurant screens (see the @import above), so it costs nothing extra to
   bring in here. align-self:stretch with no fixed height (inherited from
   .dd-bureaux-floor's align-items:stretch) is what fills the badge to the
   row's now-uniform height, whatever that turns out to be. */
.dd-bureaux-floor-num {
  flex: 0 0 auto;
  width: clamp(58px, 5.6vw, 104px);
  border-radius: 14px;
  background: #1c2340;
  color: #f6f1e4;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Fraunces', var(--serif);
  font-optical-sizing: auto;
  font-variant-numeric: lining-nums;
  font-weight: 600;
  font-size: clamp(24px, 3vw, 46px);
}

/* The scalable layer for THIS floor's content only (layoutEqualRows in
   bureaux.js) — separate from .dd-bureaux-floor's own fixed row height, so
   shrinking a busy floor's text never moves the row boundaries around it.
   overflow:hidden is the same "never silently gone" backstop as the old
   whole-board version: it only ever has to bite if a floor's content is so
   long it's still too tall even at the 0.55 floor JS stops shrinking at,
   which real content here doesn't reach. */
.dd-bureaux-floor-fit {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  transform-origin: top left;
}
.dd-bureaux-floor-units { min-width: 0; display: flex; flex-direction: column; justify-content: center; height: 100%; gap: clamp(6px, 0.9vh, 12px); padding: clamp(10px, 1.4vh, 20px) 0; }
/* Grid, not flex-wrap: a flex row's number-then-name pair only aligns to
   itself, so unit numbers of different lengths ("0501" vs "1401-1402" vs
   the units with none at all) each pushed their own name to a different x
   position. The fixed first column here is sized for the longest number in
   the directory, so every name in a floor's list — and every floor's list
   in a column — starts at the same x regardless of that unit's own number.
   The number cell is always rendered (see bureaux.js), even empty, so a
   number-less unit doesn't collapse the column and shift its name left. */
/* The column width comes from fitNumberColumn (bureaux.js), measured from
   the widest number actually in the directory once the type is final. The
   clamp() is only the pre-measurement fallback: as a fixed, viewport-
   derived width it could be narrower than its own content on a small
   screen, and since these numbers must not wrap, that meant a long number
   like "1401-1402" overran into the office name beside it. */
.dd-bureaux-unit { display: grid; grid-template-columns: var(--dd-num-col, clamp(60px, 6.2vw, 108px)) 1fr; align-items: baseline; column-gap: clamp(10px, 1vw, 18px); }
.dd-bureaux-unit-number {
  /* nowrap: a hyphenated range like "1401-1402" is a legal break point, so
     without this it wraps onto a second line inside its fixed-width column
     and silently makes that one unit twice as tall as every other — which
     is exactly how floor 14 ended up overflowing its row on the real
     screen while fitting in testing. */
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-size: clamp(13px, 1vw, 17px);
  color: #a9843f;
  letter-spacing: 0.04em;
}
/* Row, not a plain inline span: the description sits on the SAME line as
   the name (not stacked below it), taking whatever width the name doesn't
   need and ellipsizing rather than wrapping when that isn't enough — one
   line per office instead of two, which is what makes a single shared font
   size (fitSharedFontSize in bureaux.js) land on a comfortable size rather
   than a tiny one for a four-tenant floor. Whole-floor rows override this
   back to a stacked column below (see .dd-bureaux-unit--whole). */
.dd-bureaux-unit-text { display: flex; align-items: baseline; min-width: 0; gap: 0.5em; }
/* font-size is a literal default only — bureaux.js sets it inline after
   measuring every regular floor's densest row, so every office ends up the
   same size regardless of viewport. white-space:nowrap keeps a long name
   from wrapping and eating the line the description needs. */
.dd-bureaux-unit-name { flex: 0 0 auto; white-space: nowrap; font-family: var(--serif); font-weight: 600; font-size: 26px; color: #1c2340; }
.dd-bureaux-unit-desc {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-family: var(--sans);
  font-size: 19px;
  color: rgba(28, 35, 64, 0.58);
}

/* A floor entered with a single, number-less unit is a whole-floor tenant
   (Data\Bureaux — Panoramic Sky, Regus, I. Commerce), not a line item next
   to numbered offices, so it gets the row rather than a place in a list:
   the number cell drops, the name grows into a display headline, and a
   small gold kicker + rule say why in the vocabulary the rest of the board
   already speaks (uppercase, letter-spaced, the same #a9843f gold as the
   header rule and the unit numbers), rather than a badge or icon. */
.dd-bureaux-unit--whole { display: block; }
.dd-bureaux-unit--whole .dd-bureaux-unit-number { display: none; }
.dd-bureaux-unit--whole .dd-bureaux-unit-text { display: flex; flex-direction: column; gap: clamp(6px, 0.8vh, 10px); }
.dd-bureaux-unit-whole-tag {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--sans);
  font-size: clamp(11px, 0.85vw, 14px);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: #a9843f;
}
.dd-bureaux-unit-whole-tag::before { content: ''; display: inline-block; width: clamp(20px, 2vw, 34px); height: 2px; background: #a9843f; }
/* white-space:normal undoes the base rule's nowrap — a whole-floor name
   has this row to itself (no description sharing the line, no shared font
   size to hit), so it's free to wrap onto a second line rather than run
   past the row's width. */
.dd-bureaux-unit--whole .dd-bureaux-unit-name {
  white-space: normal;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 600;
  font-size: clamp(30px, 3.4vw, 52px);
  line-height: 1.05;
}
/* Back to a real multi-line clamp (display:-webkit-box + -webkit-line
   -clamp) — the base rule's single-line white-space:nowrap ellipsis
   doesn't apply here at all without re-establishing that box context. */
.dd-bureaux-unit--whole .dd-bureaux-unit-desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  white-space: normal;
  overflow: hidden;
  font-size: clamp(15px, 1.2vw, 20px);
  color: rgba(28, 35, 64, 0.62);
}

.dd-bureaux-empty {
  margin: auto;
  text-align: center;
  font-family: var(--serif);
  font-size: clamp(22px, 2.2vw, 36px);
  color: rgba(28, 35, 64, 0.5);
}

/* ------------------------------------------------------- bureaux diaporama */
/* "L'Ascension" — the second business_center screen type
   (config.bureaux_mode = 'diaporama', see Data\ScreenConfig /
   Frontend BureauxDiaporama): one office at a time, full-bleed, rotating.

   The organising idea is that this screen is a moving SECTION DRAWING of the
   tower, not a stack of centred cards. A permanent rail down the left plots
   every occupied floor as a tick, with a gold marker that physically travels
   between them as the rotation climbs — so a visitor who looks up mid-cycle
   learns where in the building this tenant sits, which a centred text card
   never told them. Everything else follows from that: content ENTERS FROM
   BELOW and leaves upward, so the whole frame reads as the building
   scrolling past a lift car rather than as slides swapping.

   Deliberately typographic, because the content is: every office in the live
   directory has no uploaded logo, so a logo-first design would be a design
   for content that mostly doesn't exist. The office name is the hero, set
   huge in Cormorant; the floor numeral is set in Fraunces (already loaded
   for the restaurant screens and the directory badges) and echoed as an
   outline "blueprint" ghost bleeding off the frame for depth. No new webfont
   request, no external asset — the grain is an inline SVG turbulence.

   A tenant that defines `promo` slides still gets its own beats afterward in
   ITS brand, full takeover — see .dd-bureaux-diapo-slide--promo. */
.dd-bureaux-diapo {
  --dp-ink: #141b33;
  --dp-ink-soft: rgba(20, 27, 51, 0.56);
  --dp-gold: #a8801f;
  --dp-rule: rgba(20, 27, 51, 0.14);
  position: fixed;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  background:
    radial-gradient(120% 100% at 6% -10%, #fffdf8 0%, #f7f1e3 46%, #eee3cd 100%);
}
/* Letterpress grain: an inline feTurbulence, so the texture costs no request
   and can't 404 on a kiosk with a cold cache. Multiply keeps it in the
   paper rather than hazing the type. */
.dd-bureaux-diapo::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 4;
  opacity: 0.35;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/></filter><rect width='160' height='160' filter='url(%23n)' opacity='0.42'/></svg>");
}
.dd-bureaux-diapo .dd-bureaux-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
}

.dd-bureaux-diapo-slide {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: clamp(64px, 5.6vw, 104px) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "rail top"
    "rail body"
    "rail foot";
  padding: clamp(30px, 4vh, 62px) clamp(38px, 4.4vw, 88px) clamp(26px, 3.4vh, 54px) 0;
  color: var(--dp-ink);
}

/* ---- the tower rail: this screen's whole reason for existing ---- */
.dd-bureaux-diapo-rail {
  grid-area: rail;
  position: relative;
  display: flex;
  justify-content: center;
  padding: clamp(6px, 1vh, 16px) 0;
}
.dd-bureaux-diapo-rail::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: linear-gradient(180deg, transparent, var(--dp-rule) 12%, var(--dp-rule) 88%, transparent);
}
.dd-bureaux-diapo-tick {
  position: absolute;
  left: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--sans);
  font-variant-numeric: tabular-nums;
  font-size: clamp(10px, 0.72vw, 13px);
  letter-spacing: 0.08em;
  color: rgba(20, 27, 51, 0.3);
  transition: color 0.6s ease, opacity 0.6s ease;
}
.dd-bureaux-diapo-tick--on { color: var(--dp-gold); }
/* The travelling car. Its top is set inline per beat (see _placeMarker) and
   animated, which is the one piece of state that makes the rail read as
   movement through a building rather than decoration. */
.dd-bureaux-diapo-car {
  position: absolute;
  left: 50%;
  width: clamp(26px, 2.2vw, 38px);
  height: clamp(26px, 2.2vw, 38px);
  margin-left: calc(clamp(26px, 2.2vw, 38px) / -2);
  margin-top: calc(clamp(26px, 2.2vw, 38px) / -2);
  border-radius: 50%;
  border: 1px solid var(--dp-gold);
}
.dd-bureaux-diapo-car::after {
  content: '';
  position: absolute;
  inset: 35%;
  border-radius: 50%;
  background: var(--dp-gold);
}

/* ---- top band ---- */
.dd-bureaux-diapo-top {
  grid-area: top;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  font-family: var(--sans);
  font-size: clamp(11px, 0.92vw, 16px);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--dp-gold);
}
.dd-bureaux-diapo-count {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.18em;
  color: rgba(20, 27, 51, 0.34);
}

/* ---- body: floor plaque + the office itself ---- */
.dd-bureaux-diapo-body {
  grid-area: body;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: clamp(26px, 3.6vw, 78px);
  min-height: 0;
}
.dd-bureaux-diapo-plaque {
  display: flex;
  align-items: center;
  gap: clamp(10px, 1vw, 18px);
}
/* "ÉTAGE" set vertically against the numeral — the one bit of rotated type,
   borrowed from real lift-lobby signage. */
.dd-bureaux-diapo-plaque-label {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-family: var(--sans);
  font-size: clamp(10px, 0.8vw, 14px);
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--dp-gold);
}
.dd-bureaux-diapo-plaque-num {
  font-family: 'Fraunces', var(--serif);
  font-optical-sizing: auto;
  font-variant-numeric: lining-nums;
  font-weight: 600;
  font-size: clamp(72px, 11vw, 210px);
  line-height: 0.82;
  letter-spacing: -0.02em;
  color: var(--dp-ink);
}
/* Blueprint echo of the same numeral, outline-only, bleeding off the right
   edge. Pure depth — never the thing you read. */
.dd-bureaux-diapo-ghost {
  position: absolute;
  right: clamp(-40px, -2vw, -10px);
  bottom: clamp(-30px, -3vh, -8px);
  z-index: 0;
  font-family: 'Fraunces', var(--serif);
  font-weight: 600;
  font-size: min(52vh, 40vw);
  line-height: 0.72;
  color: transparent;
  -webkit-text-stroke: 1px rgba(20, 27, 51, 0.09);
  pointer-events: none;
}

.dd-bureaux-diapo-main { position: relative; z-index: 1; min-width: 0; }
.dd-bureaux-diapo-logo {
  height: clamp(46px, 6vh, 92px);
  margin-bottom: clamp(14px, 1.8vh, 26px);
  background-position: left center;
  background-repeat: no-repeat;
  background-size: contain;
}
.dd-bureaux-diapo-name {
  font-family: var(--serif);
  font-weight: 600;
  /* Set inline by _fitName() after measuring — long tenant names ("Dr I.
     Khabcheche ép. Ouennouri") must never overrun the frame, and a clamp()
     alone can't know the string. */
  font-size: clamp(38px, 5.6vw, 116px);
  line-height: 0.98;
  letter-spacing: -0.015em;
  color: var(--dp-ink);
}
/* Per-word mask so the name can rise line-by-line out of nothing. */
.dd-bureaux-diapo-word { display: inline-block; overflow: hidden; vertical-align: bottom; }
.dd-bureaux-diapo-word > span { display: inline-block; }
.dd-bureaux-diapo-desc {
  margin-top: clamp(12px, 1.6vh, 24px);
  max-width: 46ch;
  font-family: var(--sans);
  font-weight: 300;
  font-size: clamp(15px, 1.34vw, 25px);
  line-height: 1.45;
  color: var(--dp-ink-soft);
}
/* A supporting line, never a paragraph. Some real descriptions run to 90
   characters ("ETPH Chebli Lighting – SC Concept & Construction – …"), and
   left unbounded they grow the frame past the screen — at which point
   _fitName shrinks the HERO NAME trying to win back room it can never win,
   because the overflow isn't the name's. Clamping here is what keeps the
   name big. The clamp sits on the inner (animated) span so the outer mask
   still does its rise-from-below job. */
.dd-bureaux-diapo-desc > .dd-bureaux-diapo-word,
.dd-bureaux-diapo-promo-subtitle > .dd-bureaux-diapo-word { display: block; }
.dd-bureaux-diapo-desc > .dd-bureaux-diapo-word > span,
.dd-bureaux-diapo-promo-subtitle > .dd-bureaux-diapo-word > span {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}
.dd-bureaux-diapo-meta {
  display: flex;
  align-items: center;
  gap: clamp(10px, 1vw, 16px);
  margin-top: clamp(16px, 2.2vh, 32px);
  font-family: var(--sans);
  font-variant-numeric: tabular-nums;
  font-size: clamp(11px, 0.92vw, 16px);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--dp-gold);
}
.dd-bureaux-diapo-meta::before {
  content: '';
  width: clamp(24px, 3vw, 56px);
  height: 1px;
  background: var(--dp-gold);
  opacity: 0.6;
}

/* ---- foot ---- */
.dd-bureaux-diapo-foot {
  grid-area: foot;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  font-family: var(--sans);
  font-size: clamp(10px, 0.82vw, 14px);
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: rgba(20, 27, 51, 0.3);
}

/* ---------- promo beat: a tenant's own frame, in THEIR brand ----------
   Same ascent choreography, inverted palette. Colours come from inline
   custom properties per slide, defaulting to ISKEN's real brand (dark
   teal-black + gold, pulled from iskendz.com) so the default is a genuine
   identity rather than a placeholder. */
.dd-bureaux-diapo-slide--promo {
  --promo-bg: #061619;
  --promo-ink: #f3ecdc;
  --promo-gold: #c8a45c;
  --dp-ink: var(--promo-ink);
  --dp-ink-soft: rgba(243, 236, 220, 0.72);
  --dp-gold: var(--promo-gold);
  --dp-rule: rgba(243, 236, 220, 0.16);
  background:
    radial-gradient(70% 50% at 12% 4%, rgba(200, 164, 92, 0.16), transparent 62%),
    radial-gradient(64% 58% at 92% 92%, rgba(29, 92, 102, 0.38), transparent 64%),
    var(--promo-bg);
  color: var(--promo-ink);
}
.dd-bureaux-diapo-slide--promo .dd-bureaux-diapo-count { color: rgba(243, 236, 220, 0.4); }
.dd-bureaux-diapo-slide--promo .dd-bureaux-diapo-foot { color: rgba(243, 236, 220, 0.4); }
.dd-bureaux-diapo-slide--promo .dd-bureaux-diapo-tick { color: rgba(243, 236, 220, 0.32); }
.dd-bureaux-diapo-slide--promo .dd-bureaux-diapo-ghost { -webkit-text-stroke-color: rgba(243, 236, 220, 0.1); }
.dd-bureaux-diapo-slide--promo .dd-bureaux-diapo-plaque-num { color: var(--promo-ink); }

.dd-bureaux-diapo-promo-media {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-position: center;
  background-repeat: no-repeat;
  background-size: cover;
}
.dd-bureaux-diapo-promo-media::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(75deg, rgba(6, 22, 25, 0.94) 8%, rgba(6, 22, 25, 0.62) 48%, rgba(6, 22, 25, 0.24) 100%);
}
.dd-bureaux-diapo-promo-kicker {
  font-family: var(--sans);
  font-size: clamp(11px, 0.95vw, 17px);
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--promo-gold);
}
.dd-bureaux-diapo-promo-title {
  margin-top: clamp(12px, 1.6vh, 24px);
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-style: italic;
  font-weight: 600;
  font-size: clamp(44px, 6.4vw, 132px);
  line-height: 0.98;
  color: var(--promo-ink);
}
.dd-bureaux-diapo-promo-subtitle {
  margin-top: clamp(14px, 1.8vh, 28px);
  max-width: 40ch;
  font-family: var(--sans);
  font-weight: 300;
  font-size: clamp(16px, 1.5vw, 29px);
  line-height: 1.4;
  color: rgba(243, 236, 220, 0.82);
}

/* --------------------------------------------------------------- pairing */

.dd-pairing { margin: auto; text-align: center; display: flex; flex-direction: column; align-items: center; }
.dd-pairing-kicker { font-size: clamp(16px, 1.5vw, 24px); letter-spacing: 0.3em; text-transform: uppercase; color: var(--dd-ink-soft); }
.dd-pairing-title { font-family: var(--serif); font-size: clamp(34px, 4vw, 64px); margin: 18px 0 56px; }
.dd-pairing-label { font-size: clamp(15px, 1.3vw, 22px); letter-spacing: 0.24em; text-transform: uppercase; color: var(--dd-ink-soft); }
.dd-pairing-code {
  font-family: var(--serif); font-weight: 700;
  font-size: clamp(110px, 18vw, 300px); line-height: 1; letter-spacing: 0.12em;
  color: var(--dd-bg-accent); margin: 10px 0 40px;
}
.dd-pairing-hint { font-size: clamp(18px, 1.8vw, 30px); color: var(--dd-ink-soft); max-width: 60vw; }

/* Discreet bottom-right countdown timer. Upcoming = gold-accented pill (matches
   the signature gold band); live = the theme's mint badge with dark ink, exactly
   like the other "live" badges (.dd-state-badge.is-live / .dd-board-live). */
.dd-room-timer {
  position: fixed;
  right: clamp(18px, 2vw, 40px);
  bottom: clamp(18px, 2.4vh, 40px);
  z-index: 10;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: clamp(15px, 1.3vw, 24px);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--dd-ink);
  background: rgba(46, 45, 40, 0.73);
  background: color-mix(in srgb, var(--dd-bg-accent) 20%, rgba(7, 16, 31, 0.66));
  border: 1px solid rgba(201, 161, 74, 0.55);
  border: 1px solid color-mix(in srgb, var(--dd-bg-accent) 55%, transparent);
  border-radius: 999px;
  padding: clamp(7px, 0.8vh, 13px) clamp(16px, 1.4vw, 26px);
  pointer-events: none;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22);
}
.dd-room-timer strong { font-weight: 700; letter-spacing: 0.02em; }
.dd-room-timer.is-live {
  color: #04231a;
  background: var(--dd-live);
  border-color: transparent;
  font-weight: 700;
  animation: dd-timer-pulse 2.4s infinite;
}
.dd-room-timer.is-live::before {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #04231a;
}
@keyframes dd-timer-pulse {
  0% { box-shadow: 0 0 0 0 rgba(56, 211, 159, 0.6); box-shadow: 0 0 0 0 color-mix(in srgb, var(--dd-live) 60%, transparent); }
  70% { box-shadow: 0 0 0 12px rgba(56, 211, 159, 0); box-shadow: 0 0 0 12px color-mix(in srgb, var(--dd-live) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 rgba(56, 211, 159, 0); box-shadow: 0 0 0 0 color-mix(in srgb, var(--dd-live) 0%, transparent); }
}

/* =========================================================== celebration ===
   "Le Cartouche" — a restaurant screen taken over, by hand, from a phone at
   the table. Guests film this, which is the whole design brief: it has to be a
   still photograph that happens to move, not a motion piece that happens to
   hold a name.

   So it is an engraved invitation card. Deep saturated ink so a phone camera
   has something to expose for; gold foil on the one word that matters; a
   double-rule frame and a letterhead plaque carrying the venue's mark. The
   composition is complete and legible with every animation removed — motion
   only adds a shimmer across the foil and a slow drift of motes, because the
   kiosk may never composite a single frame (see assets/js/celebration.js).

   Palettes are per-theme and total, not a hue swap: a child's birthday and a
   fortieth wedding anniversary should not be the same screen in a different
   colour. */

.dd-cel {
  position: fixed;
  inset: 0;
  z-index: 6; /* over the restaurant's own photo loop, which pauses beneath */
  overflow: hidden;
  background: var(--cel-deep);
  color: var(--cel-ink);
}

.dd-cel[data-theme='anniversaire'] {
  --cel-deep: #250d2d;
  --cel-mid: #4d1c52;
  --cel-foil: #f2c14e;
  --cel-foil-hi: #fff2c8;
  --cel-ink: #f7ead2;
  --cel-paper: #fdf3e0;
}
.dd-cel[data-theme='mariage'] {
  --cel-deep: #360c19;
  --cel-mid: #611b2c;
  --cel-foil: #e8c37a;
  --cel-foil-hi: #fff3da;
  --cel-ink: #f8ecdd;
  --cel-paper: #fdf2e5;
}
.dd-cel[data-theme='fiancailles'] {
  --cel-deep: #420f2a;
  --cel-mid: #71204a;
  --cel-foil: #f3bccb;
  --cel-foil-hi: #fff2f6;
  --cel-ink: #fce9ef;
  --cel-paper: #fff1f4;
}
.dd-cel[data-theme='felicitations'] {
  --cel-deep: #062a25;
  --cel-mid: #0f4a3b;
  --cel-foil: #e9cf8a;
  --cel-foil-hi: #fff8e2;
  --cel-ink: #e9f3ec;
  --cel-paper: #f6f7ec;
}
.dd-cel[data-theme='bienvenue'] {
  --cel-deep: #08152c;
  --cel-mid: #163353;
  --cel-foil: #d3dcec;
  --cel-foil-hi: #ffffff;
  --cel-ink: #e9effb;
  --cel-paper: #f3f6fd;
}

/* --- the field -------------------------------------------------------------
   Depth without an image: a soft colour mesh, an engraver's crosshatch that
   only shows where the light falls, and a vignette that pulls the eye to the
   name. All three survive a screen that never repaints, because none of them
   move. */
.dd-cel-field {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 85% at 50% 12%, color-mix(in srgb, var(--cel-foil) 15%, transparent) 0%, transparent 58%),
    radial-gradient(90% 70% at 18% 88%, color-mix(in srgb, var(--cel-mid) 85%, transparent) 0%, transparent 62%),
    radial-gradient(85% 70% at 84% 78%, color-mix(in srgb, var(--cel-mid) 70%, transparent) 0%, transparent 60%),
    radial-gradient(140% 110% at 50% 50%, transparent 38%, rgba(0, 0, 0, 0.55) 100%),
    linear-gradient(170deg, var(--cel-mid) 0%, var(--cel-deep) 62%);
}
.dd-cel-field::after {
  content: '';
  position: absolute;
  inset: 0;
  opacity: 0.5;
  background-image:
    repeating-linear-gradient(45deg, transparent 0 7px, color-mix(in srgb, var(--cel-foil) 7%, transparent) 7px 8px),
    repeating-linear-gradient(-45deg, transparent 0 7px, color-mix(in srgb, var(--cel-foil) 5%, transparent) 7px 8px);
  -webkit-mask-image: radial-gradient(120% 90% at 50% 40%, #000 0%, transparent 72%);
  mask-image: radial-gradient(120% 90% at 50% 40%, #000 0%, transparent 72%);
}

/* --- the card --------------------------------------------------------------
   One card per beat, stacked. Swapped by a class, and the crossfade is a
   transition rather than an animation on purpose: with no frames a transition
   simply applies its end state instantly, where a keyframe animation could
   strand the card mid-way. */
.dd-cel-card {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-rows: auto 1fr auto;
  /* stretch, not center: the body has to inherit the 1fr row's height so the
     headline can be fitted against a real budget rather than against its own
     content, which is circular and always "fits". */
  align-items: stretch;
  justify-items: center;
  padding: clamp(38px, 6vmin, 92px) clamp(32px, 6vw, 120px);
  text-align: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.85s ease;
}
.dd-cel-card.is-on { opacity: 1; visibility: visible; }

/* The double rule. Two hairlines at different insets — the engraved border of
   a printed invitation, and the cheapest possible way to say "this was made
   for you" rather than "this is a slide". */
.dd-cel-card::before,
.dd-cel-card::after {
  content: '';
  position: absolute;
  pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--cel-foil) 42%, transparent);
}
.dd-cel-card::before { inset: clamp(16px, 2.6vmin, 40px); }
.dd-cel-card::after {
  inset: clamp(22px, 3.6vmin, 54px);
  border-color: color-mix(in srgb, var(--cel-foil) 20%, transparent);
}

/* --- letterhead ------------------------------------------------------------
   The venue's mark on a paper plaque. The plaque is not decoration: a logo
   drawn for white paper would otherwise disappear into a deep field, and this
   screen has exactly one job that depends on the logo being visible. */
.dd-cel-head {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: clamp(46px, 8vh, 104px);
  padding: clamp(8px, 1.2vmin, 18px) clamp(16px, 2.6vmin, 34px);
  background: var(--cel-paper);
  border-radius: 2px;
  box-shadow: 0 clamp(6px, 1.4vmin, 18px) clamp(20px, 4vmin, 48px) rgba(0, 0, 0, 0.38);
}
/* A mark drawn for a dark field wants no paper at all — putting a white logo
   on cream is the same mistake as putting a black one on burgundy, and it is
   the mistake nobody catches, because the plaque still looks deliberate while
   the logo inside it has quietly gone. Which case this is comes from the
   file's own pixels (Data\LogoTone), so the screen is never guessing.

   Bare, the mark still needs to read as part of the letterhead rather than as
   something floating: it keeps the head's rhythm and gains a soft halo, which
   holds it away from the field's own gradient without drawing a box. */
.dd-cel-head--bare {
  background: none;
  box-shadow: none;
  padding: clamp(4px, 0.8vmin, 12px) 0;
}
.dd-cel-head--bare .dd-cel-logo {
  height: clamp(34px, 6.4vh, 84px);
  filter: drop-shadow(0 clamp(2px, 0.5vmin, 6px) clamp(8px, 1.8vmin, 22px) rgba(0, 0, 0, 0.45));
}
.dd-cel-head--bare .dd-cel-venue { color: var(--cel-ink); }

.dd-cel-logo {
  display: block;
  flex: 0 0 auto;
  /* A definite height, not max-height. The plaque is sized shrink-to-fit by
     its own content, so a logo whose height is only bounded leaves the two
     sizing each other in a circle — which Chrome resolves by giving the image
     zero width, i.e. an empty cream rectangle where the venue's mark should
     be. A definite height plus the intrinsic ratio gives a definite width. */
  height: clamp(30px, 5.6vh, 72px);
  width: auto;
  max-width: min(46vw, 460px);
  object-fit: contain;
}
.dd-cel-venue {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: clamp(17px, 2.2vh, 34px);
  letter-spacing: 0.02em;
  color: #241a12;
}

.dd-cel-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 1.6vh, 26px);
  width: 100%;
  min-height: 0;
}

.dd-cel-ornament { color: var(--cel-foil); line-height: 0; }
.dd-cel-ornament svg {
  width: clamp(38px, 6.4vh, 84px);
  height: clamp(38px, 6.4vh, 84px);
  opacity: 0.9;
}

.dd-cel-eyebrow {
  margin: 0;
  font-family: 'Barlow Condensed', 'Arial Narrow', sans-serif;
  font-weight: 600;
  font-size: clamp(15px, 2.4vh, 38px);
  line-height: 1.1;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--cel-foil) 88%, var(--cel-ink));
}

/* --- the name --------------------------------------------------------------
   The subject of the photograph. Sized by clamp() and then fitted down by JS
   for the names clamp() cannot know about. */
.dd-cel-name {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Takes the column's slack so its height is a BUDGET the fit can measure
     against, not a mirror of the text it contains. */
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
  margin: 0;
  overflow: hidden;
  font-family: 'Fraunces', Georgia, serif;
  font-optical-sizing: auto;
  font-weight: 600;
  font-size: clamp(46px, 12.5vw, 250px);
  line-height: 0.94;
  letter-spacing: -0.015em;
}
.dd-cel-name-text {
  display: block;
  /* Flat foil is the floor. Everything below is an enhancement. */
  color: var(--cel-foil);
  text-shadow: 0 clamp(2px, 0.5vmin, 8px) clamp(14px, 3vmin, 40px) rgba(0, 0, 0, 0.45);
}

/* The gradient foil replaces the flat colour ONLY where the browser can clip a
   background to text. Without the guard, an engine that ignores background-clip
   but honours text-fill-color renders the guest's name invisible — the single
   worst thing this screen could do. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .dd-cel-name-text {
    background-image: linear-gradient(
      100deg,
      var(--cel-foil) 0%,
      var(--cel-foil-hi) 20%,
      var(--cel-foil) 40%,
      var(--cel-foil) 60%,
      var(--cel-foil-hi) 80%,
      var(--cel-foil) 100%
    );
    background-size: 240% 100%;
    background-position: 0% 50%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    animation: dd-cel-foil 7s ease-in-out infinite;
  }
}
@keyframes dd-cel-foil {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}

.dd-cel-from {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(2px, 0.5vh, 8px);
  margin: clamp(2px, 0.8vh, 14px) 0 0;
}
.dd-cel-from-label {
  font-family: 'Barlow Condensed', 'Arial Narrow', sans-serif;
  font-size: clamp(12px, 1.7vh, 26px);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--cel-ink) 62%, transparent);
}
.dd-cel-from-name {
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-style: italic;
  font-size: clamp(24px, 4.6vh, 76px);
  line-height: 1.12;
  color: var(--cel-ink);
}

/* --- the message beat ------------------------------------------------------ */
.dd-cel-kicker {
  margin: 0;
  font-family: 'Barlow Condensed', 'Arial Narrow', sans-serif;
  font-weight: 600;
  font-size: clamp(14px, 2.1vh, 32px);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--cel-foil) 85%, var(--cel-ink));
}
.dd-cel-message {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1 1 auto;
  width: min(100%, 62ch);
  min-height: 0;
  margin: 0;
  overflow: hidden;
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-style: italic;
  font-size: clamp(26px, 5.4vw, 96px);
  line-height: 1.22;
  color: var(--cel-ink);
}
.dd-cel-message-text { display: block; }
.dd-cel-message::before,
.dd-cel-message::after {
  position: absolute;
  font-family: 'Fraunces', Georgia, serif;
  font-size: 2.4em;
  line-height: 1;
  color: color-mix(in srgb, var(--cel-foil) 34%, transparent);
  pointer-events: none;
}
.dd-cel-message::before { content: '\201C'; top: -0.32em; left: -0.42em; }
.dd-cel-message::after { content: '\201D'; right: -0.42em; bottom: -0.62em; }
.dd-cel-sign {
  margin: clamp(6px, 1.4vh, 22px) 0 0;
  font-family: 'Cormorant Garamond', Georgia, serif;
  font-size: clamp(19px, 3.2vh, 46px);
  color: color-mix(in srgb, var(--cel-ink) 76%, transparent);
}

/* --- the closing rule ------------------------------------------------------ */
.dd-cel-rule {
  position: relative;
  width: min(46%, 420px);
  height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--cel-foil) 70%, transparent), transparent);
}
.dd-cel-rule::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 7px;
  height: 7px;
  background: var(--cel-foil);
  transform: translate(-50%, -50%) rotate(45deg);
}

/* --- motes -----------------------------------------------------------------
   Confetti, petals, sparks or stars depending on the theme. Their resting
   state is a scattering of small marks, which is presentable on its own; the
   drift is a bonus for screens that actually paint. */
.dd-cel-motes { position: absolute; inset: 0; pointer-events: none; }
.dd-cel-mote {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: calc(var(--s) * 9px);
  height: calc(var(--s) * 9px);
  opacity: 0.55;
  transform: rotate(var(--r));
  animation: dd-cel-drift var(--t) linear var(--d) infinite;
}
.dd-cel-motes[data-kind='confetti'] .dd-cel-mote {
  height: calc(var(--s) * 15px);
  background: color-mix(in srgb, var(--cel-foil) 80%, transparent);
  border-radius: 1px;
}
.dd-cel-motes[data-kind='confetti'] .dd-cel-mote:nth-child(3n) { background: color-mix(in srgb, var(--cel-ink) 60%, transparent); }
.dd-cel-motes[data-kind='petal'] .dd-cel-mote {
  background: color-mix(in srgb, var(--cel-foil) 62%, transparent);
  border-radius: 100% 0 100% 0;
}
.dd-cel-motes[data-kind='spark'] .dd-cel-mote {
  width: calc(var(--s) * 5px);
  height: calc(var(--s) * 5px);
  background: var(--cel-foil-hi);
  border-radius: 50%;
  box-shadow: 0 0 calc(var(--s) * 10px) color-mix(in srgb, var(--cel-foil) 70%, transparent);
}
.dd-cel-motes[data-kind='star'] .dd-cel-mote {
  background: var(--cel-foil-hi);
  clip-path: polygon(50% 0%, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0% 50%, 40% 40%);
  opacity: 0.7;
}
@keyframes dd-cel-drift {
  0% { transform: translate3d(0, 12vh, 0) rotate(var(--r)); opacity: 0; }
  12% { opacity: 0.6; }
  88% { opacity: 0.6; }
  100% { transform: translate3d(3vw, -22vh, 0) rotate(calc(var(--r) + 220deg)); opacity: 0; }
}

.dd-cel-grain {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.32;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='c'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23c)' opacity='0.3'/%3E%3C/svg%3E");
}

/* --- entrance --------------------------------------------------------------
   Staggered, with `backwards` fill so nothing flashes its final state during
   its delay. That fill is also the risk: on a screen that never composites, a
   backwards-filled element stays in its FROM state forever. celebration.js
   adds .is-settled after 2.4s on a plain setTimeout, which cancels every
   entrance and leaves the resting styles — the card, intact, either way. */
.dd-cel-card.is-on .dd-cel-head { animation: dd-cel-in 0.9s cubic-bezier(0.2, 0.7, 0.25, 1) 0.05s backwards; }
.dd-cel-card.is-on .dd-cel-ornament { animation: dd-cel-in 0.9s cubic-bezier(0.2, 0.7, 0.25, 1) 0.18s backwards; }
.dd-cel-card.is-on .dd-cel-eyebrow,
.dd-cel-card.is-on .dd-cel-kicker { animation: dd-cel-in 0.9s cubic-bezier(0.2, 0.7, 0.25, 1) 0.3s backwards; }
.dd-cel-card.is-on .dd-cel-name,
.dd-cel-card.is-on .dd-cel-message { animation: dd-cel-rise 1.15s cubic-bezier(0.16, 0.8, 0.24, 1) 0.42s backwards; }
.dd-cel-card.is-on .dd-cel-from,
.dd-cel-card.is-on .dd-cel-sign { animation: dd-cel-in 0.9s cubic-bezier(0.2, 0.7, 0.25, 1) 0.66s backwards; }
.dd-cel-card.is-on .dd-cel-rule { animation: dd-cel-in 0.9s ease 0.8s backwards; }

.dd-cel.is-settled .dd-cel-head,
.dd-cel.is-settled .dd-cel-ornament,
.dd-cel.is-settled .dd-cel-eyebrow,
.dd-cel.is-settled .dd-cel-kicker,
.dd-cel.is-settled .dd-cel-name,
.dd-cel.is-settled .dd-cel-message,
.dd-cel.is-settled .dd-cel-from,
.dd-cel.is-settled .dd-cel-sign,
.dd-cel.is-settled .dd-cel-rule { animation: none; }

@keyframes dd-cel-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
@keyframes dd-cel-rise {
  from { opacity: 0; transform: translateY(38px) scale(0.965); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .dd-cel-mote,
  .dd-cel-name-text,
  .dd-cel-card.is-on * { animation: none; }
}

/* The restaurant's own chrome steps aside while a celebration is on: the name
   is the content now, and a menu card in the corner would only turn up in
   someone's photograph. */
.dd-showcase--celebrating .dd-restaurant-chrome { display: none; }

/* ------------------------------------------------------------------- web role --
   A screen pointed at a page somebody else renders. Full bleed, no chrome, no
   scrollbars: the frame is the screen, and anything this stylesheet drew
   around it would be a border around another design's work. */
.dd-web {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  background: var(--dd-bg-a, #07101f);
}

.dd-web-empty {
  margin: auto;
  padding: 40px;
  text-align: center;
  font-family: var(--serif, Georgia, serif);
  font-size: clamp(22px, 2.4vw, 44px);
  color: var(--dd-ink-soft, rgba(245, 241, 233, 0.66));
}
