/* ======================================================================
 * DUKES ASSISTANT — shared across every page. 19 August 2026.
 *
 * Aarav: "make it a round thing and in the middle have the crown, not
 * square", "the text ... make it come but fade in with animation",
 * "make it stay", "add this to all the other pages because it needs to
 * remain consistent".
 *
 * Deliberately UNSCOPED so one component serves the whole site. Loaded
 * after dukes.css, so these rules win on order without needing !important
 * or a specificity fight. The page-scoped copies in home-2026.css carry
 * identical values, so the two agree wherever both apply.
 * ====================================================================== */

:root {
  --da-oxblood: #7b2430;
  --da-oxblood-lift: #94303c;
  --da-navy: #0d1d31;
  --da-cream: #fbf8f2;
  --da-paper: #f2ede4;
  --da-ink: #171a20;
  --da-ink-soft: #4f555b;
  --da-tap: 48px;
}

/* ---------------------------------------------------------------- *
 * 1 — THE LAUNCHER IS A CIRCLE WITH THE CROWN IN IT
 * It was a wide rectangular card carrying a label and a sub-label. It is
 * now a single round button, crown centred, the way every chat widget
 * people already recognise behaves. The words move to the greeting
 * bubble, which is where they belong.
 * ---------------------------------------------------------------- */
.launcher {
  position: fixed;
  right: clamp(14px, 2vw, 28px);
  bottom: clamp(14px, 2vw, 28px);
  z-index: 60;
  display: grid;
  place-items: center;
  /* 19 Aug 2026, Aarav: the receptionist reads too big on laptop
     screens — one notch down across launcher, bubble and panel. */
  width: 56px;
  height: 56px;
  padding: 0;
  border-radius: 50%;
  background: var(--da-oxblood);
  border: 1px solid rgba(251, 248, 242, 0.24);
  box-shadow: 0 20px 42px -16px rgba(13, 29, 49, 0.6);
  text-decoration: none;
  overflow: hidden;
  transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1), background-color 0.2s ease, box-shadow 0.22s ease;
}
/* The label and sub-label are not deleted — a screen reader still reads
   them — they are simply taken out of the visual button. */
.launcher__label {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.launcher__glyph {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  background: none;
}
.launcher__crown {
  width: 28px;
  height: auto;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.35));
}
/* The glint stays — Aarav asked for the light to remain — but it now
   sweeps a circle rather than a card. */
