/* ============================================================================
   EDEN SCALE — one device ladder for tablet and television.
   ----------------------------------------------------------------------------
   Client 2026-09-07: "make sure it is optimized for tab and TV both. Make sure
   all the sizes, alignment, spacing, everything is double checked."

   HOW THIS WORKS
   Every fixed dimension in the shell chrome (nav tray, landing caption, gate
   screen, FAB, back button) was converted to a custom property. This sheet
   owns those properties. A single multiplier — `--ui` — drives the whole set,
   so a breakpoint changes ONE number and the entire composition scales in
   proportion. No rule is ever re-stated per band, which is what used to make
   this app drift out of alignment at unusual viewports.

   Edge anchors (--nav-top / --nav-left / --edge-x / --edge-y) are set per band
   rather than multiplied, because television needs a real overscan-safe inset
   (~3-4% of the frame) and a tablet must NOT waste that much bezel.

   LOAD ORDER: last. It only declares custom properties (plus a handful of
   scoped rules for dimensions that live in sheets not worth tokenising), so
   nothing here fights the cascade — custom properties resolve on the element
   regardless of where the `var()` that consumes them was written.

   VERIFIED MATRIX
     Tablet landscape   1024x768 · 1180x820 · 1280x800 · 1366x1024
     Tablet portrait     768x1024 · 820x1180 · 1024x1366
     Desktop            1440x900 · 1512x982
     Television         1920x1080 · 2560x1440 · 3840x2160
   ============================================================================ */

/* ══ BASE — the tablet the app was drawn at (1280x800 / 1366x1024) ═══════════
   --ui:1 here by definition. Do not retune these numbers; retune --ui. */
:root{
  --ui:1;

  /* nav tray ------------------------------------------------------------- */
  --nav-gap:calc(14px * var(--ui));
  --nav-head:calc(56px * var(--ui));
  --nav-head-r:calc(17px * var(--ui));
  --nav-burger-w:calc(23px * var(--ui));
  --nav-burger-h:calc(15.8px * var(--ui));
  --nav-burger-bar:calc(1.6px * var(--ui));
  --nav-burger-fold:calc(7.1px * var(--ui));
  --nav-tray-pad:calc(7px * var(--ui));
  --nav-tray-r:calc(22px * var(--ui));
  --nav-tray-gap:calc(6px * var(--ui));
  --nav-tile-w:calc(96px * var(--ui));
  --nav-tile-r:calc(16px * var(--ui));
  --nav-tile-gap:calc(7px * var(--ui));
  --nav-tile-pad:calc(11px * var(--ui)) calc(4px * var(--ui)) calc(9px * var(--ui));
  --nav-label:calc(10.5px * var(--ui));
  --nav-icon:calc(25px * var(--ui));

  /* landing caption ------------------------------------------------------ */
  /* The old floor was 14px, which is what a 1024- or 1280-wide tablet actually
     landed on (1.05vw = 10.8-13.4px, floored). At a sales-kiosk viewing
     distance of ~50cm that is the smallest thing on the screen and the only
     sentence on it. Floor raised to 15px and the ramp steepened so a 1366
     tablet reads 17px and a 1080p television, once --ui applies, reads ~27px. */
  --landing-fs:calc(clamp(15px,1.25vw,20px) * var(--ui));
  --landing-measure:min(58ch,54vw);
  --cap-feather-x:calc(58px * var(--ui));
  --cap-feather-y:calc(34px * var(--ui));

  /* shell + frame anchors ------------------------------------------------ */
  --nav-top:28px;
  --nav-left:clamp(20px,2vw,34px);
  --edge-x:clamp(24px,4vw,74px);
  --edge-y:clamp(26px,5vh,68px);

  /* Safe zones for the fixed chrome ---------------------------------------
     The nav tray and the back button are position:fixed, so nothing in a view
     knows they are there. These two tokens are the y a view's own content may
     start at without running under them, and the square the presenter FAB
     occupies in the bottom-right corner. Both scale with the ladder, so a view
     that respects them clears the chrome at every viewport instead of at the
     one it was eyeballed on. */
  --nav-block:calc(82px * var(--ui));         /* measured tray height at --ui:1 */
  --content-top:calc(var(--nav-top) + var(--nav-block) + 22px * var(--ui));
  --back-block:calc(var(--nav-top) + var(--back-size) + 16px * var(--ui));

  /* controls ------------------------------------------------------------- */
  --fab-size:calc(58px * var(--ui));
  --fab-inset:22px;
  /* the corner square the FAB owns — content must not enter it */
  --fab-gutter:calc(var(--fab-size) + var(--fab-inset) + 14px * var(--ui));
  --back-size:calc(46px * var(--ui));
}

