/* ==============================================================================================
   UNIFIED DECK EXPLORER - V2 PRESENTATION (North)

   WHAT THIS IS. The stylesheet for one consolidated deck experience: one nearly full-width
   deck, one compact deck navigator, one right-edge dual-purpose control, one Adaptive Duck
   Journey Card. It replaces a presentation, not an engine - the deck is still drawn by
   deck-picker.js, the zones still pulse on its own canvas, the pins are still its own markers
   at their own geometry, and the pin gate still lives on the server.

   EVERY RULE IS SCOPED html.ui-explorer. That class is set by the page's own head script and
   is absent under ?ui=legacy and ?ui=immersive, so both earlier presentations keep every byte
   of their own styling. This file cannot reach them.

   THE PALETTE IS THE OWNER'S, and it is deliberately not the previous navy-heavy surface:
   warm off-white page, white deck, dark slate type, lightly translucent floating controls.
   The branded navy footer is injected by the build and is left exactly as it is.

   WHY THE FLOATING SURFACES ARE position:fixed AND NOT IN FLOW. The deck is roughly 1:6.9, so
   it is thousands of pixels tall and the page scrolls through it. A navigator or a panel that
   lived in flow would either scroll away or, if it changed size, reflow the deck beneath it -
   and "opening the control must not resize, shift, reload or reflow the deck" is a
   requirement, not a preference. Fixed surfaces cannot reflow anything, by construction.

   AND WHY THEY READ --dx-l / --dx-r. position:fixed is viewport-relative, but on a desktop the
   experience is a centred phone-width column, not the window. Those two custom properties are
   the column's measured left and right viewport offsets, published by deck-explorer.js and
   re-published on resize. On a phone they are 0 and 100vw and every rule below collapses to
   the obvious thing.
   ============================================================================================== */

html.ui-explorer {
  /* ---- THE STAGE ---- */
  --dx-bg: #2C1E39;            /* plum: the page, and the gutter the deck sits in */
  --dx-bg-deep: #1F1B33;       /* the far end of the stage gradient, and the desktop surround */
  --dx-surface: #3A2A48;       /* panels, cards and controls: the same plum, lifted */
  --dx-paper: #FFFFFF;         /* THE DECK'S OWN PAPER. The artwork is printed white and stays
                                  white - tinting it would be recolouring the ship's plan. */
  --dx-ink: #F4EFFA;
  --dx-ink-soft: rgba(244, 239, 250, .78);
  --dx-ink-faint: rgba(244, 239, 250, .62);
  --dx-line: rgba(244, 239, 250, .20);
  --dx-glass: rgba(44, 30, 57, .93);
  --dx-shadow: 0 12px 34px -12px rgba(0, 0, 0, .62);

  /* ---- ONE COLOUR, ONE JOB. The hierarchy is the point: if two things share a colour the
          colour stops carrying meaning, which is why sharing had to leave gold - gold is
          CRAI's, and sharing was using it too. ---- */
  --dx-select: #EC3860;        /* SELECTION, AND NOTHING ELSE */
  --dx-nav: #A855F7;           /* navigation, and owner access: borders, fills, the tab */
  /* THE SAME PURPLE AT TWO READABLE WEIGHTS. #A855F7 is an identity colour and a poor text
     colour: measured on this plum it is 3.31:1 as small text, and white on it as a fill is
     3.96:1. Both are under the floor, so the text and the fill get their own values rather
     than the palette being abandoned. */
  --dx-nav-text: #D8B4FE;      /* purple text on plum - measured 8.8:1 */
  --dx-nav-solid: #7E22CE;     /* a purple fill that can carry white - measured 7.0:1 */
  --dx-crai: #F4C430;          /* CRAI only - the header's own gold, not a near-miss of it */
  --dx-crai-deep: #B07D1A;
  --dx-crai-ink: #2A1C05;
  --dx-share: #2DD4BF;         /* sharing only */
  --dx-found: #1F6FAE;         /* lifecycle */
  --dx-kept: #9B2242;          /* lifecycle, and deliberately deeper than the selection pink */
  --dx-hiding: #E24B4A;
  /* --dx-accent is the NAVIGATION colour. It kept its name because ten rules read it and
     renaming them would have been churn in a presentation pass; what it MEANS changed. */
  --dx-accent: var(--dx-nav);
  --dx-l: 0px;                 /* the content column's viewport offsets - JS republishes these */
  --dx-r: 100vw;
  background: var(--dx-bg);
}
html.ui-explorer body {
  background: var(--dx-bg);
  color: var(--dx-ink);
  /* the page scrolls vertically through the deck and never sideways */
  overflow-x: hidden;
}

/* ---- THE LEGACY COMPOSITION IS NOT PRESENT IN THIS EXPERIENCE ----
   It stays in the document because ?ui=legacy has to be able to show it, and because the
   chrome region above it and the site footer below it are injected by the build and belong to
   neither presentation. Its MOUNTS do not run under this class - the page's boot script asks
   which presentation is active before it mounts anything - so this is not a hidden second copy
   of the board quietly fetching, measuring and painting behind the new one. */
html.ui-explorer .jwrap { display: none !important; }

/* ============================ THE SHELL ============================ */
html.ui-explorer .dx {
  max-width: none;
  margin: 0;
  /* THE BREATHING ROOM BEFORE THE INJECTED FOOTER. 12 here, not the 34 this first
     carried: something below the shell already contributes 22px, measured identically at
     390, 393, 480 and 1280, so 34 put the real gap at 56 - past the 24-40 the design asks
     for. 12 + 22 lands at 34, and it was measured rather than reasoned about. */
  padding: 0 0 12px;
}
@media (min-width: 900px) {
  /* DESKTOP IS THE SAME EXPERIENCE, CENTRED - not a widescreen explorer and not a stretched
     phone. The column is designed for the pointer: the navigator and the panel gain hover
     states, and the deck gets a surface edge so it reads as an object on a desk. */
  html.ui-explorer body { background: var(--dx-bg-deep); }
  html.ui-explorer .dx {
    max-width: 500px;
    margin: 0 auto;
    background: var(--dx-bg);
    box-shadow: 0 0 0 1px var(--dx-line), 0 24px 60px -30px rgba(23, 34, 51, .35);
  }
}

