/* ═══════════════════════════════════════════════════════════════════════════
   KEELAMBI MOBILE APP LAYER  ·  ≤767px  ·  2026-08-24
   ───────────────────────────────────────────────────────────────────────────
   ONE bottom action bar, ONE Bullman entry point, ONE z-index scale.

   Before this file the phone carried six independent bottom implementations —
   .stickybar (home/flagship), .kb-live (ROI tool), .dt-dock (Doti), .pg-bar
   (SELCO), .srm-bar (experience) and the floating .bmh-fab — each with its own
   z-index (55/60/70/56), its own clearance hack, and its own "suppress the
   other one" rule. The fab still landed on top of body copy and product cards.

   The contract now:
     · the DOCK is the only thing fixed to the bottom edge on a phone;
     · a page's existing primary action is MOVED into it (never re-authored, so
       every href, data-attribute, analytics hook and data-en/data-kn span
       travels with the node and desktop gets it back verbatim on resize);
     · a page that legitimately needs the bottom edge for a step action (ROI
       wizard Continue, builder total) takes it, and the dock yields — the same
       BottomSlot priority contract that already existed, now global;
     · clearance is MEASURED from the rendered dock, never hard-coded.

   Scope gate: every rule here is inside @media(max-width:767px) or behind
   html.mapp-on (set by mobile-app.js only when that query matches). Desktop
   and tablet ≥768px render byte-identically to before.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── ONE declared stacking order ─────────────────────────────────────────────
   Values preserve the existing effective order (nav 60 > overlay 59 > mega 58)
   so nothing re-stacks by surprise; what changes is that every layer now names
   a token instead of inventing a number. The six bottom-edge elements that had
   55/56/58/60/60/70 between them all collapse onto --kz-dock. */