/* Consume the control tokens. These three live in sheets (fab.css, v8-shell)
   that are not worth tokenising wholesale for two dimensions each. */
/* ONE position, every view. fab.css used to lift this to bottom:236px on the
   villa view to dodge that panel's action row; that made the button move as you
   navigated AND still collided at other viewports. It is pinned here instead,
   and the views that reach into the corner reserve --fab-gutter for it. */
.efab-root{right:var(--fab-inset)!important;bottom:var(--fab-inset)!important}
.efab-main{width:var(--fab-size);height:var(--fab-size)}
#backButton{
  min-width:var(--back-size)!important;
  height:var(--back-size)!important;
  font-size:calc(26px * var(--ui))!important;
}


/* ══ TABLET PORTRAIT — 768x1024 · 820x1180 · 1024x1366 ══════════════════════
   The landing caption measure is written as a fraction of the WIDTH, so in
   portrait it collapsed to ~415px on a 768 tablet — a two-word-per-line column
   under a full-bleed render. Let it use the width it has; the ch cap still
   stops it becoming an unreadable long line. The montage itself needs nothing:
   object-fit:cover already handles any aspect. */
@media (orientation:portrait){
  :root{
    --landing-measure:min(58ch,82vw);
    --edge-y:clamp(30px,3.6vh,58px);
  }
  /* Portrait crops a 16:9 render to roughly its middle third. Bias the frame
     a touch above centre so architecture and canopy stay in shot rather than
     tarmac and hedging. */
  .im-shot img{object-position:center 42%}
}

/* Small portrait tablet (768/810/820 wide): three 96px tiles + the 56px head
   plus gaps is ~370px, which fits — but the tray then sits within ~50px of the
   right edge of the safe band on 768. Ease the tile a little so the whole nav
   reads as a comfortable island rather than a bar. */
@media (orientation:portrait) and (max-width:840px){
  :root{
    --nav-tile-w:calc(86px * var(--ui));
    --nav-left:22px;
    --edge-x:26px;
  }
}


/* ══ SHORT LANDSCAPE — 1024x768 · 1280x800 · 1180x820 ═══════════════════════
   Vertical room is the scarce axis here, not horizontal. Pull the bottom
   anchor in so the caption is not crowded by the frame edge, and keep the nav
   where it is (it already clears the top comfortably at 800px tall). */
@media (orientation:landscape) and (max-height:840px){
  :root{
    --edge-y:clamp(24px,4.4vh,44px);
    --cap-feather-y:calc(28px * var(--ui));
  }
}


/* ══ TELEVISION · 1080p — 1920x1080 ═════════════════════════════════════════
   A 56px nav head and a 19px caption are correct at 60cm on a tablet and
   illegible at 3m on a screen. Step the whole chrome up together, and move
   every anchor inside a ~3.5% overscan-safe margin — consumer panels still
   crop the outer 2-5% of the signal, and a caption sitting 26px off the edge
   is simply cut off. */
@media (orientation:landscape) and (min-width:1700px){
  :root{
    --ui:1.22;
    --nav-top:clamp(34px,3.6vh,52px);
    --nav-left:clamp(40px,3vw,72px);
    --edge-x:clamp(58px,3.6vw,96px);
    --edge-y:clamp(44px,6vh,84px);
    --fab-inset:clamp(28px,2.2vw,52px);
    --landing-measure:min(58ch,46vw);
  }
}

@media (orientation:landscape) and (min-width:1900px){
  :root{
    --ui:1.34;
    --nav-top:clamp(40px,4vh,62px);
    --nav-left:clamp(52px,3.2vw,86px);
    --edge-x:clamp(70px,3.9vw,116px);
    --edge-y:clamp(52px,6.6vh,96px);
    --fab-inset:clamp(34px,2.4vw,60px);
  }
}


/* ══ TELEVISION · 1440p — 2560x1440 ═════════════════════════════════════════
   The ladder is tuned to hold ONE effective design width (~1430-1520 CSS px)
   at every band — viewport / --ui is deliberately near-constant across 1080p,
   1440p and 4K. That is what makes the interface the same physical size on a
   65" panel regardless of its pixel count; scaling by a gentler curve instead
   would make a 4K TV render the app smaller than a 1080p one, which is exactly
   backwards for a screen viewed from the same three metres. */