/* ---- the compact page head: where you are, and the way back ---- */
html.ui-explorer .dx-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px 8px;
}
html.ui-explorer .dx-back {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; min-height: 40px;
  border: 1px solid var(--dx-line); border-radius: 12px;
  background: var(--dx-surface); color: var(--dx-ink);
  font: inherit; font-size: 17px; font-weight: 700; text-decoration: none;
  cursor: pointer;
}
html.ui-explorer .dx-head-t { flex: 1 1 auto; min-width: 0; }
html.ui-explorer .dx-ship {
  display: block; font-size: 16px; font-weight: 800; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
html.ui-explorer .dx-sail {
  display: block; font-size: 11.5px; color: var(--dx-ink-soft); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ============================ THE DECK NAVIGATOR ============================
   STICKY RATHER THAN FIXED, so it keeps its place in the flow and cannot overlap the head it
   follows, while still staying reachable through a deck thousands of pixels tall. */
html.ui-explorer .dx-nav {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 8px;
  padding: 7px 12px;
  background: var(--dx-glass);
  backdrop-filter: saturate(140%) blur(10px);
  -webkit-backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid var(--dx-line);
}
html.ui-explorer .dx-step {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border: 1px solid var(--dx-line); border-radius: 12px;
  background: var(--dx-surface); color: var(--dx-ink);
  font: inherit; font-size: 18px; font-weight: 700; line-height: 1; cursor: pointer;
}
html.ui-explorer .dx-step[disabled] { opacity: .34; cursor: default; }
/* THE DECK NAME IS A BUTTON, and the button IS the direct deck picker. One control doing the
   naming and the choosing, rather than a label beside a control that repeats it. */
html.ui-explorer .dx-deckbtn {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 40px; padding: 0 12px;
  border: 1px solid var(--dx-line); border-radius: 12px;
  background: var(--dx-surface); color: var(--dx-ink);
  font: inherit; font-size: 14.5px; font-weight: 800; cursor: pointer;
}
html.ui-explorer .dx-deckbtn .dx-deckname {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
html.ui-explorer .dx-deckbtn .dx-caret { flex: 0 0 auto; font-size: 10px; color: var(--dx-ink-soft); }
html.ui-explorer .dx-count {
  flex: 0 0 auto; font-size: 11px; font-weight: 700; color: var(--dx-ink-faint);
  font-variant-numeric: tabular-nums;
}

/* the direct picker: a plain list of the ship's real decks, no artwork */
html.ui-explorer .dx-decklist {
  position: fixed; z-index: 46;
  top: var(--dx-navbottom, 112px);
  left: calc(var(--dx-l) + 10px);
  width: calc(var(--dx-r) - var(--dx-l) - 20px);
  max-height: min(58vh, 460px); overflow-y: auto; -webkit-overflow-scrolling: touch;
  background: var(--dx-surface); color: var(--dx-ink);
  border: 1px solid var(--dx-line); border-radius: 14px;
  box-shadow: var(--dx-shadow);
  padding: 6px;
}
html.ui-explorer .dx-decklist[hidden] { display: none; }
html.ui-explorer .dx-deckrow {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 42px; padding: 8px 10px;
  border: 0; border-radius: 10px; background: none; color: inherit;
  font: inherit; font-size: 14px; text-align: left; cursor: pointer;
}
html.ui-explorer .dx-deckrow:hover { background: rgba(168, 85, 247, .14); }
html.ui-explorer .dx-deckrow[aria-current="true"] {
  background: rgba(168, 85, 247, .20); font-weight: 800;
}
html.ui-explorer .dx-deckrow .dx-deckno {
  flex: 0 0 auto; min-width: 30px; font-weight: 800; color: var(--dx-nav-text);
  font-variant-numeric: tabular-nums;
}

/* ============================ THE DECK ============================ */
html.ui-explorer .dx-stage {
  position: relative;
  padding: 10px 0 0;
  /* the gutter either side of the deck, and behind it: plum, easing deeper down the ship */
  background: linear-gradient(180deg, var(--dx-bg), var(--dx-bg-deep));
}
/* THE DECK OCCUPIES 96.5% OF THE CONTENT WIDTH, which is the owner's 96-98% band, and the
   remaining gutter is intentional rather than left over. It is NOT 100%: the hull art reaches
   the image's own left and right edge at the ship's widest beam, and a pulsing zone band is a
   stroked rect whose box measures about 7px wider than the frame on each side. Flush against
   the glass, the sponsons sit on the bezel and the band's edge is clipped away - which reads as
   a cropped deck even when nothing is mathematically cropped. */
html.ui-explorer .dx-deck { width: 100%; }
html.ui-explorer .dx-deck .dpk { font-family: inherit; color: var(--dx-ink); }
html.ui-explorer .dx-deck .dpk-browse-stage { margin: 0; }
html.ui-explorer .dx-deck .dpk-stage {
  position: relative; overflow: visible;      /* never clip the band or a marker */
  min-height: 0; border-radius: 0; text-align: center; background: transparent;
  max-height: none;
}
html.ui-explorer .dx-deck .dpk-frame {
  display: block; position: relative;
  width: 96.5%; max-width: none;
  margin: 0 auto;
  background: var(--dx-paper);
  border-radius: 10px;
  box-shadow: 0 0 0 1px rgba(168, 85, 247, .22), 0 14px 32px -18px rgba(0, 0, 0, .75);
}
/* THE WHOLE LENGTH OF THE DECK, AT ITS OWN PROPORTIONS. width only: the height follows the
   artwork, so the plan is never stretched, squashed or cropped, and the page scrolls through
   it instead of trapping it in a short box. */
html.ui-explorer .dx-deck img.dpk-deck {
  display: block; width: 100%; height: auto;
  max-width: none; max-height: none;
  border-radius: 10px;
}
html.ui-explorer .dx-deck .dpk-zc {
  position: absolute; inset: 0; width: 100%; height: 100%;
  cursor: default; border-radius: 10px;
}
html.ui-explorer .dx-deck .dpk-pl { position: absolute; inset: 0; }
/* the module's own enlarged state is not reachable here and must not be entered by a stray
   tap: the deck is already full width and scrolls as the page */
html.ui-explorer .dx-deck .dpk-stage.dpk-big { overflow: visible; max-height: none; }
html.ui-explorer .dx-deck .dpk-stage.dpk-big .dpk-pin img { width: 42px; height: 42px; border-width: 3px; }
html.ui-explorer .dx-deck .dpk-stage.dpk-big .dpk-stem { border-left-width: 7px; border-right-width: 7px; border-top-width: 12px; }
/* the rail is not built under this presentation; these are belt and braces */
html.ui-explorer .dx-deck .dpk-decks-head,
html.ui-explorer .dx-deck .dpk-deckscroll { display: none !important; }

/* ---- FOUND / KEPT LABELS IN THE UPPER LOOP ----
   A pseudo-element on the marker the engine already draws. The photograph stays the centre
   piece at its own 42px geometry, the stem is untouched, and nothing about the marker's box
   changes - so the hit test, which measures 26px around each marker's own recorded
   coordinate and never reads the DOM, is unaffected. */
html.ui-explorer .dx-deck .dpk-mark { position: absolute; }
html.ui-explorer .dx-deck .dpk-mark[data-outcome]::after {
  content: attr(data-outcome);
  position: absolute; left: 50%; top: -1px; transform: translateX(-50%);
  padding: 1px 5px; border-radius: 999px;
  font-size: 7.5px; font-weight: 800; letter-spacing: .06em; line-height: 1.5;
  color: #fff; background: var(--dx-found);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .4);
  pointer-events: none; white-space: nowrap;
}
html.ui-explorer .dx-deck .dpk-mark[data-outcome="KEPT"]::after { background: var(--dx-kept); }
/* the duck the visitor chose, said quietly on the deck itself */
/* THE SELECTED DUCK ON THE DECK: a thin pink outline, and nothing thicker. The ring is a
   box-shadow, which paints outside the element without changing its box - so the 42px
   photograph, the stem and the 26px hit test around the marker's own coordinate are all
   exactly what they were. */
html.ui-explorer .dx-deck .dpk-mark.dx-focus img {
  box-shadow: 0 0 0 2px var(--dx-select), 0 4px 10px rgba(0, 0, 0, .6);
}

/* ============================ THE RIGHT-EDGE DUAL-PURPOSE CONTROL ============================ */
html.ui-explorer .dx-tab {
  position: fixed; z-index: 44;
  top: var(--dx-tabtop, 170px);
  right: calc(100vw - var(--dx-r));
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  min-width: 34px; min-height: 92px; padding: 10px 7px;
  border: 1px solid rgba(168, 85, 247, .55); border-right: 0;
  border-radius: 14px 0 0 14px;
  background: linear-gradient(135deg, rgba(168, 85, 247, .30), rgba(168, 85, 247, .14)), var(--dx-glass);
  backdrop-filter: saturate(140%) blur(10px);
  -webkit-backdrop-filter: saturate(140%) blur(10px);
  box-shadow: var(--dx-shadow);
  color: var(--dx-ink); font: inherit; cursor: pointer;
}
html.ui-explorer .dx-tab .dx-tab-l {
  writing-mode: vertical-rl; text-orientation: mixed;
  font-size: 11px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
}
html.ui-explorer .dx-tab .dx-tab-n {
  font-size: 10.5px; font-weight: 800; color: var(--dx-nav-text);
  font-variant-numeric: tabular-nums;
}
html.ui-explorer .dx-tab[aria-expanded="true"] { opacity: 0; pointer-events: none; }

/* THE PANEL SLIDES LEFTWARD OVER THE DECK. transform only, so opening it neither reflows nor
   repaints the deck underneath. */
html.ui-explorer .dx-panel {
  position: fixed; z-index: 45;
  top: var(--dx-tabtop, 170px);
  left: calc(var(--dx-l) + 8px);
  width: calc(var(--dx-r) - var(--dx-l) - 16px);
  max-height: min(66vh, 560px);
  display: flex; flex-direction: column;
  background: var(--dx-glass);
  backdrop-filter: saturate(150%) blur(14px);
  -webkit-backdrop-filter: saturate(150%) blur(14px);
  border: 1px solid var(--dx-line); border-radius: 16px;
  box-shadow: var(--dx-shadow);
  transform: translateX(calc(100% + 24px));
  transition: transform .26s cubic-bezier(.4, 0, .2, 1);
  overflow: hidden;
}
html.ui-explorer .dx-panel.on { transform: translateX(0); }
@media (prefers-reduced-motion: reduce) {
  html.ui-explorer .dx-panel { transition: none; }
}
html.ui-explorer .dx-panel-top {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 10px 7px; border-bottom: 1px solid var(--dx-line);
}
html.ui-explorer .dx-panel-top strong {
  flex: 1 1 auto; min-width: 0; font-size: 12px; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase; color: var(--dx-ink-soft);
}
html.ui-explorer .dx-x {
  flex: 0 0 auto; width: 32px; height: 32px;
  display: grid; place-items: center;
  border: 1px solid var(--dx-line); border-radius: 9px;
  background: var(--dx-surface); color: var(--dx-ink-soft);
  font: inherit; font-size: 16px; line-height: 1; cursor: pointer;
}
html.ui-explorer .dx-split {
  display: flex; align-items: stretch; gap: 0;
  min-height: 0; flex: 1 1 auto;
}
html.ui-explorer .dx-cat {
  flex: 0 0 46%; min-width: 0;
  display: flex; flex-direction: column;
  border-right: 1px solid var(--dx-line);
  padding: 8px;
}
html.ui-explorer .dx-ducks {
  flex: 1 1 54%; min-width: 0;
  display: flex; flex-direction: column;
  padding: 8px; min-height: 0;
}

/* the left half: the current category, prominent, with its count */
html.ui-explorer .dx-catcur {
  display: flex; align-items: center; gap: 6px; width: 100%;
  min-height: 42px; padding: 7px 9px;
  border: 1px solid var(--dx-line); border-radius: 11px;
  background: var(--dx-surface); color: var(--dx-ink);
  font: inherit; font-size: 13.5px; font-weight: 800; text-align: left; cursor: pointer;
}
html.ui-explorer .dx-catcur .dx-catname { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
html.ui-explorer .dx-catcur .dx-n,
html.ui-explorer .dx-catrow .dx-n {
  flex: 0 0 auto; font-size: 11px; font-weight: 800; color: var(--dx-nav-text);
  font-variant-numeric: tabular-nums;
}
/* THE ARROWS ARE NOT OPTIONAL. Stepping by swipe is an addition; a visible, reachable control
   is the way the category is changed, so nothing here is gesture-only. */
html.ui-explorer .dx-catsteps { display: flex; gap: 6px; margin-top: 6px; }
html.ui-explorer .dx-catstep {
  flex: 1 1 0; min-width: 0; min-height: 34px;
  border: 1px solid var(--dx-line); border-radius: 9px;
  background: var(--dx-surface); color: var(--dx-ink-soft);
  font: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
}
html.ui-explorer .dx-catstep[disabled] { opacity: .34; cursor: default; }
html.ui-explorer .dx-catlist {
  margin-top: 6px; display: flex; flex-direction: column; gap: 3px;
  overflow-y: auto; -webkit-overflow-scrolling: touch; min-height: 0;
}
html.ui-explorer .dx-catlist[hidden] { display: none; }
html.ui-explorer .dx-catrow {
  display: flex; align-items: center; gap: 6px; width: 100%;
  min-height: 36px; padding: 6px 9px;
  border: 1px solid transparent; border-radius: 9px;
  background: none; color: var(--dx-ink);
  font: inherit; font-size: 12.5px; font-weight: 600; text-align: left; cursor: pointer;
}
html.ui-explorer .dx-catrow:hover { background: rgba(168, 85, 247, .14); }
html.ui-explorer .dx-catrow[aria-current="true"] {
  background: rgba(236, 56, 96, .20); border-color: var(--dx-select); font-weight: 800;
}
html.ui-explorer .dx-catrow .dx-catname { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* the right half: the Ducks control and its own independently scrollable list */
html.ui-explorer .dx-ducksbtn {
  display: flex; align-items: center; gap: 6px; width: 100%;
  min-height: 42px; padding: 7px 9px;
  border: 1px solid var(--dx-line); border-radius: 11px;
  background: var(--dx-surface); color: var(--dx-ink);
  font: inherit; font-size: 13.5px; font-weight: 800; text-align: left; cursor: pointer;
}
html.ui-explorer .dx-ducksbtn .dx-dlabel { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
html.ui-explorer .dx-ducksbtn .dx-n { flex: 0 0 auto; font-size: 11px; font-weight: 800; color: var(--dx-nav-text); font-variant-numeric: tabular-nums; }
html.ui-explorer .dx-duckslist {
  margin-top: 6px; min-height: 0; flex: 1 1 auto;
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  display: flex; flex-direction: column; gap: 3px;
  padding-right: 2px;
  /* A SCROLLER ALWAYS CUTS A ROW; WHAT IT MUST NOT DO IS LOOK BROKEN. The list ends flush
     against the Registry block below it, so the row at the boundary was sliced through the
     middle of its second line - a duck’s name intact above half a sentence, hard against a
     full-width button. That reads as text overlapping a control rather than as a list with
     more in it. The padding gives the last row somewhere to land when the list is scrolled
     to its end, and the fade says ‘there is more’ in the way every scroller on the web says
     it. Nothing about the scrolling, the selection or the rows themselves changes. */
  padding-bottom: 8px;
  scroll-padding-bottom: 8px;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 16px), transparent);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 16px), transparent);
}
html.ui-explorer .dx-duckslist[hidden] { display: none; }
html.ui-explorer .dx-duckrow {
  display: flex; align-items: center; gap: 8px; width: 100%;
  min-height: 42px; padding: 5px 7px;
  border: 1px solid transparent; border-radius: 10px;
  background: none; color: var(--dx-ink);
  font: inherit; font-size: 13px; text-align: left; cursor: pointer;
}
html.ui-explorer .dx-duckrow:hover { background: rgba(168, 85, 247, .14); }
html.ui-explorer .dx-duckrow[aria-current="true"] {
  background: rgba(236, 56, 96, .20); border-color: var(--dx-select);
}
html.ui-explorer .dx-duckrow img,
html.ui-explorer .dx-duckrow .dx-noart {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%;
  object-fit: cover; background: rgba(244, 239, 250, .14);
  display: grid; place-items: center; font-size: 15px;
}
html.ui-explorer .dx-duckrow .dx-dmeta { min-width: 0; display: flex; flex-direction: column; }
html.ui-explorer .dx-duckrow .dx-dmeta strong {
  font-size: 12.5px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
html.ui-explorer .dx-duckrow .dx-dmeta small {
  font-size: 10.5px; color: var(--dx-ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
html.ui-explorer .dx-clear {
  flex: 0 0 auto; width: 100%; min-height: 34px; margin-bottom: 3px;
  border: 1px dashed var(--dx-line); border-radius: 9px;
  background: none; color: var(--dx-ink-soft);
  font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
}
html.ui-explorer .dx-empty,
html.ui-explorer .dx-note {
  font-size: 11.5px; color: var(--dx-ink-soft); line-height: 1.45;
  padding: 8px 4px; margin: 0;
}

/* Registry's own registration control, inside Registry. The real one: the component that
   owns the roster workflow is mounted here in its picker-only presentation. */
html.ui-explorer .dx-registry { padding: 0 8px 8px; border-top: 1px solid var(--dx-line); }
html.ui-explorer .dx-registry[hidden] { display: none; }
html.ui-explorer .dx-registry .dob-pickonly { padding: 8px 0 0; }
html.ui-explorer .dx-registry .dob-pickopen {
  width: 100%; min-height: 40px;
  border: 1px solid var(--dx-nav-solid); border-radius: 11px;
  background: var(--dx-nav-solid); color: #fff;
  font: inherit; font-size: 13.5px; font-weight: 800; cursor: pointer;
}
html.ui-explorer .dx-registry .dob-picklist {
  max-height: min(34vh, 230px); overflow-y: auto; -webkit-overflow-scrolling: touch;
}
html.ui-explorer .dx-registry .dob-said { font-size: 11.5px; color: var(--dx-ink-soft); margin-top: 6px; }
html.ui-explorer .dx-registry .dob-said.good { color: #5BE0A8; }
html.ui-explorer .dx-registry .dob-said.bad { color: var(--dx-kept); }
html.ui-explorer .dx-registry .dob-off,
html.ui-explorer .dx-registry .dob-empty { font-size: 11.5px; color: var(--dx-ink-soft); margin: 7px 0 0; }
html.ui-explorer .dx-registry .dob-picktop {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 12px; font-weight: 800; padding: 2px 0 6px;
}
html.ui-explorer .dx-registry .dob-picklink {
  border: 1px solid var(--dx-line); border-radius: 8px; background: var(--dx-surface);
  color: var(--dx-ink-soft); font: inherit; font-size: 11.5px; font-weight: 700;
  min-height: 30px; padding: 0 10px; cursor: pointer;
}
html.ui-explorer .dx-registry .dob-pickrow {
  display: flex; align-items: center; gap: 8px; min-height: 34px; font-size: 12.5px;
}
html.ui-explorer .dx-registry .dob-field button,
html.ui-explorer .dx-registry #aboardGo {
  width: 100%; min-height: 40px; margin-top: 7px;
  border: 1px solid var(--dx-nav-solid); border-radius: 11px;
  background: var(--dx-nav-solid); color: #fff;
  font: inherit; font-size: 13.5px; font-weight: 800; cursor: pointer;
}

/* the source label a Registry row carries */
html.ui-explorer .dx-src {
  display: inline-block; margin-left: 0;
  font-size: 9.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--dx-ink-faint);
}

/* ============================ THE ADAPTIVE DUCK JOURNEY CARD ============================
   Fixed, so it can be placed against the marker's position in the VIEWPORT - which is what
   "keep the marker and the card visible together" means on a deck that scrolls - and so that
   opening it cannot move the deck by a pixel. */
html.ui-explorer .dx-card {
  position: relative; z-index: 1;
  /* IT LEAVES THE RIGHT-EDGE TAB ITS OWN STRIP. At the full width minus 20 the card ran under
     the tab and left it half showing, which is the look of a control that has been cut off -
     the exact complaint this design exists to answer. 44 off the width keeps the tab whole and
     reachable while a card is open, so neither control is ever obscured by the other. */
  width: 100%;
  color: var(--dx-ink);
  /* THE SURFACE IS .dx-card-frame, NOT THIS ELEMENT. The card has to be able to build itself
     outward around a photograph that is already on screen; if the background and the border
     lived here, animating them would animate the photograph and the words with them. */
  background: transparent; border: 0; box-shadow: none;
  overflow: visible;
  /* the height is handed down by placeCard from the room that is actually available, and a
     long hint scrolls inside the body rather than pushing the panels off the screen */
  display: flex; flex-direction: column;
}
html.ui-explorer .dx-card-frame {
  position: absolute; inset: 0; z-index: 0;
  background: var(--dx-surface);
  border: 1.5px solid var(--dx-select); border-radius: 16px;
  box-shadow: 0 16px 44px -16px rgba(0, 0, 0, .7), 0 0 0 1px rgba(236, 56, 96, .18);
  /* the frame's two edges travel outward from the photograph's band and meet at the sides */
  transition: clip-path .44s cubic-bezier(.2, .75, .25, 1);
  clip-path: inset(0 round 16px);
}
html.ui-explorer .dx-card-frame.dx-building { clip-path: inset(44% 0 44% 0 round 16px); }
html.ui-explorer .dx-card-top,
html.ui-explorer .dx-card-body { position: relative; z-index: 1; }
html.ui-explorer .dx-card-body { overflow-y: auto; -webkit-overflow-scrolling: touch; min-height: 0; }
/* the information floats in once the frame is on its way out to the edges */
html.ui-explorer .dx-card.dx-entering .dx-card-id,
html.ui-explorer .dx-card.dx-entering .dx-card-acts,
html.ui-explorer .dx-card.dx-entering .dx-card-body {
  opacity: 0; transform: translateY(5px);
}
html.ui-explorer .dx-card-id,
html.ui-explorer .dx-card-acts,
html.ui-explorer .dx-card-body {
  transition: opacity .34s ease .16s, transform .34s ease .16s;
}
/* THE PHOTOGRAPH'S FINAL PLACE. The element that lands here is the very one that was tapped
   on the deck - moved, not re-created - so it never reloads and never flashes. */
html.ui-explorer .dx-cardphoto {
  width: 64px !important; height: 64px !important;
  border-radius: 14px !important; object-fit: cover;
  border: 2px solid var(--dx-line);
  will-change: transform;
}
html.ui-explorer .dx-card[hidden] { display: none; }
html.ui-explorer .dx-card-top {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 10px 8px;
}
html.ui-explorer .dx-card-top img,
html.ui-explorer .dx-card-top .dx-noart {
  flex: 0 0 auto; width: 46px; height: 46px; border-radius: 50%;
  object-fit: cover; background: rgba(244, 239, 250, .14); display: grid; place-items: center; font-size: 22px;
  border: 2px solid var(--dx-line);
}
html.ui-explorer .dx-card-id { flex: 1 1 auto; min-width: 0; }
html.ui-explorer .dx-card-id strong {
  display: block; font-size: 15.5px; font-weight: 800;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
html.ui-explorer .dx-card-id small {
  display: block; font-size: 11.5px; color: var(--dx-ink-soft); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
html.ui-explorer .dx-card-acts { flex: 0 0 auto; display: flex; gap: 6px; }
html.ui-explorer .dx-card-body { padding: 0 11px 11px; }
html.ui-explorer .dx-state {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px; margin-bottom: 7px;
  color: #fff; background: var(--dx-ink-soft);
}
html.ui-explorer .dx-state.is-hiding { background: var(--dx-hiding); }
html.ui-explorer .dx-state.is-found { background: var(--dx-found); }
html.ui-explorer .dx-state.is-kept { background: var(--dx-kept); }
html.ui-explorer .dx-dsc { font-size: 12.5px; line-height: 1.45; color: var(--dx-ink-soft); margin: 0 0 9px; }
html.ui-explorer .dx-ttl { font-size: 13px; font-weight: 800; margin: 0 0 4px; }
/* THE OWNER-ACCESS TOUCH. The wording is the product's existing wording - this pass does not
   invent a new phrase for it - but the line that tells a viewer the spot is theirs to see is
   the one place purple means "you have access here". */
html.ui-explorer .dx-ttl.dx-own { color: var(--dx-nav-text); }
html.ui-explorer .dx-reveal {
  width: 100%; min-height: 44px;
  border: 0; border-radius: 12px;
  background: linear-gradient(135deg, var(--dx-crai), var(--dx-crai-deep));
  /* dark ink on gold: white on this gold measures about 2:1 and is the kind of button that
     looks fine to whoever chose it and cannot be read in daylight */
  color: var(--dx-crai-ink); font: inherit; font-size: 14px; font-weight: 800; cursor: pointer;
}
html.ui-explorer .dx-costnote { font-size: 10.5px; color: var(--dx-crai); margin: 5px 0 0; text-align: center; }
html.ui-explorer .dx-journey {
  width: 100%; min-height: 42px; margin-top: 8px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--dx-line); border-radius: 12px;
  background: var(--dx-surface); color: var(--dx-nav-text);
  font: inherit; font-size: 13.5px; font-weight: 800; text-decoration: none; cursor: pointer;
}
html.ui-explorer .dx-choose {
  display: flex; flex-direction: column; gap: 3px; max-height: 210px; overflow-y: auto;
}

/* a quiet truthful line under the navigator, for the moments the deck is not yet this
   viewer's own answer. Same role as the legacy map-filter note. */
html.ui-explorer .dx-live {
  padding: 5px 12px 0; font-size: 11.5px; color: #E9D5FF; line-height: 1.4;
}
html.ui-explorer .dx-live[hidden] { display: none; }

/* ---- the sharing control, compact, where the permanent panel used to be ---- */
html.ui-explorer .dx-sharebar { padding: 14px 12px 0; }
html.ui-explorer .dx-sharebar .share-btn { min-height: 40px; }
/* THE SHARE SURFACE IS THE COMPONENT'S OWN. assets/share-box.js now carries an opaque
   teal-dark panel and readable ink, so it reads the same wherever it is mounted - which is
   the repair, rather than this page overriding it for itself and leaving every other page
   with the white-on-pale it had. Nothing is restated here. */

/* ==============================================================================================
   THE INDIVIDUAL DUCK DECK JOURNEY, IN THE SAME DESIGN
   /duck/<code>/deck-journey keeps its route, its share metadata, its one-duck isolation, its
   members-only gate, its Deck Map Card and its existing reveal transaction. What changes here
   is how it looks: the same warm off-white page, the same nearly full-width deck at its own
   proportions, the same card language on the surface that already describes the duck.

   NOTHING BELOW SETS display ON .deckwrap, AND THAT IS DELIBERATE.
   .deckwrap carries js-mapgated, and applyMapGate() hides it by writing an INLINE display
   when the live map is members-only. A display rule here with !important would beat that
   inline style and put a gated deck on a signed-out visitor's screen. Padding and width are
   safe to restate; display is not ours to touch.
   ============================================================================================== */
html.ui-explorer #deckTop { background: var(--dx-bg); }
html.ui-explorer #zoneView { padding-bottom: 30px; }

/* THE DECK IS NO LONGER TRAPPED IN A SHORT BOX. The page's own rule caps it at
   height:min(52vh,420px), which fits a 1:6.9 deck plan into half a phone screen and shrinks
   its width to match. width-only sizing gives the plan the column and lets the page scroll
   through its length, which is how the sailing explorer shows the same artwork. */
html.ui-explorer #zoneView .deckwrap { padding: 0 !important; }
/* AND IT ESCAPES THE PAGE PADDING, so the deck is as wide here as it is on the sailing
   explorer. Measured: 96.5% of the wrapper came to 341 of 393 - 86.8% of the screen - because
   the wrapper sits inside a content column this page pads by 20px a side. The deck is the
   centrepiece of both surfaces and should not be two different widths depending on which one
   you arrived at. calc(50% - 50vw) is the full-bleed pull: whatever the ancestors pad, the
   wrapper spans the viewport and the 96.5% above resolves against that.
   MOBILE ONLY, BY DELIBERATE OMISSION. On a desktop this page is not the centred phone-width
   column the sailing explorer builds - adapting its whole desktop layout is not what this pass
   was asked for - and full-bleed there would stretch one deck across 1280px.
   NOTHING HERE TOUCHES display: .deckwrap carries js-mapgated and the gate hides it inline. */
@media (max-width: 899px) {
  html.ui-explorer #zoneView .deckwrap {
    margin-left: calc(50% - 50vw) !important;
    margin-right: calc(50% - 50vw) !important;
  }
}
html.ui-explorer #zoneView #deck {
  width: 96.5% !important;
  height: auto !important;
  max-width: none !important;
  margin: 0 auto !important;
  background: var(--dx-surface);
  border-radius: 10px;
  box-shadow: 0 1px 0 var(--dx-line), 0 12px 28px -20px rgba(23, 34, 51, .45);
}
/* one column: the deck, then the duck's own information, then the board */
html.ui-explorer #zoneView .jcompose { display: block !important; }
html.ui-explorer #zoneView .stage { display: block; margin: 0 0 14px; }
html.ui-explorer #zoneView .content { min-width: 0; width: 100%; }

/* the chrome lines above the deck, contained and quiet */
html.ui-explorer #zoneView .ctxbar,
html.ui-explorer #zoneView .live,
html.ui-explorer #zoneView .hintbar {
  max-width: 100%; overflow-wrap: anywhere; color: var(--dx-ink-soft);
}
html.ui-explorer #zoneView .dj-nav { display: flex; flex-wrap: wrap; gap: 8px; }
html.ui-explorer #zoneView .dj-nav > * { min-width: 0; }

/* THE DUCK'S OWN SURFACE, IN THE CARD'S LANGUAGE. This is the panel that already carries the
   state, the authorization explanation and the existing reveal control; it is restyled, not
   replaced, so the transaction it hosts is the same transaction. */
html.ui-explorer #zoneView .pinbox,
html.ui-explorer #zoneView .loadbox {
  background: var(--dx-surface); color: var(--dx-ink);
  border: 1px solid var(--dx-line); border-radius: 16px;
  box-shadow: 0 10px 30px -18px rgba(23, 34, 51, .35);
}
html.ui-explorer #zoneView .pinbox .ttl { color: var(--dx-ink); font-weight: 800; }
html.ui-explorer #zoneView .pinbox .dsc { color: var(--dx-ink-soft); }

/* THE BOARD BESIDE IT, IN THE LIGHT PALETTE. Its rows, counts, cards, menus and the Coming
   Aboard control are the same component doing the same thing - only the surface is restated,
   so a dark navy panel does not sit under a warm off-white page. */
html.ui-explorer .dob-pane { background: transparent; }
html.ui-explorer .dob-row {
  background: var(--dx-surface); color: var(--dx-ink);
  border: 1px solid var(--dx-line);
}
html.ui-explorer .dob-row.on { border-color: rgba(168, 85, 247, .46); }
html.ui-explorer .dob-label { color: var(--dx-ink); }
html.ui-explorer .dob-count { color: var(--dx-nav-text); }
html.ui-explorer .dob-open { background: transparent; }
html.ui-explorer .dob-cardwrap { background: transparent; }
html.ui-explorer .dob-card {
  background: var(--dx-surface); color: var(--dx-ink);
  border: 1px solid var(--dx-line);
}
html.ui-explorer .dob-cardmeta strong { color: var(--dx-ink); }
html.ui-explorer .dob-cardmeta small { color: var(--dx-ink-soft); }
html.ui-explorer .dob-note,
html.ui-explorer .dob-empty,
html.ui-explorer .dob-off { color: var(--dx-ink-soft); }
html.ui-explorer .dob-grouphead { color: var(--dx-ink); }
html.ui-explorer .dob-groupn { color: var(--dx-nav-text); }
html.ui-explorer .dob-menu { color: var(--dx-ink-faint); }
html.ui-explorer .dob-sheet {
  background: var(--dx-surface); color: var(--dx-ink);
  border: 1px solid var(--dx-line); box-shadow: var(--dx-shadow);
}
html.ui-explorer .dob-sheet button { color: var(--dx-ink); }
html.ui-explorer .dob-x { color: var(--dx-ink-faint); }

/* ==============================================================================================
   ZONE DISCOVERY - the stack, the scattered photographs, the AI tag and the gold trace.

   THE LAYER DOES NOT TAKE POINTER EVENTS. Only the photographs and their controls do, so a tap
   in the gaps between them falls through to the deck's own canvas and reaches the pulsing zone
   underneath - which is how tapping the exposed zone clears the presentation. A layer that
   swallowed every tap would make the zone unreachable, and the zone is a control.

   AND NOTHING HERE TOUCHES THE DECK. The zone goes on pulsing through every state below
   because the canvas it pulses on is a different element entirely.
   ============================================================================================== */
html.ui-explorer .dx-disc {
  position: fixed; z-index: 43;
  left: calc(var(--dx-l) + 6px);
  width: calc(var(--dx-r) - var(--dx-l) - 12px);
  top: var(--dx-disctop, 120px);
  /* the deck stage's own lower edge - above the page's share panel once that is on screen */
  bottom: var(--dx-discbottom, 12px);
  pointer-events: none;
}
html.ui-explorer .dx-disc[hidden] { display: none; }

/* ---- the compact stack, with its count ---- */
html.ui-explorer .dx-stack {
  position: absolute; left: 50%; top: 14%;
  transform: translateX(-50%);
  pointer-events: auto;
  display: block; width: 104px; height: 104px;
  border: 0; background: none; padding: 0; cursor: pointer;
}
html.ui-explorer .dx-stackimg {
  position: absolute; left: 50%; top: 0;
  width: 84px; height: 84px; margin-left: -42px;
  border-radius: 12px; object-fit: cover;
  border: 2.5px solid #fff;
  box-shadow: 0 8px 20px -8px rgba(0, 0, 0, .8);
  background: var(--dx-surface);
}
html.ui-explorer .dx-stackn {
  position: absolute; right: -2px; bottom: 2px;
  min-width: 26px; height: 26px; padding: 0 7px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; background: var(--dx-nav-solid); color: #fff;
  font-size: 12.5px; font-weight: 800;
  box-shadow: 0 3px 10px rgba(0, 0, 0, .5);
}

/* ---- the scattered photographs ----
   Large on screen and small on the wire: the src is the same URL the deck's own markers
   already loaded, so this is a cache hit scaled up, not a new download. */
html.ui-explorer .dx-photo {
  position: absolute; pointer-events: auto;
  padding: 0; border: 0; background: none; cursor: pointer;
  transform: rotate(var(--tilt, 0deg));
  transition: opacity .22s ease, transform .22s ease;
  will-change: transform, opacity;
}
html.ui-explorer .dx-photo img {
  width: 100%; height: 100%; display: block;
  object-fit: cover; border-radius: 12px;
  border: 3px solid #fff;
  box-shadow: 0 10px 26px -8px rgba(0, 0, 0, .85);
  background: var(--dx-surface);
}
html.ui-explorer .dx-photo.dx-dealt { animation: dxDeal .34s cubic-bezier(.2, .8, .3, 1) both; }
@keyframes dxDeal {
  from { opacity: 0; transform: rotate(var(--tilt, 0deg)) scale(.72) translateY(14px); }
  to   { opacity: 1; transform: rotate(var(--tilt, 0deg)) scale(1) translateY(0); }
}
/* THE ONES NOT CHOSEN FADE AND DRIFT OFF.
   animation:none is doing real work here and is not defensive noise. The deal animation runs
   with fill-mode 'both', so its final keyframe KEEPS applying opacity:1 after it finishes -
   and an animation-filled value outranks a normal declaration in the cascade, so this rule
   set opacity:0 and nothing happened. Measured: five photographs of five still fully visible
   behind the card. Cancelling the animation lets the rule below apply. */
html.ui-explorer .dx-photo.dx-gone,
html.ui-explorer .dx-dealnav.dx-gone {
  animation: none !important;
  opacity: 0; transform: rotate(var(--tilt, 0deg)) scale(.9) translateY(10px); pointer-events: none;
}
html.ui-explorer .dx-photo.dx-chosen { z-index: 60; }

/* ---- browsing the groups ---- */
html.ui-explorer .dx-dealnav {
  position: absolute; left: 50%; bottom: 2px; transform: translateX(-50%);
  pointer-events: auto;
  display: flex; align-items: center; gap: 8px;
  padding: 5px 8px; border-radius: 999px;
  background: var(--dx-glass); border: 1px solid var(--dx-line);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  box-shadow: var(--dx-shadow);
  transition: opacity .22s ease;
}
html.ui-explorer .dx-dealbtn {
  width: 36px; height: 36px; display: grid; place-items: center;
  border: 1px solid var(--dx-line); border-radius: 10px;
  background: var(--dx-surface); color: var(--dx-ink);
  font: inherit; font-size: 17px; font-weight: 700; cursor: pointer;
}
html.ui-explorer .dx-deallab {
  font-size: 12px; font-weight: 700; color: var(--dx-ink-soft);
  font-variant-numeric: tabular-nums; min-width: 54px; text-align: center;
}

/* ---- the card's complete location and hint ---- */
html.ui-explorer .dx-place {
  display: flex; flex-direction: column; gap: 3px;
  margin: 0 0 9px; padding: 8px 10px;
  border-radius: 10px; background: rgba(244, 239, 250, .06);
  border: 1px solid var(--dx-line);
}
html.ui-explorer .dx-placerow { display: flex; gap: 9px; align-items: baseline; }
html.ui-explorer .dx-placek {
  flex: 0 0 52px; font-size: 10px; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--dx-ink-faint);
}
/* NO TRUNCATION, NO ELLIPSIS: a deck name or a band wraps rather than being cut. */
html.ui-explorer .dx-placev {
  flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 700;
  color: var(--dx-ink); overflow-wrap: anywhere;
}
html.ui-explorer .dx-hint { margin: 0 0 9px; }
html.ui-explorer .dx-hintk {
  font-size: 10px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  color: var(--dx-ink-faint); margin-bottom: 3px;
}
html.ui-explorer .dx-hintv {
  margin: 0; font-size: 13px; line-height: 1.5; color: var(--dx-ink);
  overflow-wrap: anywhere; white-space: pre-wrap;
}

