/* ============================================================================
   delEat — base: reset, typography, and the primitives every page shares.
   ========================================================================= */

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

html {
  /* Round 52: the page canvas carries the ground, not just <body>. Mobile
     browsers paint the strip behind a floating address bar, and the area
     under a rubber-band overscroll, from the canvas; with only body painted
     a dark page met a pale strip at the foot of an iPhone screen. Pages with
     the scrolling ground (js/scroll.js) overwrite this as the ground fades. */
  background: var(--ground);
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 92px;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

body {
  font-family: var(--font-ui);
  font-size: var(--t-base);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  color: var(--ink);
  background: var(--ground);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "cv05" 1, "ss03" 1;
  /* Round 53c: `overflow-x: hidden` was here and is gone.
     On iOS it can promote <body> to a scrolling box, which moves the
     scrolling element off document.documentElement -- and Safari ties its
     toolbar minimisation to the document scroller. With the document not
     being the thing that scrolls, the bottom bar never collapses to the
     floating pill apple.com gets, and stays a full-width strip. That is the
     difference Parth photographed against apple.com, and it explains why the
     hero looked right and everything below it did not.
     Removed rather than softened to `clip` because nothing needs it: with the
     declaration disabled, measured across index, product, m-till, for-qsr,
     pricing, vs-sapaad and the one-pager, at 390, 768 and 1440, at 25 scroll
     positions each -- 300 measurements, every one scrollWidth == clientWidth.
     A blanket hide was masking nothing, and if something ever does overflow
     it should be a visible bug rather than a silent one. */
}

img, svg, video, canvas { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 2px solid var(--o-600);
  outline-offset: 3px;
  border-radius: var(--r-1);
}

::selection { background: var(--o-200); color: var(--n-900); }
.on-dark ::selection { background: var(--o-700); color: var(--bone); }

/* ---- headings -------------------------------------------------------------
   Display type is Inter at 700–800, set tight (--tr-tight). Rubik Black
   was here first and read as a shout above 24px; Manrope followed; Inter
   took the job on 2026-09-05 and holds the same confidence with open
   counters and a normal colour on the page.                                */
h1, h2, h3, h4, .display {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: var(--lh-head);
  letter-spacing: var(--tr-head);
  text-wrap: balance;
}
h1, .h1 { font-size: var(--t-3xl); font-weight: 800; line-height: var(--lh-tight); letter-spacing: var(--tr-tight); }
h2, .h2 { font-size: var(--t-2xl); font-weight: 800; line-height: var(--lh-tight); letter-spacing: var(--tr-tight); }
h3, .h3 { font-size: var(--t-lg); }
h4, .h4 { font-size: var(--t-md); }

p { text-wrap: pretty; }

.lede {
  font-size: var(--t-md);
  line-height: 1.5;
  color: var(--ink-2);
  max-width: 56ch;
  text-wrap: pretty;
}
.prose { max-width: var(--measure); color: var(--ink-2); }
.prose p + p { margin-top: var(--s-4); }

/* ---- the eyebrow ----------------------------------------------------------
   Small caps in mono, preceded by an orange square: the same square that is
   the period in the wordmark. It marks the start of a section the way a
   prompt marks the start of a line.                                        */
.eyebrow {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}
.eyebrow::before {
  content: ""; width: .5em; height: .5em;
  background: var(--accent);
  flex: none;
}

/* ---- layout ---------------------------------------------------------------*/
.wrap  { width: 100%; max-width: var(--page);      margin-inline: auto; padding-inline: var(--gutter); }
.wrap-wide { width: 100%; max-width: var(--page-wide); margin-inline: auto; padding-inline: var(--gutter); }
/* Round 52: the pages are viewport-fit=cover now, so on a phone held
   sideways the notch eats into the page's own gutter. Where the safe area is
   wider than the gutter, it wins; everywhere else this is the gutter. */
@supports (padding: max(0px)) {
  .wrap, .wrap-wide {
    padding-left: max(var(--gutter), env(safe-area-inset-left));
    padding-right: max(var(--gutter), env(safe-area-inset-right));
  }
}
.section { padding-block: var(--sec-y); }
.section-tight { padding-block: clamp(34px, 4vw, 56px); }
/* A `.section`'s own bottom padding is the rhythm's only budget for the gap
   to whatever comes next -- a stray margin-bottom on its last child would
   sit on top of that padding and widen the seam beyond --sec-y. Belt and
   braces: nothing inside a section's own direct wrap gets to add its own. */
.section > .wrap > :last-child,
.section > .wrap-wide > :last-child,
.section > :last-child { margin-bottom: 0; }
.stack > * + * { margin-top: var(--s-4); }
.stack-lg > * + * { margin-top: var(--s-6); }

.section-head { max-width: 780px; }
.section-head h2 { margin-top: var(--s-4); }
.section-head .lede { margin-top: var(--s-4); }

/* ---- buttons --------------------------------------------------------------
   The primary button: an orange face, a hard bottom edge, and it travels
   down when pressed. Ink sits on the orange because white on #FF6B3D is
   2.83:1 and fails; ink is 6.34:1 and passes.                              */
.btn {
  --btn-bg: var(--n-900); --btn-fg: var(--bone); --btn-edge: #000;
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  font-family: var(--font-ui);
  font-size: var(--t-sm); font-weight: 600; letter-spacing: -0.005em;
  padding: 13px 22px;
  border-radius: var(--r-3);
  background: var(--btn-bg); color: var(--btn-fg);
  box-shadow: 0 3px 0 var(--btn-edge);
  transition: transform var(--d-1) var(--snap), box-shadow var(--d-1) var(--snap),
              background var(--d-1) var(--snap);
  white-space: nowrap;
}
.btn:hover  { transform: translateY(1px); box-shadow: 0 2px 0 var(--btn-edge); }
.btn:active { transform: translateY(3px); box-shadow: 0 0 0 var(--btn-edge); }

.btn-primary { --btn-bg: var(--o-500); --btn-fg: var(--n-900); --btn-edge: var(--o-600); }
.btn-primary:hover { --btn-bg: var(--o-400); }

.btn-ghost {
  --btn-bg: transparent; --btn-fg: var(--ink); --btn-edge: transparent;
  box-shadow: inset 0 0 0 1.5px var(--line);
}
.btn-ghost:hover { --btn-bg: var(--surface); box-shadow: inset 0 0 0 1.5px var(--ink-3); transform: none; }
.btn-ghost:active { transform: translateY(1px); }

/* On the bright orange band the strongest key is CHARCOAL, not cream — the
   reference palettes all pair vivid orange with a warm black. Cream-on-orange
   is 3.0:1 and fails; cream on charcoal is 16.5:1. */
.btn-cream   { --btn-bg: #201812; --btn-fg: #FFF7F0; --btn-edge: #0C0906; }
.btn-cream:hover { --btn-bg: #2E2117; }
.btn-outline {
  --btn-bg: transparent; --btn-fg: #201812; --btn-edge: transparent;
  box-shadow: inset 0 0 0 1.5px rgba(32,24,18,.38);
}
.btn-outline:hover { --btn-bg: rgba(255,255,255,.28); box-shadow: inset 0 0 0 1.5px rgba(32,24,18,.75); transform: none; }
.btn-outline:active { transform: translateY(1px); }

.btn-lg { font-size: var(--t-base); padding: 16px 28px; border-radius: var(--r-4); }
.btn-sm { font-size: var(--t-xs); padding: 9px 15px; border-radius: var(--r-2); }

.btn-link {
  display: inline-flex; align-items: center; gap: 7px;
  font-weight: 600; font-size: var(--t-sm);
  color: var(--accent-text);
}
.btn-link .arw { transition: transform var(--d-2) var(--snap); }
.btn-link:hover .arw { transform: translateX(4px); }

/* ---- the keycap primitive: retired in Round 54 ---------------------------
   The staircase-bitten .key and its detached .crumb are gone (Parth,
   24 Sep 2026: not part of the current logo). The brand shape is now the
   capsule and the dE key, which are artwork in brand/, not CSS.            */

/* ---- surfaces -------------------------------------------------------------*/
.card {
  background: var(--surface);
  border: var(--hair);
  border-radius: var(--r-5);
  padding: var(--s-6);
}
.card-flat { background: var(--surface); border: var(--hair); border-radius: var(--r-5); }
.rule { height: 1px; background: var(--line); border: 0; }

/* ---- the machine's voice --------------------------------------------------*/
.mono, code, kbd, samp { font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-variant-ligatures: none; font-feature-settings: "liga" 0, "calt" 0; }
.num  { font-family: var(--font-mono); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }

/* Round 53: every counting number, rolling and settled. The split-flap roll
   (js/apple.js) sizes each digit cell to the widest figure in its 0-9 strip,
   so with Inter's proportional figures a number was wider while it rolled and
   snapped narrower on landing. Here rather than in apple.css because that
   sheet loads last and is not render-blocking. See the note there. */
[data-countup], [data-count], [data-total], .roll { font-variant-numeric: tabular-nums; }

/* Round 54: ONE GROUND PER PAGE ON PHONES, and it is the page's own.
   Rounds 53h and 53l (eleven iPhone recordings) established that at rest
   both of Safari's bars take the html colour, so a page with two grounds
   always shows an html-coloured band over sections of the other colour.
   53l answered with one DARK ground for every page. 54 keeps the one-ground
   rule but lets each page choose it: the ground of its first section.
   `<html data-pg="dark">` marks a dark page (tools/head45.py writes it from
   the first data-ground, so it survives regeneration); everything else is an
   eggshell page. On a dark page js/site.js and js/scroll.js still rewrite
   every eggshell section to black before the ground system reads it; on an
   eggshell page nothing is rewritten and each black section paints itself
   (css/site.css, Round 54 phone block). The one sticky nav bar is the same
   colour as this canvas, so the strip Safari paints above it cannot be seen.
   !important on purpose: it must beat the inlined first-paint canvas rule
   and any inline write from js/scroll.js, both of which are right for a
   desktop toolbar and wrong for this one. Desktop keeps both grounds and its
   crossfade. */
@media (any-pointer: coarse) {
  html { background: var(--bone) !important; }
  html[data-pg="dark"] { background: #000000 !important; }
}

/* Round 53g: NO color-scheme is declared, on purpose -- and this is a
   reversal of a rule added earlier the same day.
   Round 53 put `color-scheme: dark` on :root whenever the ground was dark,
   believing it was the missing signal for an iPhone's bars. Filmed at 4fps
   (22 Sep, 22:27) it turned out to be the OPPOSITE of what was wanted:
   `color-scheme: dark` switches Safari's whole chrome to its dark, opaque
   appearance, which it adopts within half a second of a dark section and
   sheds only 2-3.5s after leaving one -- and the bottom bar, once dark,
   stayed dark for 6s over cream. The translucent "liquid glass" bar is
   Safari's LIGHT chrome, which is what every page got before that rule
   existed, and what apple.com gets by declaring no color-scheme and no
   theme-color at all. Over a dark section the light bar reads as a pale
   strip; that is apple.com's own behaviour over its dark nav, and the
   price of the glass everywhere else. */
.aed::before { content: "AED "; font-size: .62em; font-weight: 500; color: var(--ink-3); letter-spacing: .04em; }

.tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono); font-size: var(--t-xs); font-weight: 600;
  letter-spacing: .04em; text-transform: uppercase;
  padding: 4px 9px; border-radius: var(--r-1);
  background: var(--o-50); color: var(--o-800);
}
.on-dark .tag { background: rgba(242,86,43,.16); color: var(--o-300); }
.tag-ok   { background: var(--ok-wash);   color: var(--ok-ink); }
.tag-warn { background: var(--warn-wash); color: var(--warn-ink); }
.tag-bad  { background: var(--bad-wash);  color: var(--bad-ink); }

/* ---- reveal on scroll -----------------------------------------------------
   Guarded behind .rv-armed (round 30: it was .js, which js/site.js adds on
   its first line, before the observer exists; one error in between left
   every [data-reveal] block invisible). .rv-armed is only set once the
   observer is watching. Without JavaScript, or if the script dies first,
   the content is simply there.
   A scroll animation must never be the reason something cannot be read.   */
.rv-armed [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--d-4) var(--glide), transform var(--d-4) var(--glide);
  transition-delay: var(--reveal-delay, 0ms);
}
.rv-armed [data-reveal].is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .rv-armed [data-reveal] { opacity: 1; transform: none; }
}