@media (orientation:landscape) and (min-width:2400px){
  :root{
    --ui:1.78;
    --nav-top:clamp(54px,4.2vh,84px);
    --nav-left:clamp(70px,3.2vw,110px);
    --edge-x:clamp(92px,3.9vw,150px);
    --edge-y:clamp(68px,6.6vh,124px);
    --fab-inset:clamp(44px,2.4vw,74px);
  }
}


/* ══ TELEVISION · 4K — 3840x2160 ════════════════════════════════════════════
   Most 4K panels report the full 3840 CSS pixels at devicePixelRatio 1, so
   without this band the entire interface renders at a quarter of its intended
   physical size. The multiplier is deliberately under 2.0: at 4K the type is
   already pin-sharp, so it needs presence, not literal 1:1 magnification. */
@media (orientation:landscape) and (min-width:3200px){
  :root{
    --ui:2.66;
    --nav-top:clamp(76px,4.2vh,120px);
    --nav-left:clamp(100px,3.2vw,150px);
    --edge-x:clamp(132px,3.9vw,200px);
    --edge-y:clamp(98px,6.6vh,170px);
    --fab-inset:clamp(64px,2.4vw,100px);
    --landing-measure:min(58ch,42vw);
  }
}


/* ══ THE DOWNLOAD GATE — the literal first screen ═══════════════════════════
   It is the one surface a presenter sees before anything else, and every
   dimension in download-gate.css is hard px tuned for a tablet. On a 4K panel
   the whole gate was a 600px island in a 3840px frame. Scale it with the same
   --ui ladder so the first impression matches the rest of the app. */
.eg-screen{
  max-width:min(calc(600px * var(--ui)),86vw);
  padding:calc(40px * var(--ui));
}
.eg-screen .eg-logo{width:calc(150px * var(--ui));margin-bottom:calc(14px * var(--ui))}
.eg-screen .eg-wordmark{font-size:calc(30px * var(--ui));margin-bottom:calc(16px * var(--ui))}
.eg-screen .eg-rule{width:calc(150px * var(--ui));margin-bottom:calc(14px * var(--ui))}
.eg-screen .eg-sub{font-size:calc(11px * var(--ui));margin-bottom:calc(46px * var(--ui))}
.eg-screen .eg-eyebrow{font-size:calc(11px * var(--ui));margin-bottom:calc(12px * var(--ui))}
.eg-screen .eg-lede{font-size:calc(13px * var(--ui));margin-bottom:calc(30px * var(--ui))}
/* download-gate.css caps the lede at a flat 250px — a ~19-character measure at
   its 15.5px tablet size, which is the right rhythm. Unscaled against a 34px
   television type size it became a seven-line ribbon about nine characters
   wide. Scaling the cap holds the measure at 19ch on every screen. */
.eg-lede{max-width:calc(250px * var(--ui))}
/* The compact (corner) gate card and its own inset.
   MUST exclude .eden-gate--screen. Both classes sit on the SAME element, and
   download-gate.css undoes the corner geometry for the fullscreen state with
   `left:auto; bottom:auto; max-width:none` at equal specificity (0,1,0). This
   sheet loads last, so a bare `.eden-gate` here won that tie and re-applied the
   corner geometry to the fullscreen gate — which rendered the whole download
   screen as a ~415px strip pinned to the left edge. `:not()` keeps this rule
   off the fullscreen variant entirely. */
.eden-gate:not(.eden-gate--screen){
  max-width:min(calc(340px * var(--ui)),42vw);
  left:var(--nav-left);bottom:var(--edge-y);
}
.eg-screen .eg-download{min-width:calc(320px * var(--ui))}
.eg-screen .eg-skip{font-size:calc(10.5px * var(--ui));margin-top:calc(22px * var(--ui))}
.eg-screen .eg-check{width:calc(52px * var(--ui));height:calc(52px * var(--ui))}
.eg-screen .eg-done-copy b{font-size:calc(26px * var(--ui))}
.eg-screen .eg-done-copy small{font-size:calc(11px * var(--ui))}
.eg-enter{min-width:calc(280px * var(--ui))}
.eden-gate--screen[data-state="running"] .eg-card{max-width:min(calc(620px * var(--ui)),88vw)}