/* ---- the AI tag: a separate component standing on the card's top edge ---- */
html.ui-explorer .dx-tag {
  position: relative; z-index: 0;
  align-self: center;
  perspective: 900px;
  /* IT TAKES ITS OWN TOUCHES. pointer-events:none here is what sent the owner's taps through
     a photograph and into the Facebook and email controls behind it. A photograph that is on
     screen owns the area it covers. */
  pointer-events: auto;
  transform-origin: 50% 100%;
}
html.ui-explorer .dx-tag[hidden] { display: none; }
html.ui-explorer .dx-tag img {
  display: block; width: 100%; height: auto;
  border-radius: 12px 12px 0 0;
  box-shadow: 0 -10px 30px -14px rgba(0, 0, 0, .85);
  /* nearly flat, lying away from the viewer, before it is raised */
  transform: rotateX(84deg);
  transform-origin: 50% 100%;
  opacity: .25;
}
html.ui-explorer .dx-tag.dx-tag-up img {
  animation: dxTagUp .62s cubic-bezier(.22, 1.12, .36, 1) forwards;
}
@keyframes dxTagUp {
  0%   { transform: rotateX(84deg); opacity: .25; }
  72%  { transform: rotateX(-7deg); opacity: 1; }
  88%  { transform: rotateX(3.5deg); opacity: 1; }
  100% { transform: rotateX(0deg); opacity: 1; }
}