:root{
  --kz-instick:40;      /* in-page sticky strips: chapter nav, spec spine      */
  --kz-dock:52;         /* the bottom action bar — the ONLY bottom-edge layer  */
  --kz-mega:58;         /* desktop mega panels                                */
  --kz-drawer:59;       /* mobile drawer + its scrim (full-screen)             */
  --kz-header:60;       /* the top bar, always reachable                       */
  --kz-burger:61;       /* the close affordance, above its own drawer          */
  --kz-sheet-scrim:94;  /* modal sheet backdrop                                */
  --kz-sheet:95;        /* modal sheet surface (Bullman hub)                   */
  --kz-toast:96;        /* transient confirmations                            */

  --mapp-ease:cubic-bezier(.22,1,.36,1);
  /* measured by mobile-app.js from the rendered bar; the fallback is only ever
     used for the frame before the first measurement lands */
  --mapp-bar-h:76px;
  --mapp-gap:10px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   1 · RE-POINT THE EXISTING FIXED LAYERS AT THE SCALE
   No element moves; the numbers simply stop being arbitrary. .dt-dock is the
   only real re-stack: it shipped at z70, above the nav(60) and the drawer(59),
   so on any page that gained a drawer it would have painted over the open menu.
   ═══════════════════════════════════════════════════════════════════════════ */
.stickybar,.kb-live,.dt-dock,.pg-bar,.srm-bar,.bm-chip,.bmh-fab{z-index:var(--kz-dock)}
.navmega{z-index:var(--kz-mega)}
.nav-overlay{z-index:var(--kz-drawer)}
.nav{z-index:var(--kz-header)}
.nav-burger[aria-expanded="true"]{z-index:var(--kz-burger)}
.bmh-scrim{z-index:var(--kz-sheet-scrim)}
.bmh{z-index:var(--kz-sheet)}
.kcard-toast,.kb-toast{z-index:var(--kz-toast)}

/* ═══════════════════════════════════════════════════════════════════════════
   2 · THE DOCK
   ═══════════════════════════════════════════════════════════════════════════ */
.mapp-bar{
  position:fixed;left:0;right:0;bottom:0;z-index:var(--kz-dock);
  display:none;
  padding:10px 12px max(10px,env(safe-area-inset-bottom,0px));
  background:rgba(252,251,248,.92);
  -webkit-backdrop-filter:blur(22px) saturate(1.6);backdrop-filter:blur(22px) saturate(1.6);
  border-top:0.5px solid rgba(17,24,20,.12);
  transform:translateY(102%);
  transition:transform .28s var(--mapp-ease);
  /* the bar is chrome: it must never be what a screen-reader reads first, but
     it must never be skipped either — it is a landmark, not a decoration */
  contain:layout paint;
}
html.mapp-on .mapp-bar{display:block}
html.mapp-on .mapp-bar.is-up{transform:none}
@media(prefers-reduced-motion:reduce){.mapp-bar{transition:none}}

.mapp-bar-in{display:flex;align-items:stretch;gap:8px;max-width:560px;margin-inline:auto}

/* — the two side actions: identity, not decoration.
     52px box, 44px minimum touch target satisfied by the box itself. — */
.mapp-btn{
  flex:0 0 auto;width:52px;min-height:52px;padding:0;border-radius:16px;cursor:pointer;
  display:flex;align-items:center;justify-content:center;overflow:hidden;
  border:0.5px solid rgba(17,24,20,.12);background:rgba(255,255,255,.72);
  color:var(--ink,#1D1D1F);text-decoration:none;
  transition:transform .14s var(--mapp-ease),background-color .14s var(--mapp-ease);
}
.mapp-btn:active{transform:scale(.955);background:rgba(255,255,255,.95)}
.mapp-btn:focus-visible{outline:2px solid var(--saffron,#FF9933);outline-offset:2px}
@media(prefers-reduced-motion:reduce){.mapp-btn:active{transform:none}}

.mapp-btn--bull{padding:3px}
.mapp-btn--bull img{width:100%;height:100%;object-fit:cover;border-radius:13px;display:block}
.mapp-btn--wa{background:#25D366;border-color:#1FAE53;color:#fff}
.mapp-btn--wa:active{background:#1FAE53}
/* the phone stands in where the primary is already WhatsApp — a quiet
   surface, because it is the alternative channel, not the action */
.mapp-btn--call{background:rgba(255,255,255,.72);color:var(--ink,#1D1D1F)}
.mapp-btn--call:active{background:rgba(255,255,255,.95)}
.mapp-btn svg{width:23px;height:23px}

/* — the slot: whatever this page's primary bottom action is — */
.mapp-slot{flex:1 1 auto;min-width:0;display:flex}
.mapp-slot:empty{display:none}

/* The adopted node keeps its own classes (.btn/.kb-btn/.dt-dock .kb-btn …), so
   these rules are deliberately written to win on the two properties that decide
   dock layout — size and shape — and to leave colour alone. A page's primary
   button stays the colour its own design system gave it. */
html.mapp-on .mapp-slot > *{
  flex:1 1 auto;min-width:0;width:100%;
  min-height:52px;margin:0;
  display:flex;align-items:center;justify-content:center;gap:8px;
  border-radius:16px;
  font-size:15px;line-height:1.25;font-weight:600;
  text-align:center;
  /* R8: Kannada is never truncated to fit chrome. It wraps, the bar grows, and
     the measured clearance grows with it. No ellipsis, no white-space:nowrap. */
  white-space:normal;overflow-wrap:break-word;word-break:normal;
  padding:8px 14px;
}
html[lang="kn"] .mapp-slot > *{font-family:var(--f-kn,"Noto Sans Kannada",sans-serif);line-height:1.35}
@media(max-width:369px){ html.mapp-on .mapp-slot > *{font-size:14px;padding-inline:10px} }

/* the ROI tool's live readout is an OUTPUT, not a CTA — it keeps its own
   two-value shape inside the slot instead of being painted as a saffron pill */
html.mapp-on .mapp-slot > .kb-live{
  position:static;transform:none;box-shadow:none;
  display:grid;grid-template-columns:minmax(0,1fr) auto;
  grid-template-areas:"lab lab" "val pb";
  align-items:center;justify-items:start;gap:1px 8px;
  padding:7px 14px;text-align:left;
  background:rgba(255,255,255,.9);border:0.5px solid rgba(17,24,20,.12);
}
/* the three readout parts each get their own cell: at 360px a flex row squeezed
   the Kannada caption to one glyph per line, which is exactly the clipping R8
   forbids. Grid gives the caption the full width it needs. */
html.mapp-on .mapp-slot > .kb-live .kl-lab{grid-area:lab;font-size:11px;line-height:1.3;white-space:nowrap}
html.mapp-on .mapp-slot > .kb-live .kl-val{grid-area:val;font-size:19px;text-align:left;white-space:nowrap}
html.mapp-on .mapp-slot > .kb-live .kl-pb{grid-area:pb;justify-self:end;font-size:11px;white-space:nowrap;padding:3px 8px}


/* The label swap used to be written as `.stickybar .sbr-short`; the node now
   lives in the dock, so the same contract is restated for its new parent.
   html.sb-price is still set by the page's own price-context observer. */
html.mapp-on .mapp-slot .sbr-short{display:inline}
html.mapp-on .mapp-slot .sbr-full{display:none}
html.mapp-on.sb-price .mapp-slot .sbr-short{display:none}
html.mapp-on.sb-price .mapp-slot .sbr-full{display:inline}

/* the catalogue top bar's green WhatsApp pill was a conversion CTA parked in
   the header; it is the dock's right-hand action now. The node stays in the
   DOM because it is where the page writes its contextual pre-fill. */
@media(max-width:767px){
  html.mapp-on .kb-bar .kb-btn-wa{display:none}
}

/* optional second line adopted from a page bar (Doti grade+price, SELCO, srm) */
/* it sits INSIDE the primary button, so it takes the button's own ink rather
   than the page's muted grey — dim grey on saffron fails contrast outdoors,
   which is the one lighting condition this audience actually reads in */
.mapp-sub{
  flex:0 0 auto;font-size:13px;font-weight:700;line-height:1.2;
  color:inherit;opacity:.78;font-variant-numeric:tabular-nums;
  white-space:nowrap;
}

/* ── the dock stands down for a full-screen layer, never fights it ──────────
   Written at the same weight as `html.mapp-on .mapp-bar.is-up` plus one, so
   the stand-down always wins; a lighter selector silently lost to .is-up. */
html.mapp-on.nav-open .mapp-bar.is-up,
html.mapp-on.bmh-open .mapp-bar.is-up,
html.mapp-on.bm-tour .mapp-bar.is-up,
html.mapp-on.mapp-yield .mapp-bar.is-up{transform:translateY(102%);pointer-events:none}

/* ═══════════════════════════════════════════════════════════════════════════
   3 · CLEARANCE — measured, and applied where it does not paint a seam
   .dt-dock learned this the hard way: padding-bottom on <body> paints the page
   background BELOW a coloured footer and reads as a white strip. So the
   clearance goes on the footer when the page has one, and on <body> only when
   it does not (mobile-app.js sets .mapp-nofoot).
   ═══════════════════════════════════════════════════════════════════════════ */
@media(max-width:767px){
  html.mapp-on .kb-foot,
  html.mapp-on .footer{
    padding-bottom:calc(var(--mapp-foot-pad,32px) + var(--mapp-bar-h) + var(--mapp-gap));
  }
  html.mapp-on.mapp-nofoot body{
    padding-bottom:calc(var(--mapp-bar-h) + var(--mapp-gap));
  }
  /* every page that hard-coded its own reservation hands it back — the measured
     value is the only one now, so a wrapped Kannada CTA can never trap content */
  html.mapp-on body{padding-bottom:0}
  html.mapp-on.mapp-nofoot body{padding-bottom:calc(var(--mapp-bar-h) + var(--mapp-gap))}
  html.mapp-on body.has-dock .kb-foot{padding-bottom:calc(var(--mapp-foot-pad,32px) + var(--mapp-bar-h) + var(--mapp-gap))}

  /* anything the page anchors to the bottom edge for a reason (toasts, PWA
     prompts) rides above the dock instead of under it */
  html.mapp-on .pwa-update,
  html.mapp-on .pwa-toast,
  html.mapp-on .kcard-toast{
    bottom:calc(var(--mapp-bar-h) + 12px);
  }
  /* the install prompt docked itself UNDER the header and covered page content
     (it sat over the footer's Contact link at the bottom of the homepage).
     It belongs with the other transient bottom-edge messages. */
  html.mapp-on .pwa-install{
    top:auto;bottom:calc(var(--mapp-bar-h) + 12px);
    transform:translate(-50%,200%);
  }
  html.mapp-on .pwa-install.show{transform:translate(-50%,0)}
}

/* ═══════════════════════════════════════════════════════════════════════════
   4 · RETIRE THE FLOATERS  (≤767px)
   Not "hidden behind CSS while the old handler still runs" — mobile-app.js
   also un-mounts the fab from the DOM at this width. These rules are the
   belt to that braces, and cover the frame before JS boots.
   ═══════════════════════════════════════════════════════════════════════════ */
@media(max-width:767px){
  .bmh-fab,          /* floating Bullman portrait — now the dock's left button */
  .bm-chip{          /* Bullman re-entry chip — same entry point, twice        */
    display:none!important;
  }
  /* the adopted bars: their node lives in the dock now, the empty shell must
     not keep painting a second bar behind it */
  html.mapp-on .stickybar,
  html.mapp-on .pg-bar,
  html.mapp-on .srm-bar,
  html.mapp-on .dt-dock{
    display:none!important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   5 · BULLMAN TOUR → a docked sheet, not a floating card
   The tour is deliberately NON-modal: it narrates the section behind it, so a
   scrim would hide the very thing it is describing (this is the Google-Maps
   sheet, not the modal sheet). It gets the sheet's shape, grab handle, safe
   area and drag-to-dismiss — everything except the dimming.

   Only the OUTER FRAME is touched. .bm-caption--fig is a two-column grid
   (figure · copy); the handle is therefore absolutely positioned rather than
   added as a ::before child, which would take the figure's grid cell and push
   every following element into the wrong row. bullman.css keeps ownership of
   everything inside the sheet.
   ═══════════════════════════════════════════════════════════════════════════ */
@media(max-width:767px){
  html.mapp-on .bm-caption{
    left:0;right:0;bottom:0;z-index:var(--kz-dock);
    border-radius:22px 22px 0 0;border-left:0;border-right:0;border-bottom:0;
    padding:22px 18px calc(16px + max(6px,env(safe-area-inset-bottom,0px)));
    box-shadow:0 -8px 40px -18px rgba(0,0,0,.55);
    max-height:74vh;overflow-y:auto;-webkit-overflow-scrolling:touch;
    overscroll-behavior:contain;
    transition:transform .26s var(--mapp-ease);
    touch-action:pan-y;
  }
  /* the grab handle: absolute, so it never becomes a grid item */
  html.mapp-on .bm-caption::after{
    content:"";position:absolute;top:9px;left:50%;transform:translateX(-50%);
    width:38px;height:4px;border-radius:4px;background:rgba(255,255,255,.28);
    pointer-events:none;
  }
  html.mapp-on .bm-caption.bm-drag{transition:none}
  /* the tour owns the bottom edge while it runs */
  html.mapp-on.bm-tour .mapp-bar.is-up{transform:translateY(102%);pointer-events:none}
}
@media(prefers-reduced-motion:reduce){html.mapp-on .bm-caption{transition:none}}

/* ═══════════════════════════════════════════════════════════════════════════
   6 · NATIVE DRAWER  (≤1023px — the whole range the drawer exists in, so there
   is exactly one drawer presentation rather than two behind a breakpoint)
   The drawer is a LIGHT surface (.navrow ships color:var(--ink)), so the group
   tones are drawn from the light token set. Rows become grouped cards: the
   iOS-Settings / Material-3 idiom, 12px radius, hairline dividers inside a
   group, a quiet section label above it.

   The drawer already ships its own sticky action bar (.navdrawer-cta:
   WhatsApp + the page primary). No contact row is added here — a second
   WhatsApp in the same menu is exactly the duplicate trigger this pass removes.
   ═══════════════════════════════════════════════════════════════════════════ */
@media(max-width:1023px){
  .navdrawer-l1{padding-bottom:calc(20px + env(safe-area-inset-bottom,0px))}

  .navgroup{margin:0 0 20px}
  .navgroup-lab{
    display:block;margin:0 6px 8px;
    font-family:var(--f-head,"Syne","DM Sans",sans-serif);
    font-weight:700;font-size:11px;letter-spacing:.11em;text-transform:uppercase;
    color:var(--ink-2,#6E6E73);
  }
  html[lang="kn"] .navgroup-lab{
    font-family:var(--f-kn,"Noto Sans Kannada",sans-serif);
    letter-spacing:.02em;text-transform:none;font-size:12.5px;line-height:1.5;
  }
  .navgroup-box{
    border-radius:12px;overflow:hidden;
    background:var(--canvas-2,#F5F5F7);
    border:0.5px solid var(--line,rgba(0,0,0,.08));
  }
  /* flat, full-bleed rows separated by a hairline — the grouped-list idiom.
     Each row keeps its own .navrow type and behaviour; only the container and
     the divider change. */
  .navgroup-box .navrow{
    border-radius:0;border:0;background:transparent;margin:0;
    border-bottom:0.5px solid var(--line,rgba(0,0,0,.08));
    min-height:58px;padding:10px 14px;
  }
  .navgroup-box .navrow:last-child{border-bottom:0}
  .navgroup-box .navrow:active{background:rgba(0,0,0,.045)}
  /* the grouped row is a list item, not a headline: one step down from the
     ungrouped 22px so the group label above it can lead */
  .navgroup-box .navrow-lab{font-size:18px}
  html[lang="kn"] .navgroup-box .navrow-lab{font-size:17px;line-height:1.35}

  /* the leading glyph — one 21px stroke icon per row, never an emoji and never
     decoration: it is the at-a-glance identifier for a destination in a list */
  .navrow-ico{
    flex:0 0 auto;width:26px;height:26px;margin-right:2px;
    display:flex;align-items:center;justify-content:center;
    color:var(--ink-2,#6E6E73);
  }
  .navrow-ico svg{width:21px;height:21px;display:block}
  .navrow:active .navrow-ico{color:var(--saffron-ink,#B25A00)}

  /* Ask Bullman keeps its portrait and its own card — it is not a destination
     in the list, it is the way out of not knowing which destination you want */
  .navrow--bull{
    border-radius:12px;background:rgba(255,153,51,.10);
    border:0.5px solid rgba(255,153,51,.34);
    min-height:64px;padding-inline:12px;margin-bottom:4px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   7 · TOUCH TARGETS + KANNADA FIT  (≤767px)
   Nothing here changes a word of copy — only the box the word sits in.
   ═══════════════════════════════════════════════════════════════════════════ */
@media(max-width:767px){
  /* the catalogue top bar shipped 28–41px targets; the row is 60px tall, so
     there is room for 44 without moving anything else */
  .kb-bar{padding-top:env(safe-area-inset-top,0px);height:calc(60px + env(safe-area-inset-top,0px))}
  .kb-bar-logo{display:flex;align-items:center;min-width:44px;min-height:44px}
  .kb-langseg{min-height:44px;padding-inline:14px}
  .langseg{min-height:44px}
  .nav-logo{min-height:44px;display:inline-flex;align-items:center}
  /* drawer + mega links: a destination in a list is a tap target, not a line of
     text — the label keeps its size, the row grows around it */
  .navmega a,.navl2pane a,.navdrawer a{min-height:44px;display:flex;align-items:center}
  .kb-bar .kb-btn-wa{min-height:44px}
  .langseg{min-width:44px}

  /* Kannada sits on a taller line than Latin and its descenders are what get
     clipped first; give every chrome label room rather than shrinking the text */
  html[lang="kn"] .mapp-btn,
  html[lang="kn"] .navrow-lab,
  html[lang="kn"] .navgroup-lab{line-height:1.45}
  .navrow-lab{padding-block:2px}
  /* Breadcrumbs are links a thumb has to hit. Padding would work, but it adds
     ~26px of row height and that pushed the ROI page's payoff figure below the
     fold (verify-render blocker B). So the HIT AREA grows without the LAYOUT
     growing: a transparent pseudo-element centred on the link. */
  .kb-crumb a{position:relative}
  .kb-crumb a::after{content:"";position:absolute;left:0;right:0;top:50%;
    height:44px;transform:translateY(-50%)}
}

/* ═══════════════════════════════════════════════════════════════════════════
   8 · HORIZONTAL COLLECTIONS  (≤767px)
   The catalogue grids become swipeable rails with a peeking next card — the
   native collection idiom — without touching the card markup or the renderer.
   ═══════════════════════════════════════════════════════════════════════════ */
@media(max-width:767px){
  .kb-cards,
  .kb-rail{
    display:flex;gap:12px;
    overflow-x:auto;overflow-y:hidden;
    scroll-snap-type:x mandatory;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:none;
    /* the rail bleeds to the page edge and pads back in, so the first card
       lines up with the copy above it and the last one can still centre */
    margin-inline:calc(var(--kb-gut,20px) * -1);
    padding:2px var(--kb-gut,20px) 6px;
    /* a horizontal rail inside a vertical page: lock the axis so a diagonal
       swipe scrolls the page, never both at once */
    touch-action:pan-x pan-y;
    overscroll-behavior-x:contain;
  }
  .kb-cards::-webkit-scrollbar,
  .kb-rail::-webkit-scrollbar{display:none}
  .kb-cards > *,
  .kb-rail > *{
    flex:0 0 78%;max-width:300px;scroll-snap-align:start;
  }
  .kb-cards > *:last-child,
  .kb-rail > *:last-child{scroll-snap-align:end}
}

/* ═══════════════════════════════════════════════════════════════════════════
   9 · NATIVE POLISH PASS  (2026-08-24)
   Four refinements, all still inside the ≤767px / html.mapp-on gate.
   Written against the classes that actually exist: the brief's #bullman-modal
   is .bmh (+ .bmh-scrim / .bmh-grab), its "6 job tiles" are .bm-need, and
   .mapp-drawer-group is .navgroup-box.
   ═══════════════════════════════════════════════════════════════════════════ */
@media(max-width:767px){

  /* ── 9.1 · the sheet reads as a layer above the page, not a panel on it ──
     bullman-hub.css is injected at runtime and therefore lands later in the
     cascade — these overrides win on specificity, not order. Desktop keeps its
     lighter, unblurred scrim because there the panel is anchored, not modal. */
  html.mapp-on .bmh-scrim{
    background:rgba(0,0,0,.40);
    -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
    transition:opacity .3s var(--mapp-ease),visibility 0s linear .3s;
  }
  html.mapp-on .bmh-scrim.on{transition:opacity .3s var(--mapp-ease),visibility 0s}
  html.mapp-on .bmh{max-height:85vh}
  /* the drag pill: 40×4, fully rounded, and centred on its own line */
  html.mapp-on .bmh-grab{
    width:40px;height:4px;border-radius:12px;
    background:rgba(209,213,219,.8);margin:10px auto 4px;
  }

  /* the six job tiles — a 56px minimum so a thumb never misses, and a real
     press state. Touch has no hover, so :active is the only tactile signal
     available; scale is the one the platform itself uses. */
  html.mapp-on .bm-need,
  html.mapp-on .bmh-mode{
    min-height:56px;
    transition:transform .12s var(--mapp-ease),background-color .16s var(--mapp-ease),
               border-color .16s var(--mapp-ease);
  }
  html.mapp-on .bm-need:active,
  html.mapp-on .bmh-mode:active{transform:scale(.98)}

  /* ── 9.2 · the bar yields to an on-screen in-page CTA ──
     Same weight as the .is-up rule plus one, like every other stand-down. */
  html.mapp-on.mapp-cta-visible .mapp-bar.is-up{
    transform:translateY(102%);pointer-events:none;
  }

  /* ── 9.4 · iOS inset-grouped drawer ── */
  html.mapp-on .navgroup-box{
    border-radius:16px;background:#FFFFFF;border:1px solid #E5E5EA;overflow:hidden;
  }
  /* hairline dividers BETWEEN cells, inset to the label like the platform does
     — a full-width rule reads as a table, an inset one reads as a list */
  html.mapp-on .navgroup-box .navrow{border-bottom:0;position:relative}
  html.mapp-on .navgroup-box .navrow::after{
    content:"";position:absolute;left:52px;right:0;bottom:0;height:1px;
    background:#E5E5EA;
  }
  html.mapp-on .navgroup-box .navrow:last-child::after{display:none}
  html.mapp-on .navgroup-box .navrow:active{background:#F2F2F7}

  /* the Bullman card anchors the foot of the menu: warm, not saffron — it is
     an invitation to ask, not the action on screen (R4 keeps saffron for that) */
  html.mapp-on .navrow--bull{
    background:#FFF8F5;border:1px solid #FFD8CC;border-radius:16px;
  }
  html.mapp-on .navrow--bull:active{background:#FFEFE8}
}

/* ── 9.3 · the savings figure acknowledges the slider ──
   Animation that communicates state (the number just changed), which is the
   one kind R7 permits. Reduced motion gets the value with no movement. */
@keyframes mappPulseGreen{
  0%  {transform:scale(1);   text-shadow:0 0 0 rgba(19,136,8,0)}
  40% {transform:scale(1.055);text-shadow:0 0 14px rgba(19,136,8,.30)}
  100%{transform:scale(1);   text-shadow:0 0 0 rgba(19,136,8,0)}
}
.animate-pulse-green{
  animation:mappPulseGreen .42s var(--mapp-ease);
  transform-origin:left center;
  will-change:transform;
}
@media(prefers-reduced-motion:reduce){
  .animate-pulse-green{animation:none}
  html.mapp-on .bm-need:active,html.mapp-on .bmh-mode:active{transform:none}
}

/* ═══════════════════════════════════════════════════════════════════════════
   10 · NATIVE PASS  (2026-08-30)  — still entirely inside ≤767px / html.mapp-on.
   Four refinements: the dock reads as one machined surface, the Bullman sheet
   tracks the finger 1:1 while dragging, "Talk to Bullman" leads the sheet in
   the established warm-invitation idiom, and catalogue cards answer a touch.
   ═══════════════════════════════════════════════════════════════════════════ */
@media(max-width:767px){
  /* 10.1 · dock material: a top light-edge, like the sheet and the fab already
     carry. Purely inside the bar's own box — no layout, no clearance change. */
  .mapp-bar{box-shadow:inset 0 1px 0 rgba(255,255,255,.55),0 -12px 32px -22px rgba(10,20,16,.25)}

  /* 10.2 · while a finger is on the sheet, the 300ms open-transition must not
     fight the drag. bullman-hub.js adds .bmh-drag for exactly that window. */
  html.mapp-on .bmh.bmh-drag{transition:none!important}
  html.mapp-on .bmh-scrim.bmh-drag{transition:none!important}

  /* 10.3 · Talk to Bullman leads the sheet — the warm-invitation card idiom
     the drawer's .navrow--bull already established. Warm, NOT saffron-filled:
     it is an invitation to talk, not the action on screen (R4). */
  html.mapp-on .bmh-mode[data-bm-act="voice"]{background:#FFF8F5;border-color:#FFD8CC;min-height:64px}
  html.mapp-on .bmh-mode[data-bm-act="voice"] .bmh-ico{background:rgba(255,153,51,.16)}
  html.mapp-on .bmh-mode[data-bm-act="voice"]:active{background:#FFEFE8}

  /* 10.4 · touch has no hover: the catalogue card's press state is the one
     tactile signal available. Same scale the platform uses everywhere else. */
  html.mapp-on a.kb-card:active{transform:scale(.985);box-shadow:none}
}
@media(max-width:767px) and (prefers-reduced-motion:no-preference){
  html.mapp-on a.kb-card{transition:transform .12s var(--mapp-ease)}
  /* the sheet arrives with a hint of spring — motion that states "this surface
     is physical", the one kind R7 permits. Reduced motion keeps the instant
     presentation bullman-hub.css already defines. */
  html.mapp-on .bmh.open{transition:transform .34s cubic-bezier(.32,1.18,.4,1),opacity .01s linear,visibility 0s}
}
@media(prefers-reduced-motion:reduce){
  html.mapp-on a.kb-card:active{transform:none}
}