/* ══ TOUCH TARGETS ══════════════════════════════════════════════════════════
   A tile is 96x~62 at --ui:1 — wide enough, but shorter than the 48px comfort
   floor once the label wraps. Guarantee the floor on every pointer-coarse
   device without changing the drawn shape on a mouse-driven display. */
@media (pointer:coarse){
  .tm-items button{min-height:calc(58px * var(--ui))!important}
  .vs-sides button,
  .gal-switch button,
  .poi-cats button{min-height:calc(46px * var(--ui))!important}
}


/* ══ SECONDARY CHROME — the rails inside the other views ════════════════════
   The nav tray scales with --ui; without this block the gallery switch, the
   location category rail, the east/west toggle, the back button and the estate
   legend would NOT, so on a 4K panel a 90px nav tile sat beside a 40px filter
   chip. Everything below is written as `base * var(--ui)`, and --ui is exactly
   1 on every tablet in the matrix — so none of this changes a single pixel of
   the signed-off tablet composition. It only exists above 1700px. */

/* the back control mirrors the nav: same overscan inset, and centred on the
   burger head rather than floating 15px above it (it used to be pinned at a
   flat right:30/top:28, which on a television sits inside the overscan crop) */
.top-actions{
  right:var(--nav-left);
  top:calc(var(--nav-top) + (var(--nav-head) - var(--back-size)) / 2);
}

/* gallery tab switch */
.gal-switch button{
  min-height:calc(40px * var(--ui));
  padding:0 calc(clamp(16px,1.5vw,26px) * var(--ui));
  border-radius:calc(9px * var(--ui));
  font-size:calc(clamp(11px,.78vw,14px) * var(--ui));
}

/* location category rail */
.poi-cats button,
.location-view .poi-cats button{
  min-width:calc(168px * var(--ui));
  min-height:calc(46px * var(--ui));
  padding:0 calc(20px * var(--ui));
  border-radius:calc(12px * var(--ui));
  font-size:calc(clamp(10px,.7vw,12px) * var(--ui));
}
/* east / west orientation toggle */
.vs-sides button{
  min-height:calc(42px * var(--ui));
  padding:0 calc(clamp(16px,1.4vw,24px) * var(--ui));
  border-radius:calc(12px * var(--ui));
  font-size:calc(clamp(11px,.74vw,13px) * var(--ui));
}

/* NOTE: the glass tray shell (gap / padding / radius), the tile radius, the
   four trays' bottom padding and the location rail's 44px touch floor all live
   in eden-glass.css behind !important. They are multiplied by --ui at source
   there rather than duplicated here, so there is one definition of each. */

/* estate legend column */
.est-index{
  width:min(calc(252px * var(--ui)),19vw);
  padding:calc(6px * var(--ui)) calc(10px * var(--ui));
}
.est-ix{
  min-height:calc(38px * var(--ui));
  gap:calc(11px * var(--ui));
  padding:calc(4px * var(--ui)) calc(6px * var(--ui));
  font-size:calc(9.5px * var(--ui));
}
.est-ix b{
  width:calc(23px * var(--ui));height:calc(23px * var(--ui));
  font-size:calc(11px * var(--ui));
}


/* ══ ESTATE LOTS — a transparent button on every mansion ════════════════════
   Client 2026-09-07: "bring those numbering back with clickable option, make
   sure to give proper transparent button on the building."

   The button is the building: estate-real.js sizes each one to that villa's
   footprint and clips it to the roof outline, so the whole roof is the tap
   target rather than a 20px chip floating beside it. At rest it is genuinely
   transparent — the render is untouched — and only the numeral shows. Touch,
   hover or selection is what brings the fill up.

   The box must NOT counter-scale (it is welded to the roof); only the numeral
   does, via --inv, so it stays one legible size from overview to full zoom. */
#masterplanView .est-lot{
  position:absolute;
  margin:0;padding:0;border:0;
  background:transparent;
  -webkit-appearance:none;appearance:none;
  cursor:pointer;
  display:grid;place-items:center;
  /* a hair of inset keeps neighbouring roofs from sharing an edge pixel */
  outline:none;
  transition:background .22s ease, box-shadow .22s ease;
  -webkit-tap-highlight-color:transparent;
}
/* THE NUMBER PLATE — a square brand chip sitting on the roof.
   Client 2026-09-07: "make the numbering a little nicer and bold, make it like
   a nice button square shape and put the number on top, use Embassy font only,
   make it a little bigger so it looks nice."

   So the numeral is no longer bare type on the render: it is a rounded-square
   glass plate in the same material as every other control in the app, with the
   numeral set in ivyora-display — the Embassy display face the lockup and the
   headings use — rather than the UI sans it had before.

   The plate SCALES WITH THE PLAN via --lotk (written by estate-real.js each
   camera frame): ~20px at overview, easing up to a 48px ceiling as the camera
   dives. The footprint around it stays the tap target, so the whole roof is
   pressable, not just the plate. */