/* ---- utilities ------------------------------------------------------------*/
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.center { text-align: center; }
.muted  { color: var(--ink-3); }
.fine   { font-size: var(--t-xs); line-height: 1.5; color: var(--ink-3); }
.grid   { display: grid; gap: var(--s-5); }
.flex   { display: flex; }
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ---- rail edge fade ---------------------------------------------------
   Every horizontal snap rail on the site, named by selector rather than a
   class added to markup, so this one block reaches every rail whichever
   page it lives on. The rail bleeds to the viewport edge (full 100vw,
   re-padded so the first card still lands where the section's own wrap
   would put it) and fades with a mask-image at each end. js/apple.js and
   js/site.js toggle .at-start/.at-end from the rail's own scrollLeft, so
   the fade only shows on the side that actually still has more content --
   at rest, with nothing to the left, .at-start drops that side's fade and
   the first card reads at full opacity, never faded. Pager dots are a
   sibling of the rail, never inside it, so they are never masked. */
/* 2026-09: three galleries only, at every width -- highlights (.hl-track),
   what just shipped (.nc-track) and which one are you (.sv-pills, the venue
   -type picker strip -- already its own bespoke horizontal scroller, css
   below). Everything else that used to rail on mobile (.plans, the moment
   chapters, .bento-wall, .modcards) now just stacks -- see the "chapters
   stack, never rail" block in css/site.css's mobile pass. .sv-list itself
   (the icon-row detail list inside a .sv-panel) was never a rail and stays
   out of this list; only its parent picker strip carries the gallery
   grammar. .ms-track/.wcards-rail/.ps-track/.rooms-grid stay named here only
   because index-3d/video/figure.html (not owned by this pass) still
   reference them; nothing on the live site uses them. */