.launcher__glint {
  position: absolute;
  inset: -40%;
  background: linear-gradient(115deg, transparent 42%, rgba(255, 255, 255, 0.34) 50%, transparent 58%);
  transform: translateX(-60%);
  animation: da-glint 5.5s ease-in-out infinite;
  pointer-events: none;
}
@keyframes da-glint {
  0%, 62%   { transform: translateX(-60%); }
  78%, 100% { transform: translateX(60%); }
}
@media (hover: hover) and (pointer: fine) {
  .launcher:hover { background: var(--da-oxblood-lift); transform: translateY(-3px) scale(1.04); box-shadow: 0 26px 54px -18px rgba(13, 29, 49, 0.68); }
}
.launcher:active { transform: translateY(0) scale(0.98); }
.launcher:focus-visible { outline: 3px solid #d8b56d; outline-offset: 4px; }

/* ---------------------------------------------------------------- *
 * 2 — THE GREETING BUBBLE. Fades in, and stays.
 * ---------------------------------------------------------------- */
.dbubble {
  position: fixed;
  right: clamp(14px, 2vw, 28px);
  bottom: calc(clamp(14px, 2vw, 28px) + 70px);
  z-index: 61;
  width: min(19rem, calc(100vw - 2.5rem));
  padding: 0.9rem 1rem 0.85rem;
  border-radius: 20px 20px 6px 20px;
  background: var(--da-cream);
  border: 1px solid rgba(123, 36, 48, 0.2);
  box-shadow: 0 30px 60px -26px rgba(13, 29, 49, 0.55);
  /* 19 Aug fix — the visible state must never depend on an animation
     completing. Base state is VISIBLE; the keyframe only fades it in from
     transparent. If the animation never runs, the greeting still shows. */
  opacity: 1;
  transform: none;
  animation: da-fade-in 520ms cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes da-fade-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
.dbubble.is-leaving {
  animation: da-fade-out 220ms ease forwards;
}
@keyframes da-fade-out {
  to { opacity: 0; transform: translateY(6px); }
}
.dbubble p {
  margin: 0 1.2rem 0.7rem 0;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--da-ink);
}
.dbubble__x {
  position: absolute;
  top: -9px; right: -9px;
  width: 26px; height: 26px;
  border: 0;
  border-radius: 50%;
  background: var(--da-oxblood);
  color: var(--da-cream);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 8px 18px -8px rgba(13, 29, 49, 0.6);
}
.dbubble__x:hover { background: var(--da-oxblood-lift); }
.dbubble__go {
  min-height: 40px;
  padding: 0.45rem 0.9rem;
  border: 0;
  border-radius: 8px;
  background: var(--da-oxblood);
  color: var(--da-cream);
  font: inherit;
  font-size: 0.84rem;
  font-weight: 750;
  cursor: pointer;
}
.dbubble__go:hover { background: var(--da-oxblood-lift); }

/* ---------------------------------------------------------------- *
 * 3 — THE PANEL, one colour family end to end
 * ---------------------------------------------------------------- */
.dchat {
  position: fixed;
  right: clamp(14px, 2vw, 28px);
  bottom: clamp(14px, 2vw, 28px);
  z-index: 80;
  display: flex;
  flex-direction: column;
  width: min(25.5rem, calc(100vw - 2rem));
  height: min(36rem, calc(100vh - 4rem));
  border-radius: 22px;
  overflow: hidden;
  background: var(--da-cream);
  border: 1px solid rgba(13, 29, 49, 0.16);
  box-shadow: 0 40px 90px -40px rgba(13, 29, 49, 0.7);
  transform-origin: 100% 100%;
  animation: da-pop 300ms cubic-bezier(0.22, 1.28, 0.5, 1) both;
}
.dchat[hidden] { display: none; }
@keyframes da-pop {
  from { opacity: 0; transform: translateY(18px) scale(0.94); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.dchat__head { display: flex; align-items: center; gap: 0.65rem; padding: 0.8rem 0.9rem; background: var(--da-oxblood); color: var(--da-cream); }
.dchat__mark { display: grid; place-items: center; width: 36px; height: 36px; flex: none; border-radius: 50%; background: rgba(251, 248, 242, 0.16); }
.dchat__mark img { width: 21px; height: auto; }
.dchat__title { display: grid; gap: 0.1rem; min-width: 0; }
.dchat__title strong { font-size: 0.98rem; font-weight: 750; }
.dchat__title span { font-size: 0.76rem; color: rgba(251, 248, 242, 0.78); line-height: 1.35; }
.dchat__close { margin-left: auto; flex: none; width: 44px; height: 44px; border: 0; border-radius: 50%; background: transparent; color: var(--da-cream); font-size: 1.6rem; line-height: 1; cursor: pointer; }
.dchat__close:hover { background: rgba(251, 248, 242, 0.16); }

.dchat__log { flex: 1 1 auto; overflow-y: auto; padding: 0.9rem; display: flex; flex-direction: column; gap: 0.55rem; background: var(--da-paper); }
.dchat__msg { display: flex; }
.dchat__msg p { margin: 0; max-width: 88%; padding: 0.6rem 0.85rem; font-size: 0.9rem; line-height: 1.5; }
.dchat__msg--bot p, .dchat__msg--bot .dchat__typing { border-radius: 14px 14px 14px 4px; background: #fffdf8; border: 1px solid rgba(23, 26, 32, 0.12); color: var(--da-ink); }
.dchat__msg--me { justify-content: flex-end; }
.dchat__msg--me p { border-radius: 14px 14px 4px 14px; background: var(--da-oxblood); border: 1px solid var(--da-oxblood); color: var(--da-cream); }

.dchat__typing { display: inline-flex; align-items: center; gap: 0.28rem; padding: 0.85rem 1rem; }
.dchat__typing span { width: 7px; height: 7px; border-radius: 50%; background: var(--da-ink-soft); animation: da-blink 1.25s infinite ease-in-out both; }
.dchat__typing span:nth-child(2) { animation-delay: 0.18s; }
.dchat__typing span:nth-child(3) { animation-delay: 0.36s; }
@keyframes da-blink { 0%, 80%, 100% { opacity: 0.28; } 40% { opacity: 1; } }

.dchat__summary { margin-top: 0.2rem; padding: 0.85rem 0.95rem; border-radius: 14px; background: #fffdf8; border: 1px solid rgba(23, 26, 32, 0.14); }
.dchat__summary-kind { margin: 0 0 0.5rem; color: var(--da-oxblood); font-size: 0.7rem; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; }
.dchat__summary dl { margin: 0; }
.dchat__summary dl > div { display: grid; grid-template-columns: 8.2rem minmax(0, 1fr); gap: 0.6rem; padding: 0.34rem 0; border-top: 1px solid rgba(23, 26, 32, 0.09); }
.dchat__summary dt { margin: 0; font-size: 0.78rem; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; color: var(--da-ink-soft); }
.dchat__summary dd { margin: 0; font-size: 0.94rem; color: var(--da-ink); }

.dchat__reply { flex: none; padding: 0.75rem; background: var(--da-cream); border-top: 1px solid rgba(23, 26, 32, 0.1); }
.dchat__chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.dchat__chip { min-height: 40px; padding: 0.45rem 0.85rem; border: 1px solid rgba(23, 26, 32, 0.22); border-radius: 999px; background: #fff; color: var(--da-ink); font: inherit; font-size: 0.86rem; font-weight: 650; cursor: pointer; transition: background-color 0.16s ease, border-color 0.16s ease, transform 0.14s ease; }
.dchat__chip:hover { background: #f6e3e6; border-color: rgba(123, 36, 48, 0.5); transform: translateY(-1px); }
.dchat__form { display: flex; gap: 0.4rem; }
.dchat__input { flex: 1 1 auto; min-width: 0; min-height: 44px; padding: 0.5rem 0.75rem; border: 1px solid rgba(23, 26, 32, 0.22); border-radius: 10px; font: inherit; font-size: 0.95rem; background: #fff; color: var(--da-ink); }
.dchat__input:focus-visible { outline: 3px solid #d8b56d; outline-offset: 1px; }
.dchat__send { flex: none; min-height: 44px; padding: 0 1rem; border: 0; border-radius: 10px; background: var(--da-oxblood); color: var(--da-cream); font: inherit; font-size: 0.88rem; font-weight: 750; cursor: pointer; }
.dchat__send:hover { background: var(--da-oxblood-lift); }

.dchat__routes { display: grid; gap: 0.4rem; }
.dchat__route { display: flex; align-items: center; justify-content: center; min-height: 44px; padding: 0.45rem 0.85rem; border: 1px solid rgba(23, 26, 32, 0.2); border-radius: 10px; background: #fff; color: var(--da-ink); font-size: 0.88rem; font-weight: 750; text-decoration: none; }
.dchat__route--primary { background: var(--da-oxblood); border-color: var(--da-oxblood); color: var(--da-cream); }
.dchat__route--primary:hover { background: var(--da-oxblood-lift); }
.dchat__restart { min-height: 40px; border: 0; background: none; color: var(--da-ink-soft); font: inherit; font-size: 0.8rem; text-decoration: underline; text-underline-offset: 0.28em; cursor: pointer; }

.dchat__foot { flex: none; margin: 0; padding: 0.6rem 1rem; background: var(--da-cream); border-top: 1px solid rgba(23, 26, 32, 0.09); color: var(--da-ink-soft); font-size: 0.76rem; }
.dchat__foot a { color: var(--da-oxblood); }

body.has-dchat-open .launcher { display: none; }

@media (max-width: 32em) {
  .dchat { right: 0; left: 0; bottom: 0; width: 100%; height: 90vh; border-radius: 20px 20px 0 0; transform-origin: 50% 100%; }
  .dbubble { bottom: calc(clamp(14px, 2vw, 28px) + 66px); }
}
@media (prefers-reduced-motion: reduce) {
  .launcher, .dbubble, .dchat { animation: none !important; transition: none !important; }
  .launcher__glint { animation: none; opacity: 0; }
  .dchat__typing span { animation: none; opacity: 0.5; }
}

/* ---- 19 Aug, round five ------------------------------------------------
   "there is a square around the crown, get rid of that."
   dukes.css paints a plate behind .launcher__glyph and the crown PNG sits
   on its own matte. Both are cleared here, and the artwork is masked to a
   circle so no corner of the source image can show against the button. */
.launcher__glyph,
.launcher .launcher__glyph {
  background: none !important;
  background-color: transparent !important;
  border: 0 !important;
  border-radius: 50% !important;
  box-shadow: none !important;
}
.launcher__crown,
.launcher .launcher__crown {
  background: none !important;
  background-color: transparent !important;
  border: 0 !important;
  box-shadow: none !important;
  mix-blend-mode: normal;
  /* the source PNG carries a solid matte; clipping to a circle inside the
     button removes the visible square without touching the artwork file */
  clip-path: circle(48% at 50% 50%);
}
/* Belt and braces: nothing in this component may be left invisible by a
   motion preference. Reduced motion removes the movement, never the text. */
@media (prefers-reduced-motion: reduce) {
  .dbubble { opacity: 1 !important; transform: none !important; }
  .dchat   { opacity: 1 !important; transform: none !important; }
}
/* ======================================================================
 * THE REASON IT WAS NOT SHOWING — and the override. 19 August.
 *
 * dukes.css carries explicit kill rules for the launcher on at least a
 * dozen routes, e.g.
 *     .page--sellers-guide .launcher { display: none !important; }
 *     .page--tenant-fees   .launcher { display: none !important; }
 *     .page--about         .launcher { display: none !important; }
 *     [data-page="managed-portfolio"] .launcher { display: none !important; }
 * Those are 0,2,0 WITH !important. The base rule in this file is 0,1,0
 * with none, so it lost on every one of those pages: the module built the
 * launcher and the greeting appeared, while CSS deleted the button.
 *
 * `html body .launcher[data-launcher]` is 0,2,2 with !important, which
 * outranks every one of them. Scoped to [data-launcher] so it can only
 * ever affect the real assistant button.
 * ==================================================================== */
html body .launcher[data-launcher] {
  display: grid !important;
  visibility: visible !important;
  opacity: 1 !important;
  place-items: center !important;
  width: 68px !important;
  height: 68px !important;
  border-radius: 50% !important;
  background: var(--da-oxblood) !important;
}
/* the one case that must still win: the panel is open */
html body.has-dchat-open .launcher[data-launcher] { display: none !important; }

/* those same page rules also hid the crown itself */
html body .launcher[data-launcher] .launcher__glyph {
  display: grid !important;
  width: 100% !important;
  height: 100% !important;
  background: none !important;
}
html body .launcher[data-launcher] .launcher__crown { display: block !important; width: 34px !important; }
/* ---- CROWN SIZING, corrected 19 Aug -------------------------------
   Verified the artwork: dukes-crown.png is RGBA, 252x182, corners fully
   transparent, 45.6% of pixels transparent. So the square never came from
   the image — it was the .launcher__glyph plate in dukes.css, which is
   already overridden. The circular clip I added was therefore doing no
   useful work AND cropping the crown's wide edges, which is why it looked
   small. Clip removed, artwork enlarged, button opened up to hold it. */
html body .launcher[data-launcher] {
  width: 74px !important;
  height: 74px !important;
}
html body .launcher[data-launcher] .launcher__crown,
.launcher__crown {
  clip-path: none !important;
  width: 46px !important;
  max-width: none !important;
  height: auto !important;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.4));
}
/* the greeting bubble sits clear of the taller button */
.dbubble { bottom: calc(clamp(14px, 2vw, 28px) + 90px); }
/* ======================================================================
 * ROOT CAUSE of "too small" and "off to the right". 19 August.
 *
 * dukes.css holds the launcher in a pre-entrance state:
 *     [data-enhanced] .launcher {
 *       pointer-events: none;
 *       transform: translate3d(0, 10px, 0) scale(0.72);
 *     }
 * and only releases it when its own script sets [data-launcher-in="on"].
 * A launcher this module injects never receives that attribute, so it was
 * stuck at 72% scale, pushed 10px down, and NOT CLICKABLE. That is why the
 * crown looked small and sat low and right of where it belonged — the
 * whole button was scaled and translated, not just the artwork.
 *
 * Everything that positions or sizes the button is pinned here, so no
 * pre-entrance state, padding or grid template from dukes.css can move it.
 * ==================================================================== */
html body .launcher[data-launcher] {
  transform: none !important;
  pointer-events: auto !important;
  visibility: visible !important;
  opacity: 1 !important;
  padding: 0 !important;
  margin: 0 !important;
  gap: 0 !important;
  grid-template-columns: 1fr !important;
  grid-template-rows: 1fr !important;
  place-content: center !important;
  place-items: center !important;
  text-align: center !important;
  line-height: 0 !important;
  will-change: auto !important;
}
/* the crown occupies the single cell and centres in it, whatever dukes.css
   thinks the button's internal layout should be */
html body .launcher[data-launcher] .launcher__glyph {
  grid-area: 1 / 1 !important;
  place-self: center !important;
  justify-content: center !important;
  align-items: center !important;
  margin: 0 !important;
  padding: 0 !important;
  transform: none !important;
  line-height: 0 !important;
}
html body .launcher[data-launcher] .launcher__crown {
  display: block !important;
  margin: 0 auto !important;
  transform: none !important;
}
/* hover keeps its lift without reintroducing the pre-entrance transform */
@media (hover: hover) and (pointer: fine) {
  html body .launcher[data-launcher]:hover { transform: translateY(-3px) scale(1.04) !important; }
}
html body .launcher[data-launcher]:active { transform: scale(0.97) !important; }
/* ======================================================================
 * THE CROWN DISAPPEARING — diagnosed in the browser, 19 August.
 * Computed styles on the live page:
 *     .launcher        opacity 1  visible   74x74   OK
 *     .launcher__glyph opacity 0  <-- THE BUG
 *     .launcher__crown opacity 1  46x33  image loaded (natural 252px)
 * dukes.css L10935 [data-enhanced] .launcher .launcher__glyph starts at
 * opacity 0 and is only revealed when its own script sets
 * [data-launcher-in="on"], which an injected launcher never receives.
 * The previous pass neutralised the pre-entrance state on the BUTTON but
 * not on the glyph inside it, so the button showed and the crown did not.
 *
 * Every descendant of the assistant launcher is now pinned visible, so no
 * pre-entrance state anywhere in that subtree can hide the mark again.
 * ==================================================================== */
html body .launcher[data-launcher],
html body .launcher[data-launcher] * {
  opacity: 1 !important;
  visibility: visible !important;
}
html body .launcher[data-launcher] .launcher__glyph,
html body .launcher[data-launcher] .launcher__crown {
  opacity: 1 !important;
  visibility: visible !important;
  transform: none !important;
  animation: none !important;
  filter: none !important;
  clip-path: none !important;
  display: block !important;
}
html body .launcher[data-launcher] .launcher__glyph {
  display: grid !important;
  place-items: center !important;
}
html body .launcher[data-launcher] .launcher__crown {
  width: 46px !important;
  height: auto !important;
  margin: 0 auto !important;
}
/* the glint is decorative and must stay behind the crown, not over it */
html body .launcher[data-launcher] .launcher__glint {
  opacity: 0.5 !important;
  z-index: 0 !important;
}
html body .launcher[data-launcher] .launcher__glyph { position: relative; z-index: 1 !important; }
/* ======================================================================
 * THE GLINT — bigger, and genuinely continuous. 19 August.
 * Aarav: "the animation needs to be a bit bigger, like a glow line that
 * moves, and it needs to remain constant, it disappears for a split second".
 *
 * The old one was a diagonal band translating across the button: it swept,
 * then SNAPPED back to its start position off-screen, which is the split
 * second where nothing was there. A translation can never loop seamlessly
 * because its start and end positions are different.
 *
 * A rotating conic gradient has no start or end — 360deg is identical to
 * 0deg — so the glow travels round the rim forever with no reset frame.
 * Masked to a ring so it reads as a moving line of light on the edge
 * rather than a wash over the crown.
 * ==================================================================== */
html body .launcher[data-launcher] { overflow: hidden !important; }

html body .launcher[data-launcher] .launcher__glint {
  position: absolute !important;
  inset: -25% !important;
  width: auto !important;
  height: auto !important;
  border-radius: 50% !important;
  opacity: 1 !important;
  z-index: 0 !important;
  pointer-events: none !important;
  transform: none !important;
  /* the moving light: one bright arc, plus a fainter trailing arc so the
     rim never reads as empty on the opposite side */
  background: conic-gradient(
    from 0deg,
    rgba(255, 255, 255, 0)    0deg,
    rgba(255, 236, 190, 0.10) 26deg,
    rgba(255, 244, 214, 0.62) 48deg,
    rgba(255, 255, 255, 0.85) 60deg,
    rgba(255, 244, 214, 0.55) 72deg,
    rgba(255, 236, 190, 0.08) 96deg,
    rgba(255, 255, 255, 0)    150deg,
    rgba(255, 236, 190, 0.16) 210deg,
    rgba(255, 255, 255, 0)    260deg,
    rgba(255, 255, 255, 0)    360deg
  ) !important;
  /* keep it on the rim: a ring, not a filled disc */
  -webkit-mask-image: radial-gradient(circle at 50% 50%, transparent 56%, #000 68%, #000 100%) !important;
          mask-image: radial-gradient(circle at 50% 50%, transparent 56%, #000 68%, #000 100%) !important;
  animation: da-rim-sweep 4.2s linear infinite !important;
}
@keyframes da-rim-sweep {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* a soft steady halo underneath, so the button always has some light on it
   even at the point in the cycle where the bright arc is furthest away */
html body .launcher[data-launcher]::before {
  content: "" !important;
  position: absolute !important;
  inset: 0 !important;
  border-radius: 50% !important;
  z-index: 0 !important;
  pointer-events: none !important;
  background: radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.20) 0%, rgba(255, 255, 255, 0) 58%) !important;
  animation: none !important;
  opacity: 1 !important;
}

/* the crown stays above all of it */
html body .launcher[data-launcher] .launcher__glyph { position: relative !important; z-index: 2 !important; }

@media (prefers-reduced-motion: reduce) {
  html body .launcher[data-launcher] .launcher__glint { animation: none !important; opacity: 0.45 !important; }
}
/* The glint was measuring 0x0 with no animation running, because dukes.css
   L9376 carries `.launcher__glint { display: none !important; }` (and L6801
   a non-important twin). Opacity and inset were being overridden; display
   was not, so the element was never laid out at all. */
html body .launcher[data-launcher] .launcher__glint {
  display: block !important;
  inset: -25% !important;
  width: auto !important;
  height: auto !important;
}
/* The rim glow was laid out and its animation was RUNNING, but the computed
   transform stayed `none`: an earlier `transform: none !important` on the
   launcher subtree (added to kill dukes.css's pre-entrance scale) also beat
   the animation, because !important declarations outrank animations.
   Animating the independent `rotate` property instead sidesteps that
   entirely, and still leaves the pre-entrance transform neutralised. */
@keyframes da-rim-spin {
  from { rotate: 0deg; }
  to   { rotate: 360deg; }
}
html body .launcher[data-launcher] .launcher__glint {
  transform: none !important;          /* pre-entrance scale stays dead */
  rotate: 0deg;                         /* animated below, not !important */
  animation: da-rim-spin 4.2s linear infinite !important;
  will-change: rotate;
}
@media (prefers-reduced-motion: reduce) {
  html body .launcher[data-launcher] .launcher__glint { animation: none !important; rotate: 20deg; opacity: 0.5 !important; }
}
/* ======================================================================
 * FINAL: the glow moves to a PSEUDO-ELEMENT. 19 August.
 * The .launcher__glint span had accumulated competing !important rules
 * (display:none from dukes.css, transform:none from my own pre-entrance
 * fix, plus a blanket `* { opacity }` rule). !important beats animations,
 * so the keyframes ran while the value stayed pinned. Rather than pile on
 * another override, the glow moves to .launcher::after — a pseudo-element
 * nothing else in the codebase targets, so it animates cleanly.
 * ==================================================================== */
html body .launcher[data-launcher] .launcher__glint { display: none !important; }

html body .launcher[data-launcher]::after {
  content: "";
  position: absolute;
  inset: -26%;
  border-radius: 50%;
  z-index: 1;
  pointer-events: none;
  background: conic-gradient(
    from 0deg,
    rgba(255,255,255,0)     0deg,
    rgba(255,238,196,0.12)  24deg,
    rgba(255,246,220,0.68)  46deg,
    rgba(255,255,255,0.92)  58deg,
    rgba(255,246,220,0.60)  70deg,
    rgba(255,238,196,0.10)  94deg,
    rgba(255,255,255,0)    146deg,
    rgba(255,238,196,0.18) 208deg,
    rgba(255,255,255,0)    258deg,
    rgba(255,255,255,0)    360deg
  );
  -webkit-mask-image: radial-gradient(circle at 50% 50%, transparent 54%, #000 67%, #000 100%);
          mask-image: radial-gradient(circle at 50% 50%, transparent 54%, #000 67%, #000 100%);
  animation: da-rim 4.2s linear infinite;
}
@keyframes da-rim { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

/* the crown must sit above the glow */
html body .launcher[data-launcher] .launcher__glyph { position: relative !important; z-index: 3 !important; }

@media (prefers-reduced-motion: reduce) {
  html body .launcher[data-launcher]::after { animation: none; transform: rotate(28deg); }
}
/* ======================================================================
 * THE ORIGINAL GOLDEN GLOW, RESTORED AND MADE CONTINUOUS. 19 August.
 *
 * Aarav: "we had that golden glow like before, find the original glow we
 * had at the very beginning and add that" — plus the shimmer reference he
 * sent: golden, premium, with a bit of red.
 *
 * The original lives in dukes.css: a registered @property --launcher-angle
 * driving a conic-gradient of saturated brass, masked to the BORDER with
 * mask-composite, a crisp line on ::before and a blurred halo on ::after.
 * It is real and it is good. Two things stopped it:
 *   1. it is gated behind [data-launcher-in="on"] — now set in the JS
 *   2. its keyframes go quiet: opacity drops to 0 from 57% to 100% of a
 *      14s cycle, which is exactly the "disappears for a split second"
 *
 * So: keep the original artwork, replace only the TIMING so the segment
 * travels the rim forever, and warm the palette slightly towards red.
 * My own ::after conic experiment is removed so nothing competes.
 * ==================================================================== */

/* stand my experiment down — the original does this properly */
html body .launcher[data-launcher]::after { background: none; animation: none; }
html body .launcher[data-launcher] .launcher__glint { display: none !important; }

/* the halo must be allowed outside the disc, as the original intends */
html body .launcher[data-launcher] { overflow: visible !important; isolation: isolate; }

/* one continuous circuit: no quiet phase, no reset frame */
@keyframes da-launcher-travel {
  from { --launcher-angle: 0deg; }
  to   { --launcher-angle: 360deg; }
}

html body .launcher[data-launcher]::before,
html body .launcher[data-launcher]::after {
  content: "" !important;
  position: absolute !important;
  inset: -1px !important;
  border-radius: 50% !important;
  pointer-events: none !important;
  opacity: 1 !important;
  /* the original brass segment, with more red carried through the tail */
  background: conic-gradient(
    from var(--launcher-angle),
    transparent 0%,
    rgba(197, 166, 106, 0.35) 2.5%,
    rgba(245, 226, 190, 1)    4%,
    rgba(212, 175, 108, 1)    5.2%,
    rgba(150, 58, 46, 1)      6.4%,
    rgba(176, 106, 60, 0.92)  7.6%,
    rgba(197, 166, 106, 0.30) 8.6%,
    transparent 11.5%
  ) !important;
  /* masked to the rim only, exactly as the original does it */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0) !important;
  -webkit-mask-composite: xor !important;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0) !important;
          mask-composite: exclude !important;
  padding: 2px !important;
  z-index: 3 !important;
  animation: da-launcher-travel 6s linear infinite !important;
}
/* the blurred halo behind the crisp line, the original's second layer */
html body .launcher[data-launcher]::after {
  inset: -3px !important;
  padding: 4px !important;
  filter: blur(5px) !important;
  opacity: 0.75 !important;
  z-index: 1 !important;
}
/* crown above both */
html body .launcher[data-launcher] .launcher__glyph { position: relative !important; z-index: 4 !important; }

@media (prefers-reduced-motion: reduce) {
  html body .launcher[data-launcher]::before,
  html body .launcher[data-launcher]::after { animation: none !important; --launcher-angle: 40deg; }
}
/* ======================================================================
 * AMRIT'S LIST — smaller footer, shorter pages. 19 August.
 * Deliberately UNSCOPED so it applies on every page, not just home:
 * Aarav asked for these "on the side too", i.e. site-wide.
 * ==================================================================== */

/* ---- 1 · A SMALLER FOOTER ----
   It was running to ~110px of padding a side with generous link spacing.
   Everything is one step tighter: the column gap, the link rhythm, the
   type sizes and the block padding. No links are removed. */
.site-foot {
  padding-block: clamp(26px, 3vw, 42px) !important;
}
.site-foot__inner { gap: clamp(20px, 2.4vw, 36px) !important; }
.site-foot__nav { gap: clamp(16px, 2vw, 32px) !important; }
.site-foot__heading {
  margin-bottom: 0.5rem !important;
  font-size: 0.68rem !important;
  letter-spacing: 0.12em !important;
}
.site-foot__list { gap: 0.1rem !important; }
.site-foot__list li { margin: 0 !important; }
.site-foot__link {
  display: inline-flex;
  align-items: center;
  min-height: 34px !important;      /* still comfortably tappable in a list */
  padding-block: 0.1rem !important;
  font-size: 0.86rem !important;
  line-height: 1.35 !important;
}
.site-foot__mark { width: clamp(120px, 12vw, 168px) !important; height: auto !important; }
.site-foot__position { font-size: 0.82rem !important; line-height: 1.5 !important; max-width: 42ch; }
.site-foot__office,
.site-foot__office-label { font-size: 0.78rem !important; line-height: 1.45 !important; }
.site-foot__reach { margin-top: 0.75rem !important; }
.site-foot .crail { margin-block: 0.6rem !important; }

/* the strip beneath stays minimal */
.legalstrip { padding-block: 0.5rem !important; font-size: 0.7rem !important; line-height: 1.4 !important; }
.legalstrip__note { max-width: 96ch; margin-inline: auto; opacity: 0.7; }

/* ---- 2 · SHORTER PAGES, EVERYWHERE ----
   One step off the vertical rhythm of every band on every page. This is
   the single biggest lever on length and costs no content. */
.band,
.hx-band,
.spj,
.sgx-hero,
section[class*="__band"] {
  padding-block: clamp(30px, 3.4vw, 52px) !important;
}
/* heading blocks stop reserving so much air beneath them */
.band__head,
.hx-head,
.sgx-phase-heading { margin-bottom: clamp(16px, 1.9vw, 24px) !important; }

/* nothing above may reintroduce a half-viewport plate */
.band > *:last-child,
.hx-band > *:last-child { margin-bottom: 0 !important; }

/* ======================================================================
 * FIX - 19 August 2026: the launcher label comes off every page.
 *
 * The "Speak to Dukes / Five ways to get started" text measured 123px wide
 * at zero height, anchored outside the 58px launcher, so it printed across
 * the crown. It was first clipped on the homepage only; Aarav then found it
 * on /sales/, so it belongs here - this stylesheet is the one every page
 * loads, which makes it the right single place for a launcher fix.
 *
 * CLIPPED, NOT REMOVED. The launcher is an <a> with no aria-label, no
 * aria-labelledby and no title - checked in the browser - so this text is
 * its entire accessible name. display:none would leave a link with no name,
 * which is a worse defect than the one being fixed. The standard clip keeps
 * it for assistive technology and takes it off the screen.
 *
 * Appended rather than edited in place: this file has more than one writer.
 * ==================================================================== */
html body .launcher[data-launcher] .launcher__label,
html body .launcher[data-launcher] .launcher__text,
html body .launcher[data-launcher] .launcher__sub {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px !important;
  padding: 0 !important;
  overflow: hidden !important;
  clip: rect(0 0 0 0) !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
  border: 0 !important;
}


/* ======================================================================
 * 19 August, Aarav homepage review - the greeting and the crown button.
 *   "make the red a bit more like you could see it"
 *   "move the text a bit more to the left so it looks aligned"
 *   "make the x smaller and put the x in the middle - right now it is
 *    hard to tell it is even there"
 *   "make the text bold or something easier to see, more fashion"
 * ==================================================================== */
html body .launcher[data-launcher] {
  background: #8e2938 !important;                 /* a step brighter, reads as red */
  border: 1px solid rgba(251, 248, 242, 0.42) !important;
  box-shadow: 0 0 0 4px rgba(142, 41, 56, 0.18), 0 20px 42px -16px rgba(13, 29, 49, 0.6) !important;
}
html body .launcher[data-launcher]:hover { background: #a23346 !important; }

.dbubble { padding: 1.15rem 1.15rem 1.05rem; }
.dbubble p {
  margin: 0 2.1rem 0.9rem 0;                     /* clears the smaller x, text sits flush left */
  font-size: 1.02rem;
  font-weight: 650;
  line-height: 1.55;
  color: #1d2024;
  text-align: left;
}
.dbubble__x {
  top: 0.6rem !important;
  right: 0.6rem !important;
  width: 26px !important;
  height: 26px !important;
  min-width: 0 !important;
  min-height: 0 !important;
  padding: 0 !important;
  display: grid !important;
  place-items: center !important;
  border-radius: 50% !important;
  font-size: 0.85rem !important;
  line-height: 1 !important;
}
.dbubble__go { font-weight: 800; }

/* ======================================================================
 * 19 August, evening - the chat sized for real laptops. 480x656 was a
 * third of a MacBook screen. The panel now stays a compact card on any
 * display, and the greeting bubble slims with it.
 * ==================================================================== */
.dchat {
  width: min(23.5rem, calc(100vw - 2rem));
  height: min(33rem, calc(100vh - 5.5rem));
}
@media (max-height: 900px) {
  .dchat { height: min(29.5rem, calc(100vh - 4.5rem)); }
}
.dbubble { width: min(20.5rem, calc(100vw - 2.5rem)); }

/* ======================================================================
 * 19 August, late night - Aarav on the Mac: "the receptionist - reduce
 * the size of it, and the text boxes - reduce the size of it". Final
 * sizing layer: everything one real notch down. His earlier verdicts are
 * kept - the brighter red stays, the bubble text stays bold (650), the
 * centred small x stays - only the SCALE comes down. Interactive targets
 * hold at 40px or better.
 * ==================================================================== */
html body .launcher[data-launcher] {
  width: 56px !important;
  height: 56px !important;
}
html body .launcher[data-launcher] .launcher__crown,
.launcher__crown {
  width: 34px !important;
}
.dbubble {
  width: min(17.5rem, calc(100vw - 2.5rem));
  padding: 0.85rem 0.9rem 0.8rem;
  bottom: calc(clamp(14px, 2vw, 28px) + 70px);
}
.dbubble p {
  margin: 0 1.9rem 0.7rem 0;
  font-size: 0.9rem;
  line-height: 1.5;
}
.dbubble__go { min-height: 40px; font-size: 0.84rem; }
.dchat {
  width: min(22rem, calc(100vw - 2rem));
  height: min(30rem, calc(100vh - 5rem));
}
@media (max-height: 900px) {
  .dchat { height: min(26.5rem, calc(100vh - 4.5rem)); }
}

/* ======================================================================
 * 19 August, late - THE COMPACT PREMIUM SKIN, SITE-WIDE. Aarav: the
 * assistant pop-up was oversized on every page but home. `html body`
 * outranks any page-scoped [data-page] rule, so one spec now governs
 * the panel everywhere: a compact card, quieter header, measured type,
 * chips that read as controls rather than balloons.
 * ==================================================================== */
html body .dchat {
  width: min(23.5rem, calc(100vw - 2rem)) !important;
  height: min(33rem, calc(100vh - 5.5rem)) !important;
  border-radius: 18px !important;
}
@media (max-height: 900px) {
  html body .dchat { height: min(29.5rem, calc(100vh - 4.5rem)) !important; }
}
@media (max-width: 47.99em) {
  html body .dchat {
    width: 100% !important;
    height: min(78vh, 34rem) !important;
    right: 0 !important;
    left: 0 !important;
    bottom: 0 !important;
    border-radius: 18px 18px 0 0 !important;
  }
}
html body .dchat__head { padding: 0.8rem 0.9rem !important; gap: 0.6rem !important; }
html body .dchat__mark { width: 30px !important; height: 30px !important; }
html body .dchat__mark img { width: 17px !important; }
html body .dchat__title strong { font-size: 0.92rem !important; }
html body .dchat__title span { font-size: 0.72rem !important; line-height: 1.3 !important; }
html body .dchat__close { width: 38px !important; height: 38px !important; font-size: 1.3rem !important; }
html body .dchat__log { padding: 0.9rem !important; gap: 0.55rem !important; }
html body .dchat__msg p { font-size: 0.92rem !important; line-height: 1.5 !important; padding: 0.55rem 0.8rem !important; }
html body .dchat__chips { gap: 0.45rem !important; }
html body .dchat__chip {
  min-height: 40px !important;
  padding: 0.5rem 0.9rem !important;
  font-size: 0.875rem !important;
  font-weight: 700 !important;
  border: 1px solid rgba(23, 26, 32, 0.2) !important;
  border-radius: 999px !important;
  background: #fffdf8 !important;
  color: #171a20 !important;
  transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}
@media (hover: hover) and (pointer: fine) {
  html body .dchat__chip:hover { background: #17483c !important; border-color: #17483c !important; color: #fffdf8 !important; }
}
html body .dchat__chip:focus-visible { outline: 2px solid #c5a66a !important; outline-offset: 2px !important; }
html body .dchat__input { min-height: 44px !important; font-size: 0.92rem !important; }
html body .dchat__send { min-height: 44px !important; font-size: 0.875rem !important; }
html body .dchat__summary { font-size: 0.875rem !important; }
html body .dchat__route { min-height: 46px !important; font-size: 0.9rem !important; }
html body .dchat__foot { font-size: 0.78rem !important; padding: 0.6rem 0.9rem !important; line-height: 1.45 !important; }
html body .dbubble { width: min(20.5rem, calc(100vw - 2.5rem)) !important; }

/* ======================================================================
 * 19 August, night - THE REPLY AREA STOPS EATING THE CONVERSATION.
 * Five stacked option pills were taller than the log above them, so the
 * chat you were having disappeared behind the choices. The contract now:
 * the LOG owns the panel's height; the reply zone is a compact, capped
 * strip - slim left-aligned option rows with tight gaps, scrolling
 * within itself on the rare step that needs more room.
 * ==================================================================== */
html body .dchat__log {
  flex: 1 1 auto !important;
  min-height: 0 !important;
  overflow-y: auto !important;
}
html body .dchat__reply {
  flex: 0 0 auto !important;
  max-height: 45% !important;
  overflow-y: auto !important;
  padding: 0.55rem 0.9rem 0.7rem !important;
  border-top: 1px solid rgba(23, 26, 32, 0.08);
  background: rgba(23, 26, 32, 0.025);
}
html body .dchat__chips {
  display: flex !important;
  flex-wrap: wrap !important;
  gap: 6px !important;
}
html body .dchat__chip {
  flex: 0 1 auto !important;
  max-width: 100% !important;
  min-height: 38px !important;
  padding: 0.42rem 0.8rem !important;
  font-size: 0.85rem !important;
  line-height: 1.3 !important;
  text-align: left !important;
  border-radius: 10px !important;
}
/* the long journey labels take their own slim row; short answers such as
   bedroom counts sit inline side by side automatically */
html body .dchat__msg p { padding: 0.5rem 0.75rem !important; }
html body .dchat__form { gap: 6px !important; }
html body .dchat__routes { gap: 6px !important; }
html body .dchat__route { min-height: 44px !important; }

/* rev 27.1 - the balance: slightly taller card, tighter option rows,
   reply capped at 40%, so the conversation always holds the majority. */
html body .dchat {
  height: min(35rem, calc(100vh - 5rem)) !important;
}
@media (max-height: 900px) {
  html body .dchat { height: min(31.5rem, calc(100vh - 4.5rem)) !important; }
}
html body .dchat__reply { max-height: 40% !important; }
html body .dchat__chip { min-height: 36px !important; padding: 0.38rem 0.75rem !important; font-size: 0.84rem !important; }
html body .dchat__chips { gap: 5px !important; }

/* ======================================================================
 * 20 August - THE FIN-GRADE POLISH (Aarav's reference: Claude's own
 * support widget). Sender + time captions, user replies as brand chips,
 * a real composer, and the jump-to-latest button.
 * ==================================================================== */
html body .dchat__stamp {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  color: rgba(23, 26, 32, 0.55);
}
html body .dchat__msg--me { align-self: flex-end; text-align: right; }
html body .dchat__msg--me .dchat__stamp { text-align: right; }
html body .dchat__msg--me p {
  background: #7b2430 !important;
  border-color: #7b2430 !important;
  color: #fff7ed !important;
  border-radius: 14px 14px 4px 14px !important;
}
html body .dchat__msg--bot p {
  border-radius: 14px 14px 14px 4px !important;
}
/* the composer */
html body .dchat__form { display: flex; align-items: center; gap: 8px !important; }
html body .dchat__input {
  flex: 1;
  min-height: 44px !important;
  padding: 0.55rem 1rem !important;
  border: 1px solid rgba(23, 26, 32, 0.22) !important;
  border-radius: 999px !important;
  background: #fffdf8 !important;
  font-size: 0.92rem !important;
}
html body .dchat__input:focus-visible { outline: 2px solid #17483c !important; outline-offset: 1px; }
html body .dchat__send {
  flex: 0 0 auto;
  width: 44px !important;
  height: 44px !important;
  min-height: 44px !important;
  padding: 0 !important;
  display: grid !important;
  place-items: center !important;
  border-radius: 50% !important;
  background: #7b2430 !important;
  border: 0 !important;
  color: #fff7ed !important;
  transition: background-color 160ms ease, transform 160ms ease;
}
html body .dchat__send svg { width: 18px; height: 18px; }
@media (hover: hover) and (pointer: fine) {
  html body .dchat__send:hover { background: #5e1924 !important; transform: translateY(-1px); }
}
/* the jump-to-latest button */
html body .dchat__fab {
  position: absolute;
  right: 12px;
  bottom: 132px;
  z-index: 3;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border: 1px solid rgba(23, 26, 32, 0.14);
  border-radius: 50%;
  background: #fffdf8;
  color: #171a20;
  box-shadow: 0 10px 24px -12px rgba(13, 29, 49, 0.5);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transform: translateY(6px);
  transition: opacity 180ms ease, transform 200ms cubic-bezier(0.22, 1, 0.36, 1);
}
html body .dchat__fab.is-on { opacity: 1; pointer-events: auto; transform: none; }
html body .dchat__fab svg { width: 17px; height: 17px; }
html body .dchat { position: fixed; }   /* the fab anchors to the panel */
@media (prefers-reduced-motion: reduce) {
  html body .dchat__fab, html body .dchat__send { transition: none; }
}

/* 20 August - the caption sits UNDER the bubble, not beside it: message
   rows stack as columns, so the stamp is a small line beneath, left for
   the assistant and right for the visitor - the reference layout. */
html body .dchat__msg {
  display: flex !important;
  flex-direction: column !important;
  align-items: flex-start !important;
  gap: 3px;
}
html body .dchat__msg--me { align-items: flex-end !important; }
html body .dchat__msg p { max-width: 88%; }
/* the me-row is shrink-to-fit (align-self: flex-end), so a percentage
   max-width on ITS bubble measures against the shrunken row and wraps
   short names - cap the row instead, let the bubble take natural width */
html body .dchat__msg--me { max-width: 88%; }
html body .dchat__msg--me p { max-width: none !important; }
html body .dchat__stamp { margin-top: 0 !important; white-space: nowrap; }

/* 20 August - the unread count on the crown launcher. Cream coin with an
   oxblood figure, ringed so it reads on the oxblood crown circle. */
.launcher__badge {
  /* pinned to the launcher's own fixed recipe: right/bottom clamp, 56px
     circle - the coin overlaps its top-right edge */
  position: fixed;
  right: calc(clamp(14px, 2vw, 28px) - 5px);
  bottom: calc(clamp(14px, 2vw, 28px) + 42px);
  min-width: 23px;
  height: 23px;
  padding: 0 6px;
  border-radius: 999px;
  display: grid;
  place-items: center;
  background: #fffdf8;
  color: #7b2430;
  border: 2px solid #7b2430;
  font-size: 0.76rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: 0;
  z-index: 62; /* above the launcher (60) and greeting bubble (61) */
  box-shadow: 0 2px 10px rgba(23, 26, 32, 0.3);
  pointer-events: none;
}
.launcher__badge[hidden] { display: none !important; }


/* ====================================================================
   MOBILE STRESS PASS - 20 Aug 2026, from Aarav's phone screenshots.
   ==================================================================== */

/* the greeting, launcher and badge never sit on top of the open menu */
body:has([data-menu][open]) .dbubble,
body:has([data-menu][open]) .launcher,
body:has([data-menu][open]) .launcher__badge,
body:has(.nav [aria-expanded="true"]) .dbubble {
  visibility: hidden !important;
}

/* phones: the chat is a full-screen sheet, like every real chat widget */
@media (max-width: 43.99em) {
  html body .dchat {
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    width: 100% !important;
    height: 100vh !important;
    height: 100dvh !important;
    max-height: none !important;
    border-radius: 0 !important;
    border: 0 !important;
  }
  html body .dchat__fab { bottom: 120px; }
}

/* short landscape phones: the greeting stays out of the reading line */
@media (max-height: 480px) {
  .dbubble { max-width: 15rem; }
}

/* short landscape phones: the menu's sticky action bar pinned mid-panel
   and covered the list rows (Aarav's screenshot, 20 Aug). In a viewport
   under 480px tall it flows after the list instead - scroll reaches it,
   nothing overlaps. Behaviour fix only; the shell's design is untouched. */
@media (max-height: 480px) {
  [data-menu] .menu__actions { position: static !important; }
}

/* the /sales/ header carries backdrop-filter, which makes it the
   containing block for the fixed menu panel: "bottom: 0" then means the
   HEADER's bottom, and the whole menu renders as a 50px sliver (Aarav's
   "the dropdown doesn't come", 20 Aug). An explicit height needs no
   bottom edge, so the panel fills the viewport inside or outside the
   trap. Identical geometry on unaffected pages. */
@media (max-width: 63.99em) {
  .menu[open] .menu__panel {
    inset: var(--head-h, 61px) 0 auto;
    height: calc(100svh - var(--head-h, 61px));
  }
}

/* ====================================================================
   20 Aug - Aarav: the legal strip at the very end "looks off". It
   stays (the company line is the legal identity; the film note is the
   provenance guard while the hero footage is uncleared) but it now
   reads like a designed close: centred, one quiet hierarchy, a gold
   hairline above, the note on a readable measure at whisper contrast.
   Sitewide file so every page's strip matches.
   ==================================================================== */
.legalstrip {
  padding: 1.6rem max(1.25rem, 3vw) 1.8rem !important;
  text-align: center;
  border-top: 1px solid rgba(197, 166, 106, 0.32);
}
.legalstrip__line {
  margin: 0 !important;
  font-size: 0.8rem !important;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(244, 238, 227, 0.85) !important;
}
.legalstrip__note {
  order: 2;
  max-width: 72ch;
  margin: 0.6rem auto 0 !important;
  font-size: 0.72rem !important;
  line-height: 1.6 !important;
  color: rgba(244, 238, 227, 0.78) !important;
  text-align: center !important;
}
.legalstrip { display: flex; flex-direction: column; }
.legalstrip__line { order: 1; }

/* PHONE READING MODE — the section rail must not cover content. The Dukes
   Assistant remains available as a touch-friendly floating control. */
@media (max-width: 47.99em) {
  html body .hxnav.hxnav,
  html body [data-hxnav].hxnav {
    display: none !important;
  }
}

/* 21 Aug 2026 — the red crown stays visible. The stow rule hid the launcher
   outright (opacity 0, visibility hidden) whenever it overlapped a region
   marked data-launcher-clear, which is most of /about/. Aarav: "ensure the
   red crown assistant button is visible." Stowed now means smaller and
   nudged, never gone, and still pressable. Appended: this file has another
   writer. */
[data-enhanced] .launcher.is-stowed,
[data-enhanced] .launcher[data-launcher-in="on"].is-stowed {
  opacity: 0.92 !important;
  visibility: visible !important;
  transform: translate3d(0, 0, 0) scale(0.82) !important;
  pointer-events: auto !important;
}

/* 21 Aug 2026 — the assistant opens in the MIDDLE of the screen, not docked
   to the side. Aarav: "the chat assistant should appear in the middle
   instead, not on the side." Then on the phone: "you can't get rid of the
   chat, it's too close to the top; smaller and in the middle."

   Measured on a 390px phone: the sticky header is z-index 1000 and the
   panel was 120, so the header painted OVER the panel's close button - the
   × sat under the Menu button and could not be pressed. The panel and its
   backdrop now sit above the header. On phones it is a centred card with
   clear space around it, not a full-screen sheet, and the page behind it
   stops scrolling while it is open. Appended: this file has another writer. */
html body .dchat[data-dukes-chat] {
  position: fixed !important;
  top: 50% !important;
  left: 50% !important;
  right: auto !important;
  bottom: auto !important;
  transform: translate(-50%, -50%) !important;
  width: min(600px, 94vw) !important;
  max-width: 94vw !important;
  height: min(80vh, 760px) !important;
  max-height: 92vh !important;
  z-index: 2001 !important;
  border-radius: 18px !important;
  box-shadow: 0 40px 90px -40px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(0, 0, 0, 0.06) !important;
}
html body .dchat[data-dukes-chat][hidden] { display: none !important; }

/* the ground behind it, above the header too */
html body .dchat__backdrop {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(13, 29, 49, 0.46);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
html body .dchat__backdrop[hidden] { display: none; }

/* the close control: a real 44px target, always reachable */
html body .dchat[data-dukes-chat] .dchat__close {
  min-width: 44px !important;
  min-height: 44px !important;
}

/* the page stops scrolling under an open assistant */
html body.has-dchat-open { overflow: hidden !important; }

@media (max-width: 47.99em) {
  html body .dchat[data-dukes-chat] {
    width: 92vw !important;
    max-width: 92vw !important;
    height: min(74svh, 640px) !important;
    max-height: 80svh !important;
    border-radius: 18px !important;
  }
}

/* 21 Aug 2026 — the centred panel's entrance, on every page including the
   homepage and on the phone.
   Aarav: "when we open it again it goes to the middle but it has this
   animation from the right corner to the middle and lags" and "we need it
   in the middle on the phone too."
   Cause: home-2026.css keeps its own dchat-pop keyframe - a scale from the
   BOTTOM-RIGHT corner (transform-origin 100% 100%) whose transform frames
   fight the centring translate, so the card visibly slides in from the
   corner - and a phone rule pinning the panel full-width to the bottom.
   One entrance now, from the centre, 180ms, and the phone card is centred
   like the desktop one. Doubled class + attribute outranks every page
   rule; !important on each property outranks the homepage's own. */
@keyframes dchat-centre-in {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.96); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

html body .dchat.dchat[data-dukes-chat] {
  position: fixed !important;
  top: 50% !important;
  left: 50% !important;
  right: auto !important;
  bottom: auto !important;
  inset: 50% auto auto 50% !important;
  transform: translate(-50%, -50%) !important;
  transform-origin: 50% 50% !important;
  animation: dchat-centre-in 180ms ease-out both !important;
  transition: none !important;
  width: min(600px, 94vw) !important;
  max-width: 94vw !important;
  height: min(80vh, 760px) !important;
  max-height: 92vh !important;
  border-radius: 18px !important;
  margin: 0 !important;
}
html body .dchat.dchat[data-dukes-chat][hidden] { display: none !important; animation: none !important; }
@media (max-width: 47.99em) {
  html body .dchat.dchat[data-dukes-chat] {
    width: 92vw !important;
    max-width: 92vw !important;
    height: min(74svh, 640px) !important;
    max-height: 80svh !important;
    border-radius: 18px !important;
    inset: 50% auto auto 50% !important;
    transform: translate(-50%, -50%) !important;
  }
}

/* 26 Aug 2026 — greeting card layout: the dismiss control overlaps the
   top-left corner, the copy uses the full card width, and the CTA is centred. */
html body .dbubble {
  display: flex !important;
  flex-direction: column !important;
  align-items: stretch !important;
  overflow: visible !important;
  width: min(20rem, calc(100vw - 2rem)) !important;
  max-width: calc(100vw - 2rem) !important;
  box-sizing: border-box !important;
  padding: 1.25rem 1.25rem 1.1rem !important;
  border: 1px solid rgba(123, 36, 48, 0.18) !important;
  border-radius: 18px !important;
  background: #fbf8f2 !important;
  box-shadow: 0 24px 52px -24px rgba(13, 29, 49, 0.5) !important;
}
html body .dbubble p {
  width: 100% !important;
  max-width: none !important;
  margin: 0 0 1rem !important;
  font-size: 0.95rem !important;
  font-weight: 620 !important;
  line-height: 1.5 !important;
}
html body .dbubble .dbubble__x {
  top: -10px !important;
  right: -10px !important;
  bottom: auto !important;
  left: auto !important;
  width: 32px !important;
  height: 32px !important;
  min-width: 32px !important;
  min-height: 32px !important;
  margin: 0 !important;
  padding: 0 !important;
  display: grid !important;
  place-items: center !important;
  border-radius: 50% !important;
  font-size: 0.8rem !important;
  box-shadow: 0 8px 18px -9px rgba(13, 29, 49, 0.55) !important;
  transform: none !important;
}
html body .dbubble .dbubble__x::before {
  content: "";
  position: absolute;
  inset: -6px;
}
html body .dbubble .dbubble__go {
  align-self: center !important;
  margin: 0 auto !important;
  min-height: 42px !important;
  padding: 0.65rem 1rem !important;
  border-radius: 10px !important;
}
@media (prefers-reduced-motion: reduce) {
  html body .dchat.dchat[data-dukes-chat] { animation: none !important; }
}

/* 21 Aug 2026 — hover-lift flicker, fixed structurally.
   Aarav: "when I hover over this contact box the cursor changes from the
   hand to the arrow rapidly and you cannot click it."
   Measured on /receptionist/: the contact tile lifts 2px on hover with a
   220ms transition. With the pointer near the tile's bottom edge, the lift
   moves the tile out from under the pointer, hover ends, the tile drops
   back under the pointer, hover starts - and it oscillates every frame.
   The anchor's box now stays put and the lift moves the glyph inside it,
   so the hit area never changes under the pointer. Same treatment for
   every lift-on-hover control on the site. Appended: shared file, every
   page loads it. */
.crail__link:hover,
.crail__link:focus-visible,
.crail__item:hover .crail__link { transform: none !important; }
.crail__link .crail__glyph { transition: transform 0.22s cubic-bezier(0.4, 0, 0.2, 1); }
.crail__link:hover .crail__glyph,
.crail__link:focus-visible .crail__glyph { transform: translateY(-2px); }

/* ========================================================================== *
 *  26 AUG 2026 — PHONE QA CONSOLIDATION
 *
 *  One final mobile authority for the issues found in the all-route audit:
 *  the real header seam drives every drawer, the drawer keeps the same warm
 *  parchment as the header, proactive chat never covers phone content, and
 *  route plates size themselves to their parent instead of to the viewport.
 *  Desktop layout and the established Dukes palette are intentionally left
 *  untouched.
 * ========================================================================== */

@media (max-width: 73.99em) {
  html body .site-head {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }

  html body .menu[open] .menu__panel,
  html body.page--sales-valuation .menu[open] .menu__panel {
    inset: var(--head-h, 88px) 0 0 !important;
    width: 100% !important;
    height: auto !important;
    max-width: none !important;
    max-height: calc(100dvh - var(--head-h, 88px)) !important;
    box-sizing: border-box !important;
    padding: clamp(1rem, 3vh, 1.6rem) var(--inset, 1rem)
      max(1rem, env(safe-area-inset-bottom)) !important;
    background-color: #eadfcd !important;
    background-image: var(--surface-nav-material) !important;
    border-top: 1px solid var(--surface-nav-keyline, rgb(107 70 32 / 24%)) !important;
    overflow-x: clip !important;
    overflow-y: auto !important;
  }

  html body .menu[open] .menu__actions,
  html body.page--sales-valuation .menu[open] .menu__actions {
    position: static !important;
    margin-top: clamp(1.25rem, 4vh, 2.5rem) !important;
    padding: 1rem 0 max(0.5rem, env(safe-area-inset-bottom)) !important;
    background: transparent !important;
    box-shadow: none !important;
  }

  html body[data-menu-open] .launcher,
  html body[data-menu-open] .dbubble,
  html body[data-menu-open] .dchat__backdrop,
  html body[data-menu-open] .dchat[data-dukes-chat] {
    display: none !important;
  }
}

@media (max-width: 47.99em) {
  html,
  body {
    max-width: 100% !important;
    overflow-x: clip !important;
  }

  html body main,
  html body .main,
  html body main > *,
  html body .main > * {
    min-width: 0;
    box-sizing: border-box;
  }

  html body .crumbs,
  html body .crumbs__list,
  html body .breadcrumb,
  html body .breadcrumb__list {
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }

  html body .dbubble {
    display: none !important;
  }

  html body .launcher {
    right: max(0.8rem, env(safe-area-inset-right)) !important;
    bottom: max(0.8rem, env(safe-area-inset-bottom)) !important;
    width: 3.25rem !important;
    height: 3.25rem !important;
  }

  html body .launcher.is-stowed {
    opacity: 0.78 !important;
    pointer-events: auto !important;
    transform: scale(0.76) !important;
    transform-origin: right bottom !important;
  }

  /* The sales-valuation form deliberately stows the launcher while a field is
     underneath it. Keep that collision state hidden on this one working form;
     the launcher returns as soon as the field is clear. */
  html body.page--sales-valuation-start .launcher.is-stowed {
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
  }

  /* Every assistant action remains comfortably tappable without making the
     panel feel oversized on a phone. */
  html body .dchat {
    left: max(0.75rem, env(safe-area-inset-left)) !important;
    right: max(0.75rem, env(safe-area-inset-right)) !important;
    width: auto !important;
  }

  html body .dchat__chip,
  html body .dchat__fab,
  html body .dchat__foot a {
    min-width: 44px !important;
    min-height: 44px !important;
  }

  html body .dchat__foot a {
    display: inline-flex !important;
    align-items: center !important;
  }

  html body main,
  html body .main {
    padding-bottom: calc(4.75rem + env(safe-area-inset-bottom));
  }

  html body .site-foot__link,
  html body .site-foot a,
  html body .menu__contact-link {
    min-height: 44px;
  }

  /* Tenant fees: the page index used to have zero height, so it floated over
     both the header and the opening copy. It now owns a real row in flow. */
  html body.page--tenant-fees details.tf2-nav {
    position: relative !important;
    inset: auto !important;
    top: auto !important;
    z-index: 30 !important;
    width: 100% !important;
    height: auto !important;
    margin: 0 auto 0.75rem !important;
    padding: 0 var(--inset, 1rem) !important;
    display: flex !important;
    justify-content: center !important;
    box-sizing: border-box !important;
  }

  html body.page--tenant-fees .tf2-nav__pill {
    width: min(100%, 22rem) !important;
    min-width: 0 !important;
    max-width: 100% !important;
    min-height: 48px !important;
    justify-content: center !important;
    box-sizing: border-box !important;
    transform: none !important;
  }

  html body.page--tenant-fees .tf2-nav__panel {
    top: var(--head-h, 88px) !important;
    bottom: 0 !important;
    width: min(20rem, calc(100vw - 0.75rem)) !important;
    max-width: calc(100vw - 0.75rem) !important;
    max-height: calc(100dvh - var(--head-h, 88px)) !important;
    box-sizing: border-box !important;
    padding-bottom: max(1.25rem, env(safe-area-inset-bottom)) !important;
  }

  html body.page--tenant-fees .tf2-hero,
  html body.page--tenant-fees .tf2-frame,
  html body.page--tenant-fees .tf2-hero__inner,
  html body.page--tenant-fees .tf2-schedule,
  html body.page--tenant-fees .tf2-ledger,
  html body.page--tenant-fees .tf2-chapter,
  html body.page--tenant-fees .tf2-contact,
  html body.page--tenant-fees .tf2-downloads {
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }

  html body.page--tenant-fees .tf2-schedule__item,
  html body.page--tenant-fees .tf2-fee__summary {
    grid-template-columns: minmax(0, 1fr) !important;
    gap: 0.35rem !important;
  }

  /* The protection and guaranteed-rent pages placed 94vw cards inside
     already-padded parents, creating a repeatable 12px horizontal spill. */
  html body.page--client-protection .cpl-hero,
  html body.page--client-protection .cpl-hero__grid,
  html body.page--client-protection .cpl-section,
  html body.page--client-protection .cpl-section__grid,
  html body.page--client-protection .cpl-meaning,
  html body.page--client-protection .cpl-meaning__inner,
  html body.page--client-protection .cpl-company,
  html body.page--client-protection .cpl-company__inner,
  html body.page--client-protection .cpl-sources,
  html body.page--client-protection .cpl-sources__inner,
  html body.page--client-protection .cpl-stepper-band,
  html body.page--client-protection .cpl-stepper-band__inner,
  html body.page--guaranteed-rent .grb-hero__plate,
  html body.page--guaranteed-rent .grb-hero__grid,
  html body.page--guaranteed-rent .grb-route__plate,
  html body.page--guaranteed-rent .grb-proof__plate,
  html body.page--guaranteed-rent .grb-weigh__plate,
  html body.page--guaranteed-rent .grb-terms__inner,
  html body.page--guaranteed-rent .grb-close__inner,
  html body.page--guaranteed-rent .grb-notes__inner {
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    margin-inline: auto !important;
    box-sizing: border-box !important;
  }

  /* Sales landing: contain the photo mosaic instead of letting the second
     tile establish a wider implicit grid track. */
  html body.page--sales .sdr-route,
  html body.page--sales .sdr-route__details,
  html body.page--sales .sdr-route__mosaic,
  html body.page--sales .sdr-route__media,
  html body.page--sales .sdr-mosaic__tile {
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }

  html body.page--sales .sdr-route__mosaic {
    grid-template-columns: minmax(0, 1fr) !important;
  }

  html body.page--sales .sdr-route__mosaic img,
  html body.page--sales .sdr-route__media img {
    width: 100% !important;
    max-width: 100% !important;
    object-fit: cover !important;
  }

  /* Seller's guide: remove viewport-width breakout math on the phone. */
  html body.page--sellers-guide .sgx-guide-shell,
  html body.page--sellers-guide .sgx-guide-main,
  html body.page--sellers-guide .sgx-hero,
  html body.page--sellers-guide .gx,
  html body.page--sellers-guide .gx__stages,
  html body.page--sellers-guide .gx__phase-block,
  html body.page--sellers-guide .sgx-human-story,
  html body.page--sellers-guide .sgx-outlook,
  html body.page--sellers-guide .sgx-proof,
  html body.page--sellers-guide .sgx-contact {
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    margin-inline: auto !important;
    box-sizing: border-box !important;
  }

  html body.page--sellers-guide h1,
  html body.page--sellers-guide .sgx-hero h1 {
    font-size: clamp(2.55rem, 13vw, 4.25rem) !important;
    overflow-wrap: anywhere;
  }

  /* Selling process: every editorial band gets the same phone measure. */
  html body.page--selling-process .spj,
  html body.page--selling-process .spj-hero,
  html body.page--selling-process .spj-readiness,
  html body.page--selling-process .spj-next,
  html body.page--selling-process .spj-credentials,
  html body.page--selling-process .spj-proof,
  html body.page--selling-process .spj-faq,
  html body.page--selling-process .spj-stage,
  html body.page--selling-process .spj-route-sequence {
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    margin-inline: auto !important;
    box-sizing: border-box !important;
  }

  html body.page--selling-process .spj__heading,
  html body.page--selling-process .spj__heading-copy,
  html body.page--selling-process .spj-credentials__head,
  html body.page--selling-process .spj-next > header {
    position: relative !important;
    inset: auto !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    margin-inline: auto !important;
    transform: none !important;
  }

  html body.page--selling-process .spj-credentials {
    overflow-x: auto !important;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }

  html body.page--selling-process .spj-credentials [class*="track"] {
    animation: none !important;
    transform: none !important;
  }

  html body.page--selling-process .spj-credentials [class*="item"] {
    max-width: calc(100vw - 3rem) !important;
    scroll-snap-align: center;
  }

  /* Rental valuation and calculators: child controls may shrink rather than
     force their grid wider than the phone. */
  html body.page--valuation .rv-open__inner,
  html body.page--valuation .rv__form,
  html body.page--valuation .rv__body,
  html body.page--valuation .rv__side,
  html body.page--valuation .rvx-hero,
  html body.page--valuation .rvx-hero__media,
  html body.page--stamp-duty .sdc,
  html body.page--stamp-duty .sdc__panel,
  html body.page--stamp-duty .sdc__form {
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    margin-inline: auto !important;
    box-sizing: border-box !important;
  }

  html body.page--valuation .rv-open__inner,
  html body.page--valuation .rv__form,
  html body.page--stamp-duty .sdc__panel {
    margin-left: 0 !important;
    margin-right: 0 !important;
  }

  /* About and guide illustrations may bleed decoratively, but never grow the
     page's scrollable canvas. */
  html body.page--about main,
  html body.page--about .main,
  html body.page--landlord-guide main,
  html body.page--landlord-guide .main {
    width: 100% !important;
    max-width: 100% !important;
    overflow-x: clip !important;
  }
}