#masterplanView .est-lot > b{
  width:32px;height:32px;
  display:grid;place-items:center;
  border-radius:10px;
  /* 700, not 600: ivyora-display ships 300/400/500/700 and has no 600 cut, so
     asking for 600 leaves the weight to the browser's nearest-match rule. 700
     is a real loaded face — genuinely bold, never synthesised. */
  font:700 16px/1 var(--serif);
  letter-spacing:.005em;
  font-variant-numeric:lining-nums tabular-nums;
  color:var(--e-ink-strong);
  background:linear-gradient(155deg,rgba(255,250,246,.95),rgba(254,236,222,.90));
  border:1px solid rgba(255,255,255,.88);
  /* NO backdrop-filter here, deliberately. There are 90 of these plates and
     they live inside a subtree that transforms every frame; on an iPad each one
     forces its own compositing layer and re-samples the backdrop behind it on
     every zoom step, which was a large part of why the masterplan stuttered
     (client, 2026-09-09). The plate is ~95% opaque, so the blur was doing
     almost nothing visible for a very high price.
     One shadow rather than three, for the same reason — 90 multi-layer shadows
     is a lot of rasterisation to redo as the plan scales. */
  box-shadow:
    0 3px 8px -2px rgba(63,89,58,.42),
    inset 0 1px 0 rgba(255,255,255,.9);
  /* --lotk is written by estate-real.js every camera frame: the plate is small
     when the plan is zoomed out and grows as you dive in, instead of holding
     one fixed screen size. */
  transform:scale(var(--lotk,1));
  transform-origin:center center;
  pointer-events:none;
  text-shadow:none;
  transition:background .22s ease,color .22s ease,box-shadow .22s ease;
}