.hl-track, .ms-track, .wcards-rail, .nc-track, .ps-track {
  --fade-w: 40px;
  width: 100vw; margin-inline: calc(50% - 50vw); box-sizing: border-box;
  padding-inline: max(var(--gutter), calc((100vw - var(--page-wide)) / 2 + var(--gutter)));
}
@media (min-width: 761px) {
  .hl-track, .ms-track, .wcards-rail, .nc-track, .ps-track { --fade-w: 48px; }
}
@media (max-width: 700px) {
  .rooms-grid, .news, .plus-row, .plus-row-mobile, .explore-trio {
    --fade-w: 40px;
    width: 100vw; margin-inline: calc(50% - 50vw); box-sizing: border-box;
    padding-inline: var(--gutter);
  }
}
.js .hl-track, .js .ms-track, .js .wcards-rail, .js .nc-track, .js .ps-track,
.js .rooms-grid, .js .news, .js .plus-row, .js .plus-row-mobile, .js .explore-trio {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--fade-w, 40px), #000 calc(100% - var(--fade-w, 40px)), transparent);
          mask-image: linear-gradient(to right, transparent, #000 var(--fade-w, 40px), #000 calc(100% - var(--fade-w, 40px)), transparent);
}
.js .hl-track.at-start, .js .ms-track.at-start, .js .wcards-rail.at-start, .js .nc-track.at-start, .js .ps-track.at-start,
.js .rooms-grid.at-start, .js .news.at-start, .js .plus-row.at-start, .js .plus-row-mobile.at-start, .js .explore-trio.at-start {
  -webkit-mask-image: linear-gradient(to right, #000, #000 calc(100% - var(--fade-w, 40px)), transparent);
          mask-image: linear-gradient(to right, #000, #000 calc(100% - var(--fade-w, 40px)), transparent);
}
.js .hl-track.at-end, .js .ms-track.at-end, .js .wcards-rail.at-end, .js .nc-track.at-end, .js .ps-track.at-end,
.js .rooms-grid.at-end, .js .news.at-end, .js .plus-row.at-end, .js .plus-row-mobile.at-end, .js .explore-trio.at-end {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--fade-w, 40px), #000);
          mask-image: linear-gradient(to right, transparent, #000 var(--fade-w, 40px), #000);
}
.js .hl-track.at-start.at-end, .js .ms-track.at-start.at-end, .js .wcards-rail.at-start.at-end, .js .nc-track.at-start.at-end,
.js .ps-track.at-start.at-end, .js .rooms-grid.at-start.at-end,
.js .news.at-start.at-end, .js .plus-row.at-start.at-end, .js .plus-row-mobile.at-start.at-end, .js .explore-trio.at-start.at-end {
  -webkit-mask-image: none; mask-image: none;
}
@media (prefers-reduced-motion: reduce), (forced-colors: active) {
  .hl-track, .ms-track, .wcards-rail, .nc-track, .ps-track,
  .rooms-grid, .news, .plus-row, .plus-row-mobile, .explore-trio { -webkit-mask-image: none !important; mask-image: none !important; }
}
/* round 35: the same grammar on the home page's live rails, the photo card
   rail (.rl34-track, every width), the venue tabs' phone rail (.kb-sv) and
   the venue picker strip (.sv-pills). js/site.js marks all three. */
.js .rl34-track, .js .kb-sv, .js .sv-pills, .js .rail36, .js .wy54-rail {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--fade-w, 40px), #000 calc(100% - var(--fade-w, 40px)), transparent);
          mask-image: linear-gradient(to right, transparent, #000 var(--fade-w, 40px), #000 calc(100% - var(--fade-w, 40px)), transparent);
}
.js .rl34-track.at-start, .js .kb-sv.at-start, .js .sv-pills.at-start, .js .rail36.at-start, .js .wy54-rail.at-start {
  -webkit-mask-image: linear-gradient(to right, #000, #000 calc(100% - var(--fade-w, 40px)), transparent);
          mask-image: linear-gradient(to right, #000, #000 calc(100% - var(--fade-w, 40px)), transparent);
}
.js .rl34-track.at-end, .js .kb-sv.at-end, .js .sv-pills.at-end, .js .rail36.at-end, .js .wy54-rail.at-end {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--fade-w, 40px), #000);
          mask-image: linear-gradient(to right, transparent, #000 var(--fade-w, 40px), #000);
}
.js .rl34-track.at-start.at-end, .js .kb-sv.at-start.at-end, .js .sv-pills.at-start.at-end, .js .rail36.at-start.at-end, .js .wy54-rail.at-start.at-end { -webkit-mask-image: none; mask-image: none; }
/* round 36: .rail36 is the grammar as a class, for the rails the generated
   pages carry (the family links, the jump row, the Works with rows).
   Round 54: .wy54-rail is the home page's "Why run on delEat" card rail. */
.sv-pills { --fade-w: 24px; }
@media (prefers-reduced-motion: reduce), (forced-colors: active) {
  .rl34-track, .kb-sv, .sv-pills, .rail36, .wy54-rail { -webkit-mask-image: none !important; mask-image: none !important; }
}

/* Round 63: every thinking orb's (js/orbs.js) canvas fills its own box
   exactly. The box is whatever a page's own CSS sizes, responsively; the
   script only ever gives the canvas a fixed inline pixel size, to have a
   scale to run its simulation in, so that inline size has to lose here. */
[data-orb] canvas { display: block; width: 100% !important; height: 100% !important; }