/* ---- the one gold finishing trace ---- */
html.ui-explorer .dx-trace { position: absolute; z-index: 9; pointer-events: none; overflow: visible; }
html.ui-explorer .dx-tracepath {
  fill: none; stroke: var(--dx-crai); stroke-width: 2.5;
  stroke-linecap: round; stroke-linejoin: round;
  filter: drop-shadow(0 0 5px rgba(244, 196, 48, .85));
}

/* ---- the browsing reference: where the guest was, not what is selected ---- */
html.ui-explorer .dx-duckrow.dx-lastviewed {
  border-color: var(--dx-line);
  box-shadow: inset 2px 0 0 var(--dx-nav);
}

/* ---- REDUCED MOTION: every state survives, the travel does not ---- */
@media (prefers-reduced-motion: reduce) {
  html.ui-explorer .dx-photo,
  html.ui-explorer .dx-card-frame,
  html.ui-explorer .dx-card-id,
  html.ui-explorer .dx-card-acts,
  html.ui-explorer .dx-card-body,
  html.ui-explorer .dx-dealnav { transition: none !important; animation: none !important; }
  html.ui-explorer .dx-photo.dx-dealt { animation: none !important; }
  html.ui-explorer .dx-tag img { transform: none !important; opacity: 1 !important; animation: none !important; }
  html.ui-explorer .dx-card-frame.dx-building { clip-path: inset(0 round 16px) !important; }
}

