/* suite-shell: the "Early version" chip (src/chip.ts) - C-chip, css 64-86 + 93-183 + 193-195
   Generated by scratchpad slice.mjs from nd-suite-fixes.css on 2026-09-05; edit HERE from now on
   (the old file is a stub). Every rule below is scoped by the data-nd-app / data-nd-host stamp the shell
   writes on <html>. */

/* ════════════════════════════════════════════════════════════════════════════════════
   SHARED — "Early version" chip (injected by nd-suite-fixes.js) + trim layer
   ════════════════════════════════════════════════════════════════════════════════════ */

.nd-early-chip {
  position: fixed;
  left: 10px;
  bottom: 10px;
  z-index: 99990;
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(20, 14, 8, 0.92);
  color: #f5efe8;
  font: 600 12px/1.4 Inter, system-ui, sans-serif;
  padding: 6px 8px 6px 12px;
  border: 1px solid rgba(224, 104, 48, 0.55);
  border-radius: 999px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
  max-width: calc(100vw - 20px);
  pointer-events: none;
}
.nd-early-chip b { color: #f0954f; font-weight: 800; }

.nd-early-chip .nd-chip-lines { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.nd-early-chip .nd-chip-door {
  pointer-events: auto;
  color: #ffd7b0;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.nd-early-chip .nd-chip-door:hover { color: #fff; }

/* Explicit resets rather than `all: unset` — that also stripped the focus outline, and
   the button was an ~8x17px target, small enough that the chip was effectively permanent
   on a phone. */
.nd-early-chip button {
  pointer-events: auto;
  display: grid;
  place-items: center;
  min-width: 32px;
  min-height: 32px;
  margin: -6px -4px -6px 0;
  background: none;
  border: 0;
  border-radius: 999px;
  color: #cbbfb3;
  font: inherit;
  font-weight: 800;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}
.nd-early-chip button:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }
.nd-early-chip button:focus-visible { outline: 2px solid #f0954f; outline-offset: 2px; }

/* Chip placement per app — the bottom-left is occupied by real furniture that differs:
   interior's 124px mobile nav and sitemind's 26px #sm-bbar status footer (desktop too). */
[data-nd-app="sitemind"] .nd-early-chip { bottom: 34px; }   /* clear the 26px #sm-bbar */
@media (max-width: 767px) {
  .nd-early-chip {
    font-size: 11px;
    padding: 5px 6px 5px 10px;
    bottom: calc(76px + env(safe-area-inset-bottom, 0px));
  }
  [data-nd-app="interior"] .nd-early-chip {
    bottom: calc(136px + env(safe-area-inset-bottom, 0px));
  }
  /* sitemind: the mobile inspector sheet occupies the bottom min(42vh,390px) band and
     the AI FAB sits at its top-right corner (lifted by fix S4) — go above both. */
  [data-nd-app="sitemind"] .nd-early-chip {
    bottom: calc(min(42vh, 390px) + 106px);
    left: auto;
    right: 12px;
    max-width: calc(100vw - 24px);
  }
}

/* UI-6 (2026-08-16) — arch3d on phones: BOTH fixed chips were sitting ON the workstation
   sheet. Codex QA (375x812, touch): elementFromPoint at the centre of "+ Add Floor Level"
   returned the notice's price link, and the "Back to Plan" chip covered the Cantilever
   Balcony label + the left of its slider. Nothing is hidden; both leave the sheet's band:
   - THE NOTICE rides ABOVE the sheet's top edge while the drawer is open — bottom is the
     same --nd-sheet-h that A5/A6b/A4 read, so it follows every snap and the live drag and
     can never overlap sheet content at any scroll position (it is outside the sheet's box).
     Drawer closed = nothing but canvas underneath, so the 76px rule above stands. It joins
     the snap transition (CODEX FIX 3: every consumer moves together) and the reduced-motion
     reset below. Trade-off, stated: at the "tall" snap the 3D band is only its 140px floor,
     so the 78px notice covers most of it and its top edge rises ~6px into the switcher /
     Back-to-Plan row until dismissed — a dismissible note over an orbit view, versus a
     permanent cover over form controls. pointer-events:none on the body of the chip means
     orbit, the switcher and Back to Plan all still take the tap (measured at tall: every
     one of switcher / back / grab / first tab owns its own centre hit); only the link and
     the x are solid, and neither reaches that row.
   - BACK TO PLAN moves to the TOP-RIGHT of the 3D pane, aligned with the app's own
     "3D Render" switcher row (top-4 = 16px into main, 44px buttons on touch) and grown to
     44px. The bundle puts nothing in that corner on phones (checked: the only right-anchored
     absolutes are the vastu compass at bottom-right and a dropdown under the top bar).
     !important because the JS styles the chip inline; desktop keeps that inline chip. */
@media (width <= 768px) {
  [data-nd-app="arch3d"]:has(.arch3d-workstation-drawer[data-drawer-open="true"]) .nd-early-chip {
    bottom: calc(var(--nd-sheet-h, 55vh) + 10px);
  }
  [data-nd-app="arch3d"]:has(.arch3d-workstation-drawer.nd-sheet-snapping) .nd-early-chip {
    transition: bottom .2s cubic-bezier(.32, .72, 0, 1);
  }
}
@media (prefers-reduced-motion: reduce) {
  [data-nd-app="arch3d"] .nd-early-chip { transition: none !important; }
}
/* suite-shell: trim layer stamps (src/trim.ts) - C-trim, css 87-91 + 1365-1373 (the -phone rule has no live producer; drop it when J9a folds)
   Generated by scratchpad slice.mjs from nd-suite-fixes.css on 2026-09-05; edit HERE from now on
   (the old file is a stub). Every rule below is scoped by the data-nd-app / data-nd-host stamp the shell
   writes on <html>. */


/* Anything the trim layer hides (see nd-suite-fixes.js TRIM). Kept as an attribute rather
   than an inline style so a React re-render that clones the node cannot resurrect it
   half-styled — the observer re-stamps the attribute and this rule does the rest. */
[data-nd-trimmed] { display: none !important; }

/* A18 — PHONE-ONLY TRIM (2026-08-13, his call). Same fail-closed attribute contract as
   the global trim layer, one media query narrower. Phase 6 hid arch3d's other two 2D
   doors ("2D CAD Draftsman", "Split Dual-View") but missed "Full 2D Canvas" inside the
   Structure panel — and that canvas is mouse-only (onMouseDown/onMouseMove, zero touch
   handlers in the bundle), so a phone user who taps it concludes the app is broken. It
   stays fully live on desktop, where it works. */
@media (width <= 768px) {
  [data-nd-trimmed-phone] { display: none !important; }
}
/* suite-shell: the in-flow "Suite" pill (src/pill.ts) - C-pill, css 198-269
   Generated by scratchpad slice.mjs from nd-suite-fixes.css on 2026-09-05; edit HERE from now on
   (the old file is a stub). Every rule below is scoped by the data-nd-app / data-nd-host stamp the shell
   writes on <html>. */

/* ── SUITE-13 (2026-08-16) — history shim furniture, injected by nd-suite-fixes.js ──
   .nd-suite-home  a small "‹ Suite" pill placed INSIDE each app's own top bar (engine,
                   interior, sitemind; arch3d's existing Exit Studio is retargeted instead).
                   In-flow, never fixed — UI-10 is the lesson on floating buttons.
   .nd-leave       the "Leave <app>?" sheet shown when Back is pressed at the first view.
   Both are theme-neutral on purpose: two of the apps are dark, two are light, so the pill
   borrows currentColor for text and paints only a saffron hairline + tint. */
.nd-suite-home {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  height: 28px;
  padding: 0 10px 0 7px;
  margin: 0 8px;
  border: 1px solid rgba(224, 104, 48, 0.6);
  border-radius: 999px;
  background: rgba(224, 104, 48, 0.12);
  color: inherit;
  font: 700 12px/1 Inter, system-ui, sans-serif;
  letter-spacing: 0.01em;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
/* The visible pill is 28px tall because it lives inside 24-30px header rows, but the TAP
   target is 44px: an invisible ::after box grows the hit area without moving anything
   around it (Codex round-1 nit). position:relative on the pill anchors it. */
.nd-suite-home { position: relative; }
.nd-suite-home::after { content: ""; position: absolute; inset: -8px -6px; }
.nd-suite-home:hover { background: rgba(224, 104, 48, 0.22); }
.nd-suite-home:focus-visible { outline: 2px solid #f0954f; outline-offset: 2px; }
.nd-suite-home svg { flex: 0 0 auto; }
/* sitemind's #sm-nav is a dense 30px strip of !important-sized buttons; match its scale. */
[data-nd-app="sitemind"] #sm-nav .nd-suite-home { height: 24px !important; font-size: 11px !important; margin: 0 6px !important; }
[data-nd-app="sitemind"] #sm-nav .nd-suite-home::after { inset: -10px -6px; }   /* 24 + 20 = 44px tap height */
@media (max-width: 767px) {
  .nd-suite-home span { display: none; }         /* icon-only on phones; aria-label carries the name */
  .nd-suite-home { padding: 0 8px; margin: 0 6px; min-width: 32px; justify-content: center; }
}
/* Nirman BBS (history-only host, BBS-5): the app has its own "← NirmanDost" hub link that
   ITS css hides at <=900px. The pill covers exactly that gap and nothing more, so a user
   never sees two ways home. Sits in the actions row before the status dot. */
html[data-nd-host="bbs"] .nd-suite-home { display: none; margin: 0 4px 0 0; }
/* Nirman Plan: #top is a dense nowrap bar; at phone widths the pill must not push 3D Model off-screen */
@media (max-width: 480px) { html[data-nd-host="plan"] .nd-suite-home { margin: 0 2px 0 3px; padding: 0 6px; min-width: 28px; } }
@media (max-width: 900px) {
  html[data-nd-host="bbs"] .nd-suite-home { display: inline-flex; }
}
/* suite-shell: the leave sheet (src/leaveSheet.ts) - C-leave, css 271-313
   Generated by scratchpad slice.mjs from nd-suite-fixes.css on 2026-09-05; edit HERE from now on
   (the old file is a stub). Every rule below is scoped by the data-nd-app / data-nd-host stamp the shell
   writes on <html>. */

.nd-leave {
  position: fixed;
  inset: 0;
  z-index: 99995;
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgba(20, 14, 8, 0.55);
  font-family: Inter, system-ui, sans-serif;
}
.nd-leave[hidden] { display: none; }
.nd-leave-card {
  width: min(420px, 100%);
  background: #fff;
  color: #1a1410;
  border-radius: 14px;
  padding: 20px 20px 16px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.35);
}
.nd-leave-card h2 { margin: 0 0 6px; font: 800 18px/1.25 Inter, system-ui, sans-serif; color: #1a1410; }
.nd-leave-card p  { margin: 0 0 16px; font: 400 14px/1.5 Inter, system-ui, sans-serif; color: #5a4d42; }
.nd-leave-row { display: flex; flex-wrap: wrap; gap: 8px; }
.nd-leave-btn {
  flex: 1 1 auto;
  min-height: 44px;
  padding: 0 14px;
  border-radius: 10px;
  border: 1px solid #d9cfc4;
  background: #fff;
  color: #1a1410;
  font: 700 14px/1 Inter, system-ui, sans-serif;
  cursor: pointer;
}
.nd-leave-btn:hover { background: #f7f2ec; }
.nd-leave-btn:focus-visible { outline: 2px solid #e06830; outline-offset: 2px; }
.nd-leave-primary { background: #e06830; border-color: #e06830; color: #fff; }
.nd-leave-primary:hover { background: #c95a26; }
.nd-leave-quiet { border-color: transparent; color: #7a6a5c; }
@media (max-width: 767px) {
  .nd-leave { place-items: end center; padding: 0; }
  .nd-leave-card { width: 100%; border-radius: 16px 16px 0 0; padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
  .nd-leave-row { flex-direction: column; }
}
/* suite-shell: TRANSITIONAL sitemind rules S1-S15, byte-for-byte - delete when the sitemind fold lands (override-map 3.3) - css 500-714
   Generated by scratchpad slice.mjs from nd-suite-fixes.css on 2026-09-05; edit HERE from now on
   (the old file is a stub). Every rule below is scoped by the data-nd-app / data-nd-host stamp the shell
   writes on <html>. */

/* ════════════════════════════════════════════════════════════════════════════════════
   SITEMIND (TerraPlan) — 16 fixes, backlog S1-S16. The phone flow that matters
   commercially (setup → generate → stats → share) all lives in #sm-rpanel and works
   once these land. NOT gated. The high-risk sidebar restore (S17) is NOT shipped —
   it re-plumbs the app grid and must be eyeballed on a real device first.
   ════════════════════════════════════════════════════════════════════════════════════ */

/* S1 — P0. Kill the full-screen glass box: the app's mobile rule sets bottom:12px on the
   always-mounted #studio-score but never clears the base top:140px, so the fixed panel
   stretches over the entire canvas (y=140 to y=903 on a 915px phone, z-index 55). */
@media (width <= 768px) {
  [data-nd-app="sitemind"] #studio-score { bottom: auto !important; }
}

/* S2 — P0. Journey card steps measured 1.06:1 (founder BUG-5, quantified): inactive
   steps are --muted text on rgba(255,255,255,.58) chips over the dark modal. All
   values are INLINE styles, so !important is mandatory. Active step + current app
   keep their blue identity, just legible (#2563eb on near-black was 3.4:1 → 7.2:1). */
[data-nd-app="sitemind"] [data-testid^="sitemind-suite-step-"]:not([data-testid="sitemind-suite-step-site-plot"]),
[data-nd-app="sitemind"] [data-testid^="sitemind-suite-open-"]:not([data-testid="sitemind-suite-open-sitemind"]) {
  background: rgba(255, 255, 255, .07) !important;
  color: #d5deea !important;
  border: 1px solid rgba(255, 255, 255, .14) !important;
}
[data-nd-app="sitemind"] [data-testid^="sitemind-suite-step-"]:not([data-testid="sitemind-suite-step-site-plot"]) span {
  background: rgba(255, 255, 255, .12) !important;
  color: #eef3f9 !important;
}
[data-nd-app="sitemind"] [data-testid="sitemind-suite-step-site-plot"],
[data-nd-app="sitemind"] [data-testid="sitemind-suite-open-sitemind"],
[data-nd-app="sitemind"] [data-testid="sitemind-suite-journey"] .proj-meta {
  color: #7aa7ff !important;
}
[data-nd-app="sitemind"] [data-testid="sitemind-suite-step-site-plot"],
[data-nd-app="sitemind"] [data-testid="sitemind-suite-open-sitemind"] {
  background: rgba(37, 99, 235, .22) !important;
  border-color: rgba(122, 167, 255, .45) !important;
}

/* S3 — P0. --muted2 (#4a5a72) is 2.74:1 on the app background and colours 25 pieces of
   secondary text a first-time user must read (placeholders, authority-rule labels,
   honesty notes). #6b7d99 = 4.84:1, still visibly dimmer than --muted. */
html[data-nd-app="sitemind"] { --muted2: #6b7d99 !important; }

/* S4 — P0. Lift the canvas controls out from under the mobile bottom sheet: at <=768px
   #sm-rpanel is a fixed sheet up to min(42vh,390px) tall sitting 38px off the bottom —
   zoom controls, the whole Stage-2 3D toolbar and the AI FAB were inside/behind it. */
@media (width <= 768px) {
  [data-nd-app="sitemind"] .vctrls            { bottom: calc(min(42vh, 390px) + 22px) !important; }
  [data-nd-app="sitemind"] #toolbar-3d-bottom { bottom: calc(min(42vh, 390px) + 22px) !important; }
  [data-nd-app="sitemind"] #ai-fab            { bottom: calc(min(42vh, 390px) + 46px) !important; right: 12px !important; }
  [data-nd-app="sitemind"] #ai-panel          { bottom: calc(min(42vh, 390px) + 106px) !important; max-height: 34vh !important; }
}

/* S5 — P1. The bottom 3D toolbar is ~790px of nowrap flex centred in a 412px canvas —
   ~190px sheared off each side. Wrap it instead. */
@media (width <= 768px) {
  [data-nd-app="sitemind"] #toolbar-3d-bottom {
    left: 8px !important;
    right: 8px !important;
    transform: none !important;
    flex-wrap: wrap !important;
    justify-content: center !important;
    row-gap: 6px !important;
    padding: 7px 9px !important;
  }
  [data-nd-app="sitemind"] #t3-sun,
  [data-nd-app="sitemind"] #t3-sect { width: 84px !important; }
  [data-nd-app="sitemind"] #toolbar-3d-bottom .t3sep { display: none !important; }
}

/* S6 — P1. The setup modal (the first and only screen at load) is a fixed 2-col grid on
   a 380px phone modal: the Land Area number input ended up ~33px of content at a forced
   16px font. Single column below 640px; dvh so the footer clears the Android toolbar
   (body is overflow:hidden — an off-screen footer cannot be scrolled to). */
@media (width <= 640px) {
  [data-nd-app="sitemind"] #modal-setup .setup-grid  { grid-template-columns: 1fr !important; }
  [data-nd-app="sitemind"] #modal-setup .rules-grid  { grid-template-columns: 1fr 1fr !important; }
  [data-nd-app="sitemind"] #modal-setup .setup-body  { padding: 14px 16px !important; }
  [data-nd-app="sitemind"] #modal-setup .setup-hdr   { padding: 14px 16px 12px !important; }
  [data-nd-app="sitemind"] #modal-setup .setup-footer{ padding: 12px 16px !important; }
  [data-nd-app="sitemind"] .setup-modal,
  [data-nd-app="sitemind"] .projlist-modal,
  [data-nd-app="sitemind"] .share-modal {
    max-height: calc(100dvh - 24px) !important;
    margin: 12px !important;
  }
}
html[data-nd-app="sitemind"] body { height: 100dvh !important; }

/* S7 — P1. .compass-wrap is a 3x3 entrance pad; the setup modal reuses the same class
   for a 64px dial + a sentence, cramming them into two ~41px cells (desktop bug too).
   Un-grid it there only — the Stage-2 entrance pad lives in #rp-layout, untouched. */
[data-nd-app="sitemind"] #modal-setup .compass-wrap {
  display: flex !important;
  grid-template-columns: none !important;
  grid-template-rows: none !important;
  width: auto !important;
  align-items: center !important;
  gap: 14px !important;
  margin: 6px 0 !important;
}

/* S8 — P1. Pro Toolkit grid has a hard 520px minimum inside a ~360px panel. */
@media (width <= 900px) {
  [data-nd-app="sitemind"] .pro-grid { grid-template-columns: minmax(0, 1fr) !important; }
  [data-nd-app="sitemind"] .pro-card { min-height: 0 !important; }
}

/* S9 — P1. Plot schedule table crushes 6 columns into ~360px while the coarse-pointer
   rule forces its 9px inputs to 16px. The wrapper already scrolls — give the table a
   real minimum so it actually does. */
@media (width <= 768px) {
  [data-nd-app="sitemind"] #sm-rpanel .sched-table { min-width: 440px !important; }
  [data-nd-app="sitemind"] #sm-rpanel .sched-wrap {
    -webkit-overflow-scrolling: touch !important;
    overscroll-behavior-x: contain !important;
  }
}

/* S10 — P2. Restore the stage names on phones: the mobile block hides them, leaving
   four bare numbered circles (three at 35% opacity) as the app's primary navigation.
   Guarded to >=360px so it cannot clip on a 320px device. */
@media (width <= 768px) and (width >= 360px) {
  [data-nd-app="sitemind"] #stage-bar .stg-label {
    display: block !important;
    font-size: 9px !important;
    letter-spacing: .2px !important;
  }
  [data-nd-app="sitemind"] #stage-bar .stg        { padding: 0 4px !important; gap: 5px !important; }
  [data-nd-app="sitemind"] #stage-bar .stg.locked { opacity: .55 !important; }
}

/* S11 — P2. REPLACES the old .lang-scroll bar (which added 13px of padding into a 30px
   clipped row and whose width:max-content was inert against flex-basis:0). The real
   defect: .lb is a <button>, so the coarse-pointer 44px floor shears every pill inside
   the fixed 30px #lang-bar track. Opt the pills out instead. */
@media (pointer: coarse) {
  [data-nd-app="sitemind"] #lang-bar .lb {
    min-width: 0 !important;
    min-height: 28px !important;
    padding: 2px 12px !important;
  }
}
[data-nd-app="sitemind"] #lang-bar { padding-left: 10px !important; padding-right: 10px !important; }

/* S12 — P2. Nav action buttons are hard-set 32x30px below 1500px (class specificity
   beats the coarse 44px floor), and the nav scrolls horizontally with every scrollbar
   affordance suppressed — twelve buttons hide off-screen with zero hint. */
@media (pointer: coarse) {
  [data-nd-app="sitemind"] .nav-compact-action {
    width: 40px !important;
    min-width: 40px !important;
    min-height: 40px !important;
  }
  [data-nd-app="sitemind"] .nav-compact-action svg { width: 16px !important; height: 16px !important; }
  [data-nd-app="sitemind"] #sm-nav {
    scrollbar-width: thin !important;
    scrollbar-color: rgba(197, 160, 89, .55) transparent !important;
  }
  [data-nd-app="sitemind"] #sm-nav::-webkit-scrollbar { display: block !important; height: 3px !important; }
  [data-nd-app="sitemind"] #sm-nav::-webkit-scrollbar-thumb {
    background: rgba(197, 160, 89, .55) !important;
    border-radius: 2px !important;
  }
}

/* S13 — P2. The 25 amenity zone buttons are 8px type inflated to 44px-wide empty boxes
   by the coarse rule — big buttons, unreadable labels. */
@media (width <= 768px) {
  [data-nd-app="sitemind"] #sm-rpanel .am-zone-btn {
    font-size: 10px !important;
    min-width: 0 !important;
    min-height: 32px !important;
    padding: 4px 8px !important;
  }
  [data-nd-app="sitemind"] #sm-rpanel .am-zone-lbl { font-size: 10px !important; min-width: 34px !important; }
}

/* S14 — P3. Emoji cleanup, the four glyphs CSS can safely reach (emoji-only elements).
   DO NOT relabel #btn-edit / #btn-houses / #btn-theme — JS rewrites their textContent
   per state; a frozen ::before label would lie about the app's state. */
[data-nd-app="sitemind"] #lang-bar span { font-size: 0 !important; }
[data-nd-app="sitemind"] #lang-bar span::after {
  content: "A/\0905";
  font-size: 10px;
  font-weight: 700;
  color: var(--muted);
  letter-spacing: .5px;
}
[data-nd-app="sitemind"] .survey-upload-icon { display: none !important; }
[data-nd-app="sitemind"] .rc-center { font-size: 0 !important; }
[data-nd-app="sitemind"] .rc-center::after {
  content: "PLOT";
  font-size: 9px;
  font-weight: 700;
  letter-spacing: .5px;
  color: var(--muted);
}
[data-nd-app="sitemind"] #btn-annotations { font-size: 0 !important; }
[data-nd-app="sitemind"] #btn-annotations::before {
  content: "Labels";
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
}

/* S15 — P3. First-run tip card (max-width:370px, centred, fixed) clips on <394px
   viewports; body overflow-x:hidden means it cuts, not scrolls. Pure clamp. */
[data-nd-app="sitemind"] .sm-tip-card   { max-width: min(370px, calc(100vw - 28px)) !important; }
[data-nd-app="sitemind"] .s1-draft-help { max-width: min(310px, calc(100vw - 40px)) !important; }

/* S16 is the chip reposition — shipped in the SHARED chip section above. */

/* suite-shell: TRANSITIONAL interior rules I1-I11, byte-for-byte - delete when the interior fold lands (override-map 3.4) - css 716-848
   Generated by scratchpad slice.mjs from nd-suite-fixes.css on 2026-09-05; edit HERE from now on
   (the old file is a stub). Every rule below is scoped by the data-nd-app / data-nd-host stamp the shell
   writes on <html>. */

/* ════════════════════════════════════════════════════════════════════════════════════
   INTERIOR (Interio) — 10 fixes, backlog I1-I11 (I4 = chip, in the shared section).
   The only app actually built mobile-first; its phone problems are four provable
   defects, not an architecture mismatch. NOT gated.
   ════════════════════════════════════════════════════════════════════════════════════ */

/* I1 — BLOCKER. The Designer's tool FAB + sheet anchor at bottom-[82px], but the
   mobile nav is 2 rows x 52px + gap + padding = 124px tall at a higher z — 42 of the
   FAB's 44px sit behind it, so the flagship view has no reachable tools on a phone.
   The 82px anchor assumed a single-row 68px nav. bottom-[82px] occurs exactly twice
   in the whole bundle — the FAB and the sheet, both the elements being fixed. */
@media (max-width: 767px) {
  [data-nd-app="interior"] .bottom-\[82px\] {
    bottom: calc(136px + env(safe-area-inset-bottom, 0px)) !important;
  }
}

/* I2 — 38 Tailwind opacity utilities were never compiled (steps outside the default
   scale), so the mobile tool sheet, tablet sheet, Projects popover, desktop inspector,
   camera toolbar and HUD all declare a background that resolves to NOTHING and paint
   as transparent glass over live 3D. Defining them can only affect elements that are
   already rendering wrong. (#fbf7ef occurs 0 times in the compiled CSS — verified.) */
[data-nd-app="interior"] .bg-\[\#fbf7ef\]\/94 { background-color: rgba(251, 247, 239, .94); }
[data-nd-app="interior"] .bg-\[\#fbf7ef\]\/96 { background-color: rgba(251, 247, 239, .96); }
[data-nd-app="interior"] .bg-\[\#fbf7ef\]\/72 { background-color: rgba(251, 247, 239, .72); }
[data-nd-app="interior"] .bg-white\/28 { background-color: rgba(255, 255, 255, .28); }
[data-nd-app="interior"] .bg-white\/48 { background-color: rgba(255, 255, 255, .48); }
[data-nd-app="interior"] .bg-white\/52 { background-color: rgba(255, 255, 255, .52); }
[data-nd-app="interior"] .bg-white\/58 { background-color: rgba(255, 255, 255, .58); }
[data-nd-app="interior"] .bg-white\/62 { background-color: rgba(255, 255, 255, .62); }
[data-nd-app="interior"] .bg-white\/66 { background-color: rgba(255, 255, 255, .66); }
[data-nd-app="interior"] .bg-white\/68 { background-color: rgba(255, 255, 255, .68); }
[data-nd-app="interior"] .bg-white\/72 { background-color: rgba(255, 255, 255, .72); }
[data-nd-app="interior"] .bg-white\/78 { background-color: rgba(255, 255, 255, .78); }
[data-nd-app="interior"] .bg-white\/82 { background-color: rgba(255, 255, 255, .82); }
[data-nd-app="interior"] .bg-white\/84 { background-color: rgba(255, 255, 255, .84); }
[data-nd-app="interior"] .bg-white\/86 { background-color: rgba(255, 255, 255, .86); }
[data-nd-app="interior"] .bg-studio-coal\/6  { background-color: rgba(39, 35, 30, .06); }
[data-nd-app="interior"] .bg-studio-coal\/8  { background-color: rgba(39, 35, 30, .08); }
[data-nd-app="interior"] .bg-studio-moss\/8  { background-color: rgba(73, 108, 85, .08); }
[data-nd-app="interior"] .bg-studio-moss\/12 { background-color: rgba(73, 108, 85, .12); }
[data-nd-app="interior"] .bg-studio-moss\/16 { background-color: rgba(73, 108, 85, .16); }
[data-nd-app="interior"] .bg-studio-sea\/8   { background-color: rgba(65, 111, 134, .08); }
[data-nd-app="interior"] .bg-studio-sea\/12  { background-color: rgba(65, 111, 134, .12); }
[data-nd-app="interior"] .bg-studio-brass\/6  { background-color: rgba(185, 139, 69, .06); }
[data-nd-app="interior"] .bg-studio-brass\/8  { background-color: rgba(185, 139, 69, .08); }
[data-nd-app="interior"] .bg-studio-brass\/12 { background-color: rgba(185, 139, 69, .12); }
[data-nd-app="interior"] .bg-studio-clay\/12  { background-color: rgba(163, 93, 67, .12); }
[data-nd-app="interior"] .hover\:bg-white\/72:hover { background-color: rgba(255, 255, 255, .72); }
[data-nd-app="interior"] .disabled\:hover\:bg-white\/66:disabled:hover { background-color: rgba(255, 255, 255, .66); }
/* DO NOT define text-studio-coal/35, /42, /62 or text-current/55 — they currently
   inherit a DARKER parent colour; defining them would make that text LESS legible. */

/* I3 — the blanket coarse-pointer 44px floor deforms the 2D floor plan: furniture
   rectangles are <button>s whose size IS the data, force-grown 2-6x inside a 176px
   plan. The app's own .nd-touch-exempt opt-out is applied to zero elements. Exempt
   exactly the two matching button kinds (plan item + light marker). */
@media (pointer: coarse) {
  [data-nd-app="interior"] button.absolute.touch-none {
    min-width: 0;
    min-height: 0;
  }
}

/* I5 — systemic low contrast: the studio-coal grey ramp fails AA from /40 through /60
   (2.35-4.07:1) on 217 elements of 9-12px type — the founder's "reads as blank /
   looks disabled" complaint, quantified. Monotone remap: every step only gets darker,
   ordering preserved, zero layout risk. /65 and /70 move only to keep the ramp ordered. */
[data-nd-app="interior"] .text-studio-coal\/40 { color: rgba(39, 35, 30, .68); }
[data-nd-app="interior"] .text-studio-coal\/45 { color: rgba(39, 35, 30, .70); }
[data-nd-app="interior"] .text-studio-coal\/50 { color: rgba(39, 35, 30, .72); }
[data-nd-app="interior"] .text-studio-coal\/55 { color: rgba(39, 35, 30, .74); }
[data-nd-app="interior"] .text-studio-coal\/60 { color: rgba(39, 35, 30, .76); }
[data-nd-app="interior"] .text-studio-coal\/65 { color: rgba(39, 35, 30, .78); }
[data-nd-app="interior"] .text-studio-coal\/70 { color: rgba(39, 35, 30, .82); }
[data-nd-app="interior"] .panel-heading { color: rgba(39, 35, 30, .72); }

/* I6 — the decorative "3D Room Designer" HUD label collides with the camera-preset
   toolbar on phones (it duplicates the topbar breadcrumb, and its background class is
   one of the dead ones). left-4 occurs exactly once in the bundle. */
@media (max-width: 640px) {
  [data-nd-app="interior"] .pointer-events-none.absolute.left-4.top-4 { display: none; }
}

/* I7 — LANDED 2026-08-13 (was staged). The gate was "114 elements, some in fixed-height
   boxes that can introduce new wrapping". Measured on production at 375x812 with touch
   emulation, sweeping all eight bottom-nav views: only EIGHT of those elements actually
   render on a phone, and they are all the bottom-nav labels themselves. 114 is a count of
   class TOKENS in the bundle; the panels that carry the rest do not mount below 768px.
   Before/after on the eight: 10px -> 11.5px, widest label ("Dashboard") 49px -> 57px in
   an 87px item, item height unchanged at 52px, nav-bar and document horizontal overflow
   both 0. 64 measurements across the eight views produced zero new clipping.
   The 9px and 10.5px rules match nothing on a phone today; kept because they cost nothing
   and cover panels that mount in states this sweep did not reach. */
@media (max-width: 767px) {
  [data-nd-app="interior"] .text-\[9px\]     { font-size: 11px !important; letter-spacing: .04em !important; }
  [data-nd-app="interior"] .text-\[10px\]    { font-size: 11.5px !important; }
  [data-nd-app="interior"] .text-\[10\.5px\] { font-size: 11.5px !important; }
}

/* I8 — the 12px corner resize handles on the floor plan are <span>s, so they escape
   the app's 44px floor entirely — resizing furniture is impossible by finger. Grow
   the hit area without changing the drawn dot. */
@media (pointer: coarse) {
  [data-nd-app="interior"] [data-floor-plan] span.h-3.w-3 { pointer-events: auto; }
  [data-nd-app="interior"] [data-floor-plan] span.h-3.w-3::after {
    content: "";
    position: absolute;
    inset: -10px;
  }
}

/* I9 — the toast anchors 96px up (bottom-24), inside the 124px nav, at a higher z —
   it covers two or three nav buttons for its 2.6s life. The author's own md:bottom-6
   variant shows 96px was only ever meant for the phone case it breaks. */
@media (max-width: 767px) {
  [data-nd-app="interior"] .fixed.bottom-24.right-4.z-50 {
    bottom: calc(136px + env(safe-area-inset-bottom, 0px));
  }
}

/* I10 — horizontal scrollers (980px BOQ table, camera toolbar, tool tab strip) hijack
   the Android back-swipe. The app compiled .nd-scroll-x with exactly this intent and
   applied it to zero elements — wire it onto the class the app actually uses. */
[data-nd-app="interior"] .overflow-x-auto {
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

/* I11 — desktop inspector: pl-4.5 never compiled (not a default Tailwind step), so the
   12px resize strip overlaps the first 2px of every control and steals the pointer. */
[data-nd-app="interior"] .interior-inspector-panel { padding-left: 1.125rem; }

/* suite-shell: TRANSITIONAL arch3d rules A1-A18 + C-A-back, byte-for-byte - delete when the arch3d fold lands (override-map 3.2) - css 850-1363 + 184-191 + 1374-1413
   Generated by scratchpad slice.mjs from nd-suite-fixes.css on 2026-09-05; edit HERE from now on
   (the old file is a stub). Every rule below is scoped by the data-nd-app / data-nd-host stamp the shell
   writes on <html>. */

/* ════════════════════════════════════════════════════════════════════════════════════
   ARCH3D (Elevate Pro) — waves 1+2 of backlog A1-A14. Root cause: the stylesheet is a
   hand-maintained utility subset that drifted from the JSX — 127 of 338 class tokens
   have NO rule, so panels lose padding, grids lose their template, toasts lose their
   background. These re-supply what the build should have emitted. NOT gated (the 2D
   CAD pane genuinely needs a mouse — that ceiling is source-side, not layout).
   ════════════════════════════════════════════════════════════════════════════════════ */

/* A1 — theme-aware translucent surfaces. The app defines only /40 /60 /80 /90, hardcoded
   DARK, so light theme keeps navy panels under dark text (journey steps measured 1.02:1);
   /30 /45 /50 /55 /65 /70 /95 have no rule at all (transparent panels). The .light class
   lives on a wrapper div inside the app, hence the descendant form. */
[data-nd-app="arch3d"] .bg-obsidian-surface\/30 { background-color: rgba(20, 25, 35, .30); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/40 { background-color: rgba(20, 25, 35, .40); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/45 { background-color: rgba(20, 25, 35, .45); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/50 { background-color: rgba(20, 25, 35, .50); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/55 { background-color: rgba(20, 25, 35, .55); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/60 { background-color: rgba(20, 25, 35, .60); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/65 { background-color: rgba(20, 25, 35, .65); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/70 { background-color: rgba(20, 25, 35, .70); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/80 { background-color: rgba(20, 25, 35, .80); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/90 { background-color: rgba(20, 25, 35, .90); }
[data-nd-app="arch3d"] .bg-obsidian-surface\/95 { background-color: rgba(20, 25, 35, .95); }
[data-nd-app="arch3d"] .bg-obsidian-base\/70   { background-color: rgba(12, 15, 23, .70); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/30 { background-color: rgba(245, 243, 239, .55); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/40 { background-color: rgba(245, 243, 239, .65); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/45 { background-color: rgba(245, 243, 239, .70); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/50 { background-color: rgba(245, 243, 239, .75); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/55 { background-color: rgba(245, 243, 239, .80); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/60 { background-color: rgba(245, 243, 239, .85); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/65 { background-color: rgba(245, 243, 239, .88); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/70 { background-color: rgba(245, 243, 239, .90); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/80 { background-color: rgba(245, 243, 239, .94); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/90 { background-color: rgba(245, 243, 239, .97); }
[data-nd-app="arch3d"] .light .bg-obsidian-surface\/95 { background-color: rgba(245, 243, 239, .99); }
[data-nd-app="arch3d"] .light .bg-obsidian-base\/70    { background-color: rgba(252, 251, 249, .90); }

/* A2 — --text-muted has exactly one consumer (.text-text-muted, 99 uses), so the token
   IS the class. Dark #64748b was 3.70:1; light #78716c composited to 1.02:1 on the
   (previously always-dark) surfaces. With A1's theme-aware surfaces in place these
   two values clear AA in both themes.

   NOTE: this REPLACES v4's journey-card :has() contrast block. That block forced
   #94a3b8 light-grey text in BOTH themes because the card surface stayed dark in both
   — which was true only while bg-obsidian-surface/65 was undefined. Now that A1 gives
   light theme light surfaces, forced light-grey text would be the new contrast bug. */
html[data-nd-app="arch3d"] { --text-muted: #94a3b8; }
[data-nd-app="arch3d"] .light { --text-muted: #57534e; }

/* A3 — un-stack the Nirman Suite journey rows. grid-cols-[20px_minmax(0,1fr)],
   min-h-[30px], place-items-center and font-black all have no rule, so the number
   badge stacked ABOVE the label in a single-column grid — the "blank washed rows"
   screenshot. The saffron tints on the card are also all undefined. */
[data-nd-app="arch3d"] .grid-cols-\[20px_minmax\(0\2c 1fr\)\] {
  grid-template-columns: 20px minmax(0, 1fr);
}
[data-nd-app="arch3d"] [data-testid="arch3d-suite-journey"] .grid.min-h-\[30px\] {
  grid-template-columns: 20px minmax(0, 1fr);
  min-height: 30px;
  align-items: center;
}
[data-nd-app="arch3d"] [data-testid="arch3d-suite-journey"] .grid.min-h-\[30px\] > span:first-child {
  display: grid;
  place-items: center;
  font-weight: 900;
}
[data-nd-app="arch3d"] [data-testid="arch3d-suite-journey"] > .flex.items-start.justify-between {
  flex-wrap: wrap;
  row-gap: 10px;
}
[data-nd-app="arch3d"] [data-testid="arch3d-suite-next-action"] { flex-shrink: 0; white-space: nowrap; }
[data-nd-app="arch3d"] [data-testid="arch3d-suite-journey"] {
  background-color: rgba(212, 80, 10, .10);
  border-color: rgba(212, 80, 10, .25);
}

/* A4 — the floating plan overlay: no align-content means its wrapped second flex line
   (the floor-chip card) stretches to mid-screen, landing exactly on the bottom sheet's
   tab strip; and it shares the top-4/left-4 origin with the 2D/3D/Split switcher,
   whose .z-40 class is undefined (z-auto), so the z-50 overlay covered it at EVERY
   width, desktop included. top:64px clears the strip everywhere; 76px on touch where
   the strip's buttons are forced to 44px. */
[data-nd-app="arch3d"] div[class~="absolute"][class~="top-4"][class~="left-4"][class~="right-4"][class~="bottom-4"][class~="z-50"] {
  align-content: flex-start;
  align-items: flex-start;
  row-gap: 10px;
  top: 64px;
  overscroll-behavior: contain;
}
@media (pointer: coarse) {
  [data-nd-app="arch3d"] div[class~="absolute"][class~="top-4"][class~="left-4"][class~="right-4"][class~="bottom-4"][class~="z-50"] {
    top: 76px;
  }
}
@media (max-width: 768px) {
  [data-nd-app="arch3d"] div[class~="absolute"][class~="top-4"][class~="left-4"][class~="right-4"][class~="bottom-4"][class~="z-50"] {
    left: 8px;
    right: 8px;
    bottom: 40px;            /* clears the 32px MODE/Cursor status bar */
    gap: 8px;
    justify-content: flex-start;
  }
  /* when the workstation bottom sheet is open, the overlay must not scroll under it.
     55vh, not 62dvh — the app pins the sheet at `height:55vh!important` (see A6b). With
     62dvh this expression exceeded the pane and collapsed the overlay to height 0, and
     because the overlay carries the app's own `overflow-y-auto`, that CLIPPED the whole
     tool palette away: 9 controls (6 tools, snap, Reset Grid, both floor chips) present
     in the DOM, invisible, and losing every hit test to the canvas beneath. Measured
     2026-08-13 on production at 375x812. */
  [data-nd-app="arch3d"]:has(.arch3d-workstation-drawer[data-drawer-open="true"]) div[class~="absolute"][class~="top-4"][class~="left-4"][class~="right-4"][class~="bottom-4"][class~="z-50"] {
    bottom: calc(var(--nd-sheet-h, 55vh) + 12px);
  }
}
/* restore the switcher's intended layer + keep all three buttons reachable under 380px */
[data-nd-app="arch3d"] .z-40 { z-index: 40; }
[data-nd-app="arch3d"] main > .absolute.top-4.left-4.z-40 {
  max-width: calc(100vw - 32px);
  overflow-x: auto;
  flex-wrap: nowrap;
  scrollbar-width: none;
}
[data-nd-app="arch3d"] main > .absolute.top-4.left-4.z-40::-webkit-scrollbar { display: none; }
[data-nd-app="arch3d"] main > .absolute.top-4.left-4.z-40 > button { flex: 0 0 auto; }

/* A5 — the mobile bottom-sheet drawer was 60% transparent over the live canvas and sat
   at z:40 UNDER the z:50 plan overlay — both founder screenshots ("panels overlap",
   "status readout bleeds through the sliders") are this one bug. Opaque, on top.

   CORRECTION 2026-08-13: the `height` below never applied. The app's own mobile block
   pins `height:55vh!important` on this exact selector, and !important beat us. Measured
   446.6px in an 812px viewport = 55vh, not 62dvh.

   DRAGGABLE 2026-08-13 (his call, over a fixed 45vh): the height is the single variable
   --nd-sheet-h, and A4's palette offset + A6b's 3D-viewport height are both expressed
   against the SAME variable, so the sheet cannot move without the viewport above it
   moving by exactly the same amount. !important is now required (it was not before)
   because we have to outrank the app's own `height:55vh!important`.

   CODEX FIX 1 (2026-08-13, must-fix): --nd-sheet-h is no longer written directly. The
   first version had JS write a raw `NNvh` here, and the arithmetic was wrong in a way
   portrait-only testing could never show. The SHEET is a fraction of the VIEWPORT, but
   the 3D pane lives under a topbar, so visible-3D = 100vh - topbar - sheet. At the 80vh
   snap that is `20vh - 56px`: 19px in a 667x375 landscape phone (which is still inside
   the app's own <=768px sheet breakpoint, so it is reachable), and 0 below a ~280px
   viewport height. The "visible-3D can never be 0" claim was false outside portrait.

   The fix is a derived variable with the clamp expressed in CSS, so it re-evaluates on
   every rotation and resize with no JS involved:
     --nd-sheet-raw   what JS asks for (a snap in vh, or live px while dragging)
     --nd-pane-h      the real height of the pane the sheet is stealing from
     --nd-sheet-h     the raw value clamped into what the pane can actually give
   FLOOR, stated once: the 3D viewport never gets less than min(140px, 35% of the pane),
   and the sheet never gets less than 96px (44px grab handle + ~50px tab strip). When the
   pane is too small to honour both, clamp()'s own min-wins rule keeps the sheet floor —
   the sheet stays operable and the viewport takes the loss, never the other way round.
   --nd-topbar is measured and written by the JS (fallback 56px) so nothing depends on a
   hardcoded chrome height. */
[data-nd-app="arch3d"] { --nd-sheet-raw: 55vh; }
@media (width <= 768px) {
  [data-nd-app="arch3d"] {
    --nd-pane-h: calc(100dvh - var(--nd-topbar, 56px));
    --nd-sheet-h: clamp(
      96px,
      var(--nd-sheet-raw, 55vh),
      max(96px, calc(var(--nd-pane-h) - min(140px, var(--nd-pane-h) * 0.35)))
    );
  }
}
@media (width <= 768px) {
  [data-nd-app="arch3d"] .arch3d-workstation-drawer[data-drawer-open="true"] {
    background-color: var(--obsidian-base);
    z-index: 70;
    height: var(--nd-sheet-h, 55vh) !important;
    box-shadow: 0 -10px 32px rgba(0, 0, 0, .55);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  [data-nd-app="arch3d"] .light .arch3d-workstation-drawer[data-drawer-open="true"] {
    box-shadow: 0 -10px 32px rgba(28, 25, 23, .18);
  }
}

/* A6 — REWRITTEN 2026-08-13 (the mobile wave). ONE PANE AT A TIME.

   What v5 shipped: `flex-direction: column` at <=768px, so Split mode's two 206px panes
   stacked instead of squeezing. Correct for Split — and a disaster for every other mode,
   which is what the app actually boots into. The app renders BOTH panes always and sizes
   them by inline width: 2d -> 100%/0%, split -> 50%/50%, 3d -> 0%/100% (verified in
   index-CafwQ-dF.js). In a COLUMN flex container a width:0% pane is still a full-height
   flex item, so the collapsed pane ate half the viewport.

   MEASURED ON PRODUCTION, 375x812 with touch emulation, default boot (3D Render):
     phantom 2D pane  [0, 56, 1, 378]   <- 378px of dead white, 46% of the screen
     3D pane          [0, 434, 375, 378]
     workstation sheet[0, 365, 375, 447]
     => VISIBLE 3D HEIGHT: 0px. With the sheet hidden it was worse — the 3D pane
        started at y=800 in an 812px viewport, i.e. a 12px sliver. That is the
        founder's "entirely blank/white viewport" report, still live.

   The fix: never stack, and never let a collapsed pane reserve space. Split collapses to
   the 3D pane on a phone (two 187px panes is BUG-4 symptom 4, and the 2D CAD canvas is
   mouse-only — onMouseDown/onMouseMove, zero pointer/touch handlers in the bundle), which
   is also where the product is going: Nirman Plan owns 2D, arch3d is the 3D consumer. */
@media (width <= 768px) {
  /* stay side-by-side — with the collapsed pane gone, "side by side" IS one pane */
  [data-nd-app="arch3d"] main.flex-1 > .w-full.h-full.flex.overflow-hidden { flex-direction: row; }
  [data-nd-app="arch3d"] main.flex-1 > .w-full.h-full.flex.overflow-hidden > div[style*="width: 0%"] {
    display: none !important;
  }
  /* Split -> single 3D pane */
  [data-nd-app="arch3d"] main.flex-1 > .w-full.h-full.flex.overflow-hidden > div[style*="width: 50%"]:first-child {
    display: none !important;
  }
  [data-nd-app="arch3d"] main.flex-1 > .w-full.h-full.flex.overflow-hidden > div[style*="width: 50%"] + div[style*="width: 50%"] {
    width: 100% !important;
    border-right: none !important;
  }
  /* A6b — the 3D viewport must END where the workstation sheet begins, or the model
     centres itself behind the sheet (canvas 375x756, sheet top at 365 => the model's
     centre landed at y=434, hidden). Same 62dvh expression the sheet uses in A5, so the
     two stay in lockstep. The 2D pane is deliberately NOT shrunk: its floating palette is
     positioned against a full-height containing block by A4.

     --nd-sheet-h, not 62dvh: the app pins the open sheet with `height:55vh!important` in
     its own mobile block (index-DqbJrkVz.css), so A5's 62dvh never actually applied — the
     sheet measures 446.6px in an 812px viewport. A5 now drives the sheet from
     --nd-sheet-h (fallback 55vh) and this rule subtracts the SAME variable, so the
     viewport tracks the drag live and matches every snap point exactly. */
  [data-nd-app="arch3d"]:has(.arch3d-workstation-drawer[data-drawer-open="true"])
  main.flex-1 > .w-full.h-full.flex.overflow-hidden > div:last-child:not([style*="width: 0%"]) {
    height: calc(100% - var(--nd-sheet-h, 55vh)) !important;
  }
}

/* A7 — the plan status bar wraps ~65 chars of 10px mono inside a hard 32px box and
   clips ("Cursor:" half over "Snapped:"). Make it a one-line scroller. */
[data-nd-app="arch3d"] .h-8.border-t.bg-obsidian-surface { font-size: 11.5px; }
[data-nd-app="arch3d"] .h-8.border-t.bg-obsidian-surface > div:first-child {
  flex-wrap: nowrap;
  white-space: nowrap;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  min-width: 0;
}
[data-nd-app="arch3d"] .h-8.border-t.bg-obsidian-surface > div:first-child::-webkit-scrollbar { display: none; }
[data-nd-app="arch3d"] .h-8.border-t.bg-obsidian-surface > div:first-child > span { flex: 0 0 auto; }
@media (width <= 768px) {
  /* the Snapped readout duplicates Cursor — hide it to buy width on phones */
  [data-nd-app="arch3d"] .h-8.border-t.bg-obsidian-surface > div:first-child > span:nth-child(3) { display: none; }
}

/* A8 — drawer mode tabs truncated to "Proj/Stru/Elev" stubs because min-w-[58px] is
   undefined and flex:1 shrinks all 8 tabs to 43.5px; border-transparent/border-saffron
   are also undefined so the active tab lost its saffron indicator. 8x72 = 576px makes
   the strip actually scroll; the mask is the scroll affordance scrollbar-none removed. */
[data-nd-app="arch3d"] .min-w-\[58px\] { min-width: 72px; }
[data-nd-app="arch3d"] .border-transparent { border-color: transparent; }
[data-nd-app="arch3d"] .border-saffron { border-color: var(--saffron); }
@media (width <= 768px) {
  [data-nd-app="arch3d"] .arch3d-workstation-drawer .overflow-x-auto.scrollbar-none > button > span { font-size: 10.5px; }
  [data-nd-app="arch3d"] .arch3d-workstation-drawer .overflow-x-auto.scrollbar-none {
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 24px), transparent 100%);
            mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 24px), transparent 100%);
  }
}

/* A9 — backfill the missing layout/spacing utilities: the single biggest source of
   "sloppy". Panels declared p-3.5 render with ZERO padding, every mt-2 section butts
   the one above, the drawer resize handle is 0px wide, the compass needle is 0x0, and
   the session-restore toast has no background and sits 50% off its own left edge.
   All currently match zero declarations, so this is purely additive — and scoped to
   arch3d so generic names like .mt-2 cannot leak into the other apps. */
[data-nd-app="arch3d"] .p-3\.5 { padding: 14px }
[data-nd-app="arch3d"] .p-0\.5 { padding: 2px }
[data-nd-app="arch3d"] .gap-3\.5 { gap: 14px }
[data-nd-app="arch3d"] .mt-2 { margin-top: 8px }
[data-nd-app="arch3d"] .mb-2 { margin-bottom: 8px }
[data-nd-app="arch3d"] .pb-2 { padding-bottom: 8px }
[data-nd-app="arch3d"] .pb-2\.5 { padding-bottom: 10px }
[data-nd-app="arch3d"] .pl-4 { padding-left: 16px }
[data-nd-app="arch3d"] .ml-4 { margin-left: 16px }
[data-nd-app="arch3d"] .ml-6 { margin-left: 24px }
[data-nd-app="arch3d"] .mr-1 { margin-right: 4px }
[data-nd-app="arch3d"] .py-8 { padding-top: 32px; padding-bottom: 32px }
[data-nd-app="arch3d"] .shrink-0 { flex-shrink: 0 }
[data-nd-app="arch3d"] .col-span-2 { grid-column: span 2 / span 2 }
[data-nd-app="arch3d"] .w-2 { width: 8px }
[data-nd-app="arch3d"] .h-2 { height: 8px }
[data-nd-app="arch3d"] .w-1\.5 { width: 6px }
[data-nd-app="arch3d"] .w-56 { width: 224px }
[data-nd-app="arch3d"] .w-\[160px\] { width: 160px }
[data-nd-app="arch3d"] .w-\[260px\] { width: 260px }
[data-nd-app="arch3d"] .w-\[5px\] { width: 5px }
[data-nd-app="arch3d"] .max-w-0 { max-width: 0 }
[data-nd-app="arch3d"] .min-h-7 { min-height: 28px }
[data-nd-app="arch3d"] .min-h-8 { min-height: 32px }
[data-nd-app="arch3d"] .min-h-9 { min-height: 36px }
[data-nd-app="arch3d"] .top-2 { top: 8px }
[data-nd-app="arch3d"] .left-1\/2 { left: 50% }
[data-nd-app="arch3d"] .top-\[calc\(100\%\+0\.5rem\)\] { top: calc(100% + 0.5rem) }
[data-nd-app="arch3d"] .rounded-md { border-radius: 6px }
[data-nd-app="arch3d"] .rounded-2xl { border-radius: 16px }
[data-nd-app="arch3d"] .font-black { font-weight: 900 }
[data-nd-app="arch3d"] .italic { font-style: italic }
[data-nd-app="arch3d"] .text-left { text-align: left }
[data-nd-app="arch3d"] .text-base { font-size: 16px }
[data-nd-app="arch3d"] .text-3xl { font-size: 30px }
[data-nd-app="arch3d"] .leading-snug { line-height: 1.375 }
[data-nd-app="arch3d"] .leading-normal { line-height: 1.5 }
[data-nd-app="arch3d"] .place-items-center { place-items: center }
[data-nd-app="arch3d"] .items-baseline { align-items: baseline }
[data-nd-app="arch3d"] .items-stretch { align-items: stretch }
[data-nd-app="arch3d"] .list-none { list-style: none }
[data-nd-app="arch3d"] .list-disc { list-style: disc }
[data-nd-app="arch3d"] .no-underline { text-decoration: none }
[data-nd-app="arch3d"] .opacity-50 { opacity: .5 }
[data-nd-app="arch3d"] .opacity-60 { opacity: .6 }
[data-nd-app="arch3d"] .opacity-70 { opacity: .7 }
[data-nd-app="arch3d"] .overscroll-contain { overscroll-behavior: contain }
[data-nd-app="arch3d"] .transition-opacity { transition: opacity .2s }
[data-nd-app="arch3d"] .transition-transform { transition: transform .2s }
[data-nd-app="arch3d"] .duration-75 { transition-duration: 75ms }
[data-nd-app="arch3d"] .ease-out { transition-timing-function: cubic-bezier(0, 0, .2, 1) }
[data-nd-app="arch3d"] .bg-green { background-color: var(--green) }
[data-nd-app="arch3d"] .bg-saffron-bright { background-color: var(--saffron-bright) }
[data-nd-app="arch3d"] .text-sky { color: var(--sky) }
[data-nd-app="arch3d"] .bg-saffron\/10 { background-color: rgba(212, 80, 10, .10) }
[data-nd-app="arch3d"] .bg-saffron\/15 { background-color: rgba(212, 80, 10, .15) }
[data-nd-app="arch3d"] .bg-saffron\/20 { background-color: rgba(212, 80, 10, .20) }
[data-nd-app="arch3d"] .bg-saffron-alpha\/20 { background-color: rgba(212, 80, 10, .05) }
[data-nd-app="arch3d"] .bg-green\/10 { background-color: rgba(42, 143, 88, .10) }
[data-nd-app="arch3d"] .bg-white\/5 { background-color: rgba(255, 255, 255, .05) }
[data-nd-app="arch3d"] .light .bg-white\/5 { background-color: rgba(28, 25, 23, .05) }
[data-nd-app="arch3d"] .border-saffron\/15 { border-color: rgba(212, 80, 10, .15) }
[data-nd-app="arch3d"] .border-saffron\/20 { border-color: rgba(212, 80, 10, .20) }
[data-nd-app="arch3d"] .border-saffron\/25 { border-color: rgba(212, 80, 10, .25) }
[data-nd-app="arch3d"] .border-saffron\/30 { border-color: rgba(212, 80, 10, .30) }
[data-nd-app="arch3d"] .border-saffron\/35 { border-color: rgba(212, 80, 10, .35) }
[data-nd-app="arch3d"] .border-saffron-dim\/40 { border-color: rgba(163, 61, 7, .40) }
[data-nd-app="arch3d"] .border-green\/30 { border-color: rgba(42, 143, 88, .30) }
[data-nd-app="arch3d"] .border-red\/20 { border-color: rgba(226, 84, 68, .20) }
[data-nd-app="arch3d"] .border-red\/40 { border-color: rgba(226, 84, 68, .40) }
[data-nd-app="arch3d"] .border-subtle\/50 { border-color: var(--border-subtle) }
[data-nd-app="arch3d"] .shadow-\[0_0_8px_var\(--saffron\)\] { box-shadow: 0 0 8px var(--saffron) }
[data-nd-app="arch3d"] .shadow-\[0_0_8px_var\(--saffron-bright\)\] { box-shadow: 0 0 8px var(--saffron-bright) }
[data-nd-app="arch3d"] .backdrop-blur { -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px) }
[data-nd-app="arch3d"] .backdrop-blur-md { -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px) }
/* the now-5px drawer resize handle must be grabbable by finger */
[data-nd-app="arch3d"] .drawer-resize-handle { width: 5px }
@media (pointer: coarse) {
  [data-nd-app="arch3d"] .drawer-resize-handle { width: 14px }
}

/* A10 — missing interaction-state utilities: all 17 sliders and 7 checkboxes rendered
   browser-default blue (accent-saffron undefined) — the loudest "unfinished" tell in
   the app — and disabled buttons were visually identical to live ones. */
[data-nd-app="arch3d"] .cursor-pointer { cursor: pointer }
[data-nd-app="arch3d"] .cursor-crosshair { cursor: crosshair }
[data-nd-app="arch3d"] .cursor-col-resize { cursor: col-resize }
[data-nd-app="arch3d"] .accent-saffron { accent-color: var(--saffron) }
[data-nd-app="arch3d"] .appearance-none { -webkit-appearance: none; appearance: none }
[data-nd-app="arch3d"] .disabled\:opacity-40:disabled { opacity: .4 }
[data-nd-app="arch3d"] .disabled\:cursor-not-allowed:disabled { cursor: not-allowed }
[data-nd-app="arch3d"] .hover\:bg-red\/20:hover { background-color: rgba(226, 84, 68, .20) }
[data-nd-app="arch3d"] .hover\:bg-saffron-alpha:hover { background-color: var(--saffron-alpha) }
[data-nd-app="arch3d"] .hover\:bg-sky-alpha:hover { background-color: var(--sky-alpha) }
[data-nd-app="arch3d"] .hover\:border-saffron\/35:hover { border-color: rgba(212, 80, 10, .35) }
[data-nd-app="arch3d"] .hover\:text-saffron:hover { color: var(--saffron) }
[data-nd-app="arch3d"] .hover\:opacity-100:hover { opacity: 1 }

/* A11 — touch tap targets the app's coarse rule misses (it only covers button and
   [role=button]; .nd-touch-target is applied to zero elements): 10 selects at 32px,
   the number input at 28px, 14px slider thumbs, and the journey/suite links at ~24px. */
@media (pointer: coarse) {
  [data-nd-app="arch3d"] select,
  [data-nd-app="arch3d"] input[type="number"],
  [data-nd-app="arch3d"] input[type="text"] { min-height: 40px; }
  /* 40px hit area, thin visible track — background-clip keeps the bar slim */
  [data-nd-app="arch3d"] input[type="range"] {
    height: 40px;
    padding-block: 12px;
    background-clip: content-box;
  }
  [data-nd-app="arch3d"] input[type="range"]::-webkit-slider-thumb { width: 22px; height: 22px; }
  [data-nd-app="arch3d"] input[type="range"]::-moz-range-thumb { width: 22px; height: 22px; }
  [data-nd-app="arch3d"] [data-testid^="arch3d-suite-open-"],
  [data-nd-app="arch3d"] [data-testid^="arch3d-global-suite-open-"] {
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

/* A12 — scoped micro-type bumps. text-[8px] and text-[9px] only; text-[11px] is
   DELIBERATELY not bumped — v4 verified that enlarging it clips the truncating journey
   labels harder (it is a colour problem, fixed by A1/A2, not a size problem). */
[data-nd-app="arch3d"] .text-\[8px\] { font-size: 11px; }
[data-nd-app="arch3d"] .text-\[9px\] { font-size: 10.5px; letter-spacing: .04em; }

/* A12b — the held text-[10px] bump, LANDED 2026-08-13 on measurement, phone-only.
   The gate was "some sit inside fixed-height h-8/h-9 boxes where it can introduce new
   wrapping". Tested, not guessed: on production at 375x812 with touch emulation, every
   one of the eight workstation tabs was opened and each rendered .text-[10px] element
   measured before and after the bump (79 elements). Result: scrollWidth never exceeded
   clientWidth and scrollHeight never exceeded clientHeight in a single case — no new
   truncation and no new clipping. Elements grow 12px -> 14px inside panels that already
   scroll; the one h-8 box in the set ("Apply sample restyle") is already floored to 44px
   by the app's coarse-pointer rule and did not move at all.
   Phone-scoped on purpose: 10px is a legibility problem at arm's length on a phone, and
   desktop was never reported. Keeps the desktop studio pixel-identical. */
@media (width <= 768px) {
  [data-nd-app="arch3d"] .text-\[10px\] { font-size: 11.5px; }
}

/* A15 — the drawer's resize handle is a 5px `cursor-col-resize` strip for the DESKTOP
   left rail. On a phone the drawer is a bottom sheet, so a column-resize grip is
   meaningless — and A9 widens it to 14px on coarse pointers, where it pins itself to the
   right edge of the sheet for its full 446px height. Measured: it wins the hit test for
   the "Environment" tab (tab [360,366,72,49] vs handle [361,366,14,446]) and shadows the
   right 14px of every control in every panel. Remove it on phones. */
@media (width <= 768px) {
  [data-nd-app="arch3d"] .arch3d-workstation-drawer .drawer-resize-handle { display: none; }
}

/* A16 — 2D mode on a phone: make the floating palette a BOTTOM bar. A4 already confines
   the overlay to the band above the sheet (top 76px, bottom 62dvh+12px); flex-end packs
   its two glass panels against the sheet's tab strip instead of leaving them floating
   mid-canvas. flex-end packs wrapped lines the same way flex-start does — it does not
   reintroduce the stretch A4 was fixing. */
@media (width <= 768px) {
  [data-nd-app="arch3d"] div[class~="absolute"][class~="top-4"][class~="left-4"][class~="right-4"][class~="bottom-4"][class~="z-50"] {
    align-content: flex-end;
    row-gap: 8px;
  }
}

/* A17 — DRAGGABLE SHEET (2026-08-13, his call). Three snap points: peek 30vh, default
   55vh, tall 80vh. The DEFAULT is today's 55vh, so nothing moves for an existing user
   until they drag. Heights come from --nd-sheet-h (A5/A6b/A4 all read it); this block is
   only the affordance. Phone-only by construction — the handle is display:none outside
   the media block, so a desktop user never sees a grip for a rail that docks left. */
[data-nd-app="arch3d"] .nd-sheet-grab { display: none; }
@media (width <= 768px) {
  [data-nd-app="arch3d"] .nd-sheet-grab {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 100%;
    height: 44px;                 /* the whole strip is the target, not just the pill */
    padding: 0;
    margin: 0;
    background: transparent;
    border: 0;
    border-radius: 14px 14px 0 0;
    cursor: grab;
    touch-action: none;           /* the handle owns its gesture outright */
    -webkit-tap-highlight-color: transparent;
  }
  [data-nd-app="arch3d"] .nd-sheet-grab::before {
    content: "";
    width: 44px;
    height: 4px;
    border-radius: 999px;
    background: var(--text-muted, #94a3b8);
    opacity: .55;
  }
  [data-nd-app="arch3d"] .nd-sheet-grab:active { cursor: grabbing; }
  [data-nd-app="arch3d"] .nd-sheet-grab:active::before,
  [data-nd-app="arch3d"] .nd-sheet-grab:focus-visible::before { opacity: 1; }
  [data-nd-app="arch3d"] .nd-sheet-grab:focus-visible {
    outline: 2px solid var(--saffron, #d4500a);
    outline-offset: -3px;
  }
  /* GESTURE DISAMBIGUATION, half of it. The tab strip is a HORIZONTAL scroller and is
     also the second drag surface, so hand the browser the axis it owns and keep the
     other: pan-x lets native tab scrolling run untouched while vertical movement is
     never swallowed, which is what lets the JS decide intent from the first 8px. */
  [data-nd-app="arch3d"] .arch3d-workstation-drawer > .flex.border-b { touch-action: pan-x; }

  /* CODEX FIX 3 (must-fix): ALL THREE CONSUMERS TRANSITION TOGETHER, or the lockstep
     claim is only true at rest. v1 animated the sheet alone while the 3D pane and the
     palette took the new variable instantly — expanding flashed a blank gap for 220ms,
     collapsing parked the viewport behind a still-moving sheet. Same duration, same
     easing, same trigger class on all three. The pane's rule has to carry !important and
     re-state width/opacity because React sets `transition: width .3s, opacity .2s` inline
     on that element, and an inline shorthand would otherwise win and drop the height. */
  [data-nd-app="arch3d"] .arch3d-workstation-drawer.nd-sheet-snapping {
    transition: height .2s cubic-bezier(.32, .72, 0, 1);
  }
  [data-nd-app="arch3d"]:has(.arch3d-workstation-drawer.nd-sheet-snapping)
  main.flex-1 > .w-full.h-full.flex.overflow-hidden > div:last-child:not([style*="width: 0%"]) {
    transition: height .2s cubic-bezier(.32, .72, 0, 1),
                width .3s cubic-bezier(.4, 0, .2, 1),
                opacity .2s ease !important;
  }
  [data-nd-app="arch3d"]:has(.arch3d-workstation-drawer.nd-sheet-snapping)
  div[class~="absolute"][class~="top-4"][class~="left-4"][class~="right-4"][class~="bottom-4"][class~="z-50"] {
    transition: bottom .2s cubic-bezier(.32, .72, 0, 1);
  }
}
/* Reduced motion, belt and braces: the class is never added by the JS when the query
   matches, these rules null the transitions if it somehow is, and the app's own sheet
   already carries a global `transition-duration:.01ms!important` reset for this query. */
@media (prefers-reduced-motion: reduce) {
  [data-nd-app="arch3d"] .arch3d-workstation-drawer.nd-sheet-snapping,
  [data-nd-app="arch3d"]:has(.arch3d-workstation-drawer.nd-sheet-snapping)
  main.flex-1 > .w-full.h-full.flex.overflow-hidden > div:last-child:not([style*="width: 0%"]),
  [data-nd-app="arch3d"]:has(.arch3d-workstation-drawer.nd-sheet-snapping)
  div[class~="absolute"][class~="top-4"][class~="left-4"][class~="right-4"][class~="bottom-4"][class~="z-50"] {
    transition: none !important;
  }
}

/* C-A-back (UI-6): #nd-back-plan relocation on phones; moves into arch3d index.css with J6 */
@media (width <= 768px) {
  [data-nd-app="arch3d"] #nd-back-plan {
    left: auto !important;
    bottom: auto !important;
    top: calc(var(--nd-topbar, 56px) + 16px) !important;
    right: 12px !important;
    min-height: 44px;
    max-width: calc(100vw - 160px);   /* the switcher owns the left ~130px of that row */
  }
}

/* 2026-08-17 (his call): 2D CAD lives ONLY in Nirman Plan — arch3d's Full 2D Canvas is hidden on
   every width now, so the replacement door shows on desktop too (was hidden above 768px). */
[data-nd-app="arch3d"] #nd-2d-door {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  min-height: 44px;
  padding: 8px 12px;
  border-radius: 8px;
  border: 1px solid rgba(212, 80, 10, .35);
  background: rgba(212, 80, 10, .12);
  color: var(--saffron, #d4500a);
  font: 700 11.5px/1.25 Inter, system-ui, sans-serif;
  text-align: center;
  text-decoration: none;
}

/* A13 — the selected-object HUD on phones was a fixed 300px right rail covering 73% of
   the screen top to bottom. Make it the bottom sheet it wants to be. Inline width in
   the JSX, hence !important. z:80 — an inspector sits above the workstation sheet (70). */
@media (width <= 768px) {
  [data-nd-app="arch3d"] .arch3d-floating-hud {
    position: fixed !important;
    top: auto !important;
    inset: auto 0 0 0 !important;
    width: 100vw !important;
    min-width: 0 !important;
    max-width: 100vw !important;
    height: auto !important;
    max-height: 56dvh !important;
    z-index: 80 !important;
    border-left: none !important;
    border-top: 1px solid var(--border-medium) !important;
    border-radius: 14px 14px 0 0 !important;
    background-color: var(--obsidian-base) !important;
    box-shadow: 0 -10px 32px rgba(0, 0, 0, .55) !important;
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px)) !important;
  }
}
/*$vite$:1*/