#masterplanView .est-lot:hover > b{
  background:linear-gradient(155deg,#fff6f0,#fdeadb);
  box-shadow:0 6px 14px -3px rgba(63,89,58,.5),inset 0 1px 0 rgba(255,255,255,.95);
}
#masterplanView .est-lot.is-selected > b{
  background:linear-gradient(155deg,var(--eden-green,#3f593a),var(--eden-green-deep,#2c3f28));
  border-color:rgba(255,248,243,.92);
  color:#fff2eb;
  box-shadow:0 6px 16px -4px rgba(44,63,40,.6),inset 0 1px 0 rgba(255,255,255,.28);
}

@media (pointer:coarse){
  /* no hover on touch — make the press state carry it instead */
  #masterplanView .est-lot:hover{background:transparent;box-shadow:none}
  #masterplanView .est-lot:active{background:rgba(63,89,58,.32)}
}

/* ══ FILM PANE — give the video its controls back ═══════════════════════════
   The browser draws a <video>'s control bar at the BOTTOM EDGE OF THE ELEMENT,
   full width. The presenter FAB is pinned to the bottom-right corner app-wide,
   which put it straight on top of that bar's fullscreen and picture-in-picture
   buttons.

   Client 2026-09-07: "i need that button of annotate, please keep that, but
   make sure it doesn't overlap on any functionality." So the FAB stays exactly
   where it is on every screen — the VIDEO moves instead. The stage stops short
   of a dock tall enough to clear the whole corner the FAB owns (--fab-gutter,
   the same token the villa panel reserves), so the control bar always ends
   above it. The film picker lives in that dock too, centred, so it never
   shares a band with the controls either.

   Sized as the larger of the picker's own need (96px) and the FAB's corner, so
   it stays correct at every step of the --ui ladder rather than at one. */
:root{
  /* the dock row is ~66px of controls plus its own bottom offset; the video has
     to clear that AND the FAB corner, so take the larger of the two */
  --film-dock:max(calc(112px * var(--ui)), var(--fab-gutter), var(--film-dock-measured, 0px));
}

/* ══ FIRST RUN — the gate owns the screen ═══════════════════════════════════
   Client 2026-09-07: "why do i need the top left buttons and bottom right
   buttons? remove that — this is for the first time when someone installs and
   opens first time."

   The download gate is a full-screen cover, but the nav tray, the back control
   and the presenter FAB are all position:fixed siblings, so they floated on top
   of it: three controls that do nothing until you are actually inside the app.
   download-gate.js flags <html data-gate="on"> for exactly as long as the gate
   is covering, and they stand down for that window only. */
html[data-gate="on"] .top-menu,
html[data-gate="on"] .top-actions,
html[data-gate="on"] .efab-root{
  opacity:0!important; visibility:hidden!important; pointer-events:none!important;
}

/* ══ GALLERY DOCK — tab strip and film CTAs on one line ═════════════════════
   v8-shell.css positions .gal-switch itself; the dock takes that job over so
   the strip and (on the Walkthrough pane) the two film CTAs are centred
   TOGETHER as one row rather than each centring itself and stacking. */
.gallery-view .gal-dock{
  position:absolute;z-index:27;
  /* left:0;right:0 rather than left:50% + translate. An absolutely positioned
     box with only `left` set is shrink-to-fit, and its available width is
     (container - left) — so at left:50% the dock could never exceed half the
     screen. The row measures ~944px, the cap was 920px, and it wrapped back
     into two lines, which is what pushed the tab strip onto the video controls
     in the first place. Full-width track, children centred. */
  left:0;right:0;
  /* Interiors and Life at Eden put a 96px thumbnail strip along the bottom, so
     the row has to sit ABOVE it — this is the clearance .gal-switch carried
     before the dock existed. Client 2026-09-08: "in life at eden and interiors
     the buttons are overlapping on thumbnail images, let it be a little up."
     Only the Walkthrough pane has no strip, so only it drops to the low
     position where it sits beside the film CTAs. */
  bottom:calc(96px + 14px * var(--ui));
  transform:none;
  display:flex;align-items:center;justify-content:center;
  gap:calc(10px * var(--ui));
  flex-wrap:wrap;
  /* Reserve the FAB's corner on BOTH sides so the row stays centred and can
     never run under the Annotate button. Measured at 820x1180 the row reached
     x764 while the FAB starts at x740 — a 24px collision. Padding both sides
     keeps the centring honest; where the remaining width is too narrow the row
     wraps instead, and the measured height above absorbs it. */
  padding-inline:var(--fab-gutter);
  pointer-events:none;            /* the track itself must not eat map/video taps */
}
.gallery-view .gal-dock > *{pointer-events:auto}
/* the film pane has no thumbnail strip — drop the row to the foot of the screen
   so it shares one line with the film CTAs */
.app[data-gal-pane="film"] .gallery-view .gal-dock{bottom:calc(22px * var(--ui))}
/* the strip now sits inside the dock, so it must stop positioning itself */
.gallery-view .gal-dock .gal-switch{
  position:static!important;
  left:auto!important;right:auto!important;top:auto!important;bottom:auto!important;
  transform:none!important;
}
/* and so must the picker, once it has been moved in */
.gallery-view .gal-dock .film-picker{
  position:static!important;
  left:auto!important;right:auto!important;bottom:auto!important;
  transform:none!important;
}

/* ══ LIFE AT EDEN — chips clear of the tab row ══════════════════════════════
   Client 2026-09-08: "life at eden sub buttons, push it a little up so it has
   a little space between the main button."

   v8-shell.css stacks them from the floor as
   `96 (strip) + 14 (gap) + 51 (switch) + 12 (gap)`. That 51px was the switch's
   height before the --ui ladder existed; it is 65px at --ui:1.22, so the sum
   fell 14px short and the chips ended up 5px INTO the row. Rebuilt from the
   row's measured height (published by app.js) rather than a baked-in number,
   with the gap scaled so it stays proportional on a television. */
.gal-pane[data-pane="amenities"] .gal-cats{
  top:auto!important;
  bottom:calc(96px + 14px * var(--ui) + var(--gal-dock-h, 66px) + 18px * var(--ui))!important;
}

/* ══ VIEW SWITCH — snappier ═════════════════════════════════════════════════
   styles.css cross-fades every view over 0.48s. On a desktop that reads as
   graceful; on an iPad, layered on top of real compositing work, it is the
   "taking time to switch" the client reported (2026-09-09) — half a second of
   nothing-has-happened-yet on every tap. 0.3s still reads as a fade rather than
   a cut, and the whole app feels materially quicker to answer.
   Honours reduced-motion, which styles.css did not. */
.view{transition-duration:.3s,.3s!important}
@media (prefers-reduced-motion:reduce){
  .view{transition-duration:.01s,.01s!important}
}