/* ==============================================================================================
   PHASE 2 - the tab's invitation, the swipe affordance, the share control and the in-card
   purchase.
   ============================================================================================== */

/* ---- THE COLLAPSED TAB INVITES A FIRST-TIME GUEST ----
   A visitor shown the Explorer cold did not know where to begin. The tab keeps its purple, its
   shape, its label and its count; what it gains is a breathing glow with one slightly stronger
   beat per cycle, so it reads as a control rather than a label. Soft throughout - nothing here
   flashes, and nothing here is red. */
/* ---- THE INVITATION IS A SPOTLIGHT NOW, NOT A GLOW ----
   The owner could not see the old one: it peaked at a narrow radial wash behind the tab and
   read as a slightly warmer patch of plum. This is the same mechanism made unmistakable - one
   rise to a bright peak, one smooth fall, and a dark trough between cycles so the peak reads
   as a flash rather than as a permanently lit control. Nothing about the geometry changed:
   the halo still lives on a layer BEHIND the tab with no pointer events, so the tap target is
   stationary and exactly where it was, which is the property the note above this one exists
   to protect. */
@keyframes dxTabInvite {
  0%   { opacity: .11; transform: scale(.94); filter: blur(2px) saturate(1); }
  45%  { opacity: .40; transform: scale(1.14); filter: blur(5px) saturate(1.2); }
  70%  { opacity: .20; transform: scale(1.03); filter: blur(3px) saturate(1.1); }
  100% { opacity: .11; transform: scale(.94); filter: blur(2px) saturate(1); }
}
/* AND THE TAB'S OWN EDGE CATCHES THE LIGHT AT THE SAME MOMENT. box-shadow paints outside the
   border box and changes no geometry, so the control lights up without moving, resizing or
   shifting anything beside it - and the label keeps its own colour and full opacity the whole
   way through, because nothing here touches the tab's text or its own opacity. */
@keyframes dxTabFlare {
  0%   { box-shadow: var(--dx-shadow), 0 0 0 0 rgba(168, 85, 247, 0); }
  45%  { box-shadow: var(--dx-shadow),
                     0 0 11px 4px rgba(214, 171, 255, .32),
                     0 0 26px 9px rgba(168, 85, 247, .18); }
  70%  { box-shadow: var(--dx-shadow), 0 0 7px 2px rgba(168, 85, 247, .12); }
  100% { box-shadow: var(--dx-shadow), 0 0 0 0 rgba(168, 85, 247, 0); }
}
}
/* THE BUTTON ITSELF DOES NOT MOVE, AND THAT IS NOT a stylistic preference.
   The first version scaled the tab, so the tap target was never still: an automated click
   timed out waiting for it to settle, and a target that is always drifting is harder to hit
   for anyone whose aim is not perfect. The glow and the swell now live on a layer BEHIND the
   tab, which carries no pointer events, so the invitation is just as visible and the thing
   being aimed at is stationary. */
html.ui-explorer .dx-tab { position: fixed; isolation: isolate; }
html.ui-explorer .dx-tab::before {
  /* content MUST be valid or the pseudo-element is never generated - this read "content: ;",
     which is why the invitation was absent rather than merely subtle. */
  /* THE HALO REACHES FURTHER AND BURNS WHITER AT ITS CENTRE. The inset grew so the glow can
     expand past the tab's own edge, and the gradient now starts near-white before falling to
     purple and then to nothing, which is what makes it read as a light source rather than as
     a tinted panel. */
  content: ""; position: absolute; inset: -14px -3px -14px -17px; z-index: -1;
  border-radius: 28px 0 0 28px;
  background: radial-gradient(72% 76% at 82% 50%,
              rgba(245, 235, 255, .45) 0%,
              rgba(214, 171, 255, .35) 22%,
              rgba(168, 85, 247, .20) 52%,
              rgba(168, 85, 247, 0) 80%);
  filter: blur(3px);
  animation: dxTabInvite 2.6s ease-in-out infinite;
  transform-origin: right center;
  pointer-events: none;
}
/* The tab itself flares with the halo, on the same clock, so the control lights up rather
   than merely sitting in front of something that does. */
html.ui-explorer .dx-tab { animation: dxTabFlare 2.6s ease-in-out infinite; }
/* it has done its job once the panel is open */
html.ui-explorer .dx-tab[aria-expanded="true"]::before { animation: none; opacity: 0; }
html.ui-explorer .dx-tab[aria-expanded="true"] { animation: none; box-shadow: var(--dx-shadow); }
/* a small chevron hint that the tab pulls open, under the label */
html.ui-explorer .dx-tab .dx-tab-grip {
  font-size: 12px; line-height: 1; color: var(--dx-nav-text); opacity: .95;
}

/* the panel can be swiped away; the handle says so without taking space from the controls */
html.ui-explorer .dx-panel-grip {
  position: absolute; top: 7px; right: 46px;
  width: 34px; height: 4px; border-radius: 999px;
  background: var(--dx-line); pointer-events: none;
}

/* ---- THE CARD'S SHARE CONTROL ---- */
html.ui-explorer .dx-share {
  flex: 0 0 auto; width: 32px; height: 32px;
  display: grid; place-items: center;
  border: 1px solid rgba(45, 212, 191, .45); border-radius: 9px;
  background: rgba(45, 212, 191, .12); color: var(--dx-share);
  font: inherit; font-size: 14px; line-height: 1; cursor: pointer;
}
html.ui-explorer .dx-share:hover { background: rgba(45, 212, 191, .22); }

/* ---- THE IN-CARD PURCHASE ---- */
html.ui-explorer .dx-buy {
  margin: 0 0 9px; padding: 10px 11px;
  border: 1px solid var(--dx-crai); border-radius: 12px;
  background: rgba(244, 196, 48, .08);
}
html.ui-explorer .dx-buy-q {
  font-size: 13px; font-weight: 800; color: var(--dx-ink); margin: 0 0 4px; line-height: 1.4;
}
html.ui-explorer .dx-buy-sub {
  font-size: 11.5px; color: var(--dx-ink-soft); margin: 0 0 9px; line-height: 1.45;
}
html.ui-explorer .dx-buy-row { display: flex; gap: 8px; }
html.ui-explorer .dx-buy-yes {
  flex: 1 1 auto; min-height: 42px;
  border: 0; border-radius: 11px;
  background: linear-gradient(135deg, var(--dx-crai), var(--dx-crai-deep));
  color: var(--dx-crai-ink); font: inherit; font-size: 13.5px; font-weight: 800; cursor: pointer;
}
html.ui-explorer .dx-buy-no {
  flex: 0 0 auto; min-height: 42px; padding: 0 14px;
  border: 1px solid var(--dx-line); border-radius: 11px;
  background: var(--dx-surface); color: var(--dx-ink-soft);
  font: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
}
html.ui-explorer .dx-buy-yes[disabled],
html.ui-explorer .dx-buy-no[disabled] { opacity: .55; cursor: default; }
html.ui-explorer .dx-buy.is-bad { border-color: var(--dx-kept); background: rgba(155, 34, 66, .14); }
html.ui-explorer .dx-buy.is-working { border-color: var(--dx-nav); background: rgba(168, 85, 247, .12); }

/* ---- REDUCED MOTION: the invitation becomes a strong STATIC emphasis, not nothing ---- */
@media (prefers-reduced-motion: reduce) {
  /* NOTHING REPEATS, AND NOTHING IS QUIET EITHER. Both animations stop; what is left is the
     halo held at its bright peak and the tab wearing the flare's own glow as a fixed
     box-shadow - the same light the animation reaches, simply not moving. Somebody who has
     asked for less motion still sees a spotlit control. */
  html.ui-explorer .dx-tab::before {
    animation: none !important; opacity: .42 !important; transform: scale(1.09) !important;
  }
  html.ui-explorer .dx-tab {
    animation: none !important;
    box-shadow: var(--dx-shadow),
                0 0 10px 3px rgba(214, 171, 255, .29),
                0 0 22px 7px rgba(168, 85, 247, .15) !important;
    border-color: var(--dx-nav) !important;
    filter: brightness(1.06);
  }
  html.ui-explorer .dx-panel { transition: none !important; }
}

/* ---- THE LIFECYCLE PHOTOGRAPH, STANDING ON THE CARD'S BOTTOM EDGE ----
   The AI tag's own mechanism, inverted: the hinge is the TOP edge instead of the bottom, so
   it unfurls downward out of the card rather than rising out of it. The photograph keeps its
   own shape - width is set, height follows, object-fit is never used - because cropping a
   hide or a find photograph would be editing the record rather than showing it. */
html.ui-explorer .dx-life {
  position: relative; z-index: 0;
  align-self: center;
  perspective: 1000px;
  pointer-events: auto;   /* see the note on .dx-tag - this is the reported touch-through */
}
html.ui-explorer .dx-life[hidden] { display: none; }
html.ui-explorer .dx-life img {
  /* the box IS the picture shape - placeCard() sets both from naturalWidth/naturalHeight, so
     there is nothing to contain and nothing to crop */
  display: block; width: 100%; height: auto;
  border-radius: 0 0 14px 14px;
  background: var(--dx-bg-deep);
  box-shadow: 0 14px 30px -14px rgba(0, 0, 0, .85);
  transform: rotateX(-86deg);
  transform-origin: 50% 0%;
  opacity: .25;
}
html.ui-explorer .dx-life.dx-life-down img { animation: dxLifeDown .58s cubic-bezier(.22, 1.1, .36, 1) forwards; }
@keyframes dxLifeDown {
  0%   { transform: rotateX(-86deg); opacity: .25; }
  70%  { transform: rotateX(6deg); opacity: 1; }
  88%  { transform: rotateX(-3deg); opacity: 1; }
  100% { transform: rotateX(0deg); opacity: 1; }
}
/* the state this photograph belongs to, said plainly on it */
html.ui-explorer .dx-life-label {
  position: absolute; left: 10px; top: 10px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .07em;
  color: #fff; background: var(--dx-ink-soft);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .6);
}
html.ui-explorer .dx-life[data-state="HIDING"] .dx-life-label { background: var(--dx-hiding); }
html.ui-explorer .dx-life[data-state="FOUND"] .dx-life-label { background: var(--dx-found); }
html.ui-explorer .dx-life[data-state="KEPT"] .dx-life-label { background: var(--dx-kept); }
@media (prefers-reduced-motion: reduce) {
  html.ui-explorer .dx-life img { transform: none !important; opacity: 1 !important; animation: none !important; }
}
/* ==============================================================================================
   THE ASSEMBLED COMPOSITION

   One fixed container holding the AI tag, the card and the lifecycle photograph in flow. It is
   placed once, clamped to the deck stage, and dragged as a unit; closing it hides one element
   and everything attached goes with it. The three panels used to position themselves, which is
   how a narrowed viewport could take the card away and leave the AI image behind.
   ============================================================================================== */
html.ui-explorer .dx-assembly {
  position: fixed; z-index: 48;
  left: calc(var(--dx-l) + 10px);
  width: calc(var(--dx-r) - var(--dx-l) - 54px);
  max-width: 480px;
  display: flex; flex-direction: column; align-items: stretch;
  /* it owns every touch on its surface - nothing behind it receives one */
  pointer-events: auto;
  touch-action: pan-y;
}
html.ui-explorer .dx-assembly[hidden] { display: none; }
html.ui-explorer .dx-assembly img { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }

/* ---- THE GRAB HANDLE ---- */
html.ui-explorer .dx-grab {
  position: relative; z-index: 2;
  height: 22px; display: grid; place-items: center;
  cursor: grab; touch-action: none;
}
html.ui-explorer .dx-grab:active { cursor: grabbing; }
html.ui-explorer .dx-grab-bar {
  width: 42px; height: 4px; border-radius: 999px;
  background: var(--dx-ink-faint); opacity: .8;
}

/* the three photographs say they can be opened */
html.ui-explorer .dx-cardphoto,
html.ui-explorer .dx-tag img,
html.ui-explorer .dx-life img { cursor: zoom-in; }

/* ---- THE ENLARGED PHOTOGRAPH ----
   The backdrop is the whole screen on purpose: an overlay that covered only the picture would
   leave every control around it live, which is the defect this is answering. */
html.ui-explorer .dx-viewer,
.dx-viewer {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(16, 10, 22, .93);
  display: grid; place-items: center;
  padding: 18px;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}
.dx-viewer-in { position: relative; max-width: 100%; max-height: 100%; }
.dx-viewer-in img {
  display: block;
  max-width: 94vw; max-height: 88vh;
  width: auto; height: auto;          /* the complete picture, at its own shape */
  object-fit: contain;
  border-radius: 12px;
  box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .9);
}
.dx-viewer-x {
  position: absolute; top: -14px; right: -10px;
  width: 40px; height: 40px; display: grid; place-items: center;
  border: 1px solid rgba(255, 255, 255, .3); border-radius: 50%;
  background: rgba(20, 14, 28, .92); color: #fff;
  font: inherit; font-size: 20px; line-height: 1; cursor: pointer;
}

/* ---- THE REVEAL CONFIRMATION SITS BELOW ITS BUTTON, AND THE BUTTON STANDS DOWN ---- */
html.ui-explorer .dx-reveal.is-muted {
  filter: grayscale(.75); opacity: .5; pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  html.ui-explorer .dx-assembly { transition: none !important; }
}

/* ==============================================================================================
   THE SHARED-DUCK ARRIVAL
   The photograph is the point: it takes most of the screen, keeps its own shape and is never
   cropped. The overlay owns every touch on it, so nothing behind can be pressed by accident.
   ============================================================================================== */
html.ui-explorer .dx-arrive,
.dx-arrive {
  position: fixed; inset: 0; z-index: 92;
  background: radial-gradient(120% 80% at 50% 30%, rgba(58, 42, 72, .96), rgba(31, 27, 51, .985));
  display: grid; place-items: center; padding: 20px;
  pointer-events: auto;
}
.dx-arrive-wrap { display: flex; flex-direction: column; align-items: center; gap: 14px; max-width: 100%; }
.dx-arrive-photo {
  display: block;
  max-width: min(86vw, 520px);
  max-height: 66vh;              /* the 60-70% the design asks for */
  width: auto; height: auto;     /* its own aspect, never cropped, never stretched */
  border-radius: 18px;
  border: 4px solid rgba(255, 255, 255, .92);
  box-shadow: 0 30px 70px -22px rgba(0, 0, 0, .9);
  cursor: pointer;
}
.dx-arrive-name {
  font-size: 21px; font-weight: 800; color: #F4EFFA; text-align: center;
  text-shadow: 0 2px 12px rgba(0, 0, 0, .6);
}
.dx-arrive-go {
  min-height: 48px; padding: 0 26px;
  border: 0; border-radius: 999px;
  background: linear-gradient(135deg, #C084FC, #7E22CE);
  color: #fff; font: inherit; font-size: 15.5px; font-weight: 800; cursor: pointer;
  box-shadow: 0 12px 30px -10px rgba(168, 85, 247, .8);
}
/* while the photograph is travelling into the card the backdrop gets out of the way */
.dx-arrive-going { background: transparent; pointer-events: none; transition: background .25s ease; }
@media (prefers-reduced-motion: reduce) {
  .dx-arrive-going { transition: none; }
}

/* ---- HIT AREA AND VISIBLE AREA MUST BE THE SAME THING ----
   A rotateX'd photograph is hit-tested against its transformed geometry, so while it is lying
   back the picture you can see is larger than the area that answers a tap. The panel beneath
   each one is NOT transformed, spans the same box, and takes the event instead - which is why
   both of these carry pointer-events:auto and a minimum presence even mid-animation. */
html.ui-explorer .dx-tag,
html.ui-explorer .dx-life { pointer-events: auto; }
html.ui-explorer .dx-tag img,
html.ui-explorer .dx-life img { pointer-events: auto; }
/* the trace is decoration and must never take a tap */
html.ui-explorer .dx-trace,
html.ui-explorer .dx-trace * { pointer-events: none !important; }
/* the frame layer sits under the content and must not shadow it */
html.ui-explorer .dx-card-frame { pointer-events: none; }

/* the composition never grows past the stage it is allowed to occupy; where it would, it
   scrolls inside its own frame rather than reaching over the page below the deck */
html.ui-explorer .dx-assembly { overflow-y: auto; overflow-x: visible; -webkit-overflow-scrolling: touch; }
