
/* ══════════════════════════════════════════════════════════════════════════════════════
 * D49 · THE PUBLISHED SCREEN OWNS THE VIEWPORT — the app-viewport law, on bisbe.xyz.
 *
 * FOUNDER, 2026-08-20: "all pages should take full screen lik a desktop or mobile app and not
 * scroll like a web page" — asked for "both in dev and on the bisbe.xyz public demo".
 *
 * Measured here before these rules: the app frame was a correct 100dvh box, and the DOCUMENT
 * around it was 1246px tall at 1440 — 132px of demo chrome above, 188px of explanation below —
 * so the page scrolled 346px and both bars scrolled away with it.
 *
 * The two bars stand. The app takes exactly one screenful of what is left.
 * Structural only — no colour, no type, nothing this sheet did not already own.
 *
 * ── ★ AND THEN THE APP ITSELF SLID, WHICH IS THE SAME COMPLAINT ONE LAYER DOWN. ★ ────
 * FOUNDER, 2026-08-21 ~08:30 ET, on the published demo: *"the Rooms page still scroll on
 * bisbe.xyz demo"* — with the rail sliding up under the demo's own banner.
 *
 * The first cut of this block put the explanation INSIDE `.bw-app` and made `.bw-app` a
 * scroller, reasoning that the note should be "one gesture away rather than pinned or lost".
 * That is a defensible sentence and it produced an indefensible screen: measured on the shipped
 * bundle, `.bw-app` was 768px tall over 982px of content, so ONE wheel gesture anywhere on the
 * page moved the whole app — surface top 132 → −82, rail top 203 → −11. The document did not
 * scroll and the APP did, which is the complaint wearing the fix's own clothes. It also produced
 * every L10b refusal on the world subjects: the harness opens the menus AFTER driving the
 * scrollers, so rows the app had already carried off the top read as clipped sheets.
 *
 * ★ THE APP REGION IS NOT A SCROLLER. ★ It is the app's screen, and nothing in it can drag the
 * app. The explanation keeps every word, every link and its place in the served HTML — it moves
 * into the demo's own bar as a disclosure whose panel is POSITIONED, so opening it costs the app
 * zero pixels and closing it changes nothing. That is what a desktop app does with an
 * "about this window" note, and the law FOUNDER stated is the desktop-app law.
 * ════════════════════════════════════════════════════════════════════════════════════ */
body.bw-screen{height:100vh;height:100dvh;overflow:hidden;display:flex;flex-direction:column}
body.bw-screen>.bisbe-bar,body.bw-screen>.bw-bar{flex:none}
/* `overflow:hidden`, not `auto`: there is nothing under the app to reach, so a scroller here
   could only ever move the app. `overscroll-behavior` stays as the second fence — an inner pane
   that reaches its end must not chain OUT of the app at all. */
.bw-app{flex:1 1 0%;min-height:0;overflow:hidden;overscroll-behavior:contain;
  display:flex;flex-direction:column}
/* ONE SCREENFUL OF APP, and now it is the WHOLE of the region rather than 100% of a region that
   was taller than itself. `flex:1 1 0%; min-height:0` with `height:auto` overriding the app
   sheet's own `height:100dvh` — the attribute keeps the specificity so this wins on weight and
   not on which `<link>` happens to come last. */
/* BOTH shell roots. Customers is served from the other route group and has its own root class —
   naming only one of them published a 554px app in a 768px space on 21 of the 49 screens. */
.bw-app>.atlas-surface[data-posture],.bw-app>.crm-surface[data-posture]{flex:1 1 0%;min-height:0;height:auto}
/* ★ THE DEMO-MODE CONTRACT'S OWN THREE MARKS. ★ See `honestOffControls` /
   `honestFenceFamilies` in this file for what puts them on the page and why.

   THEY ARE DELIBERATELY QUIET AND DELIBERATELY NOT HIDDEN. The walk that produced them
   describes both failure directions in one door: a dead control that says nothing reads as
   broken, and a wall of repeated notices reads as noise ("eight identical rows to get here").
   So they sit at 12.5px in the dimmed ink — present at a glance, never competing with the row
   they annotate.

   NO HUE CARRIES MEANING. Each mark is glyph + words; the colour is the same dimmed ink for
   all three. FOUNDER is red/green colourblind, so a green "opens" against a red "dead" would
   be two identical marks to him. `◇` and `◉` are the world's own shapes for the same two
   states, already used on its rows. */
.bw-off,.bw-fence-tag,.bw-open-tag{font-size:12.5px;font-weight:500;
  font-family:'Familjen Grotesk',system-ui,sans-serif;line-height:1.35}
/* The control's own reason is a sibling of the control, on the page's ground — the dimmed ink. */
.bw-off{color:var(--bw-dim)}
/* ★ THE ROW MARKS TAKE THEIR HOST'S INK, NOT THE PAGE'S. ★ These live INSIDE the anchor, and
   an anchor is not always on the page's ground: `crm-cta-p` is a filled pill — white type on
   #824032 — and a mark set in the page's dimmed grey rendered as near-illegible slate ON that
   brown (measured: rgb(84,91,104) on rgb(130,64,50)). `currentColor` at reduced opacity is
   correct on every ground by construction, including the dark theme, and needs no per-control
   rule. It is also the only form that cannot be broken by a control this world has not met yet. */
.bw-fence-tag,.bw-open-tag{color:currentColor;opacity:.7}
.bw-off{display:inline-block;margin:6px 0 0 8px;max-width:44ch}
/* ★ THE REASON MAY NEVER TAKE WIDTH FROM THE CONTROL IT EXPLAINS. ★
   Customers' search row (`.fld-find`) is `display:flex; flex-wrap:nowrap`, so the note joined
   it as a fourth item and NEGOTIATED for width: measured at 375, the search field went from
   ~250px to 113px and its placeholder was cut from "Find a property, a person, a pie" to "Find
   a pro". Explaining a control by shrinking it is a worse defect than the one being closed.
   So on a flex row the note takes a full basis and lands on its own line under the row, and the
   row is made to wrap so it can. `flex-basis` is inert off a flex parent, which is every other
   place this note appears, so nothing else moves. */
p:has(>.bw-off),div:has(>.bw-off),span:has(>.bw-off),form:has(>.bw-off),fieldset:has(>.bw-off){flex-wrap:wrap}
.bw-off{flex:0 0 100%;margin-left:0}
/* Inside the anchor, so it moves with the row and is read out as part of the link's own name —
   a mark a screen reader meets AFTER following the link is not a mark.

   TWO FITS, because "inside an anchor" is two different places. See `CONTROL_LABEL_CHARS`.
     row   — a multi-line row (an account, a conversation). It is the row's FIRST child and gets
             a full-width line of its own above the row's content. Placed LAST it was a 33-line
             row's last item across an 18px grid gap, sitting 45px below its own content and
             15px above the rule under it — reading as a heading for the row BELOW. First is
             also the scannable position: every row now opens with its own answer, or nothing.
     label — a pill or a short text link (CHECK IN, New property, "Check in here →"). Beside the
             words. `display:inline` is not written here on purpose: these anchors are often
             `display:flex`, which BLOCKIFIES its children, so the declaration would compute to
             `block` and read as a promise the layout does not keep. Flex items do not overlap,
             so the default placement is already right; only the ink needed fixing (above). */
.bw-fence-tag[data-fit="row"],.bw-open-tag[data-fit="row"]{display:block;grid-column:1/-1;margin-bottom:2px}
.bw-fence-tag[data-fit="label"],.bw-open-tag[data-fit="label"]{margin-left:7px;white-space:nowrap}
/* The one that opens is the only one a reader is being pointed AT, so it is the only one set
   in the full ink. Weight and ink, not hue. */
.bw-open-tag{color:var(--bw-ink);font-weight:700}
:root{--bw-ink:#14171D;--bw-dim:#545B68;--bw-line:rgba(20,23,29,.14);--bw-paper:#FFFFFF;--bw-g0:#FAFBFD}
[data-theme="dark"]{--bw-ink:#F2F4F8;--bw-dim:#A9B0BC;--bw-line:rgba(242,244,248,.18);--bw-paper:#1A1D24;--bw-g0:#101318}
.bw-bar{position:relative;z-index:59;background:var(--bw-paper);border-bottom:1px solid var(--bw-line);
  font-family:'Familjen Grotesk',system-ui,sans-serif}
.bw-bar-in{max-width:1180px;margin:0 auto;display:flex;align-items:center;gap:12px;padding:10px 14px;flex-wrap:wrap}
/* ONE MARK PER PLACEMENT (FOUNDER, 2026-08-12). The lockup used to be the B monogram with
   the word "Bisbe" set beside it in Cinzel — the product's name said twice in one control,
   once as artwork and once as type. The compact artwork IS the wordmark, so it is the whole
   lockup now and the anchor's aria-label carries the accessible name. The reversed file is
   the correct mark on the dark ground; swapping in CSS keeps the choice off the first paint,
   which the mark-light-only lockup got wrong (forest green on #1A1D24, near invisible). */
/* THE READ-ONLY FENCE PILL — 14px, not 12.5px. It is the one sentence on the frame that
   tells a visitor nothing here writes, and at 12.5px it read as decoration beside a 18px
   lockup. A fence a person does not read is not a fence. (Its ≤700px override still steps
   to 12px; raising that is the pill lane's own call, not this regen's.)
   NO THEME PREFIX: a dangling [data-theme="dark"] from a deleted lockup-swap rule sat in
   front of this selector — CSS ignores the comment between them — so the pill was styled
   ONLY in dark theme and rendered completely unstyled in the default light theme on every
   published page (review finding, 2026-08-13). The rule is theme-agnostic by its vars. */
.bw-ro{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--bw-line);border-radius:999px;
  padding:5px 11px;font-size:14px;font-weight:600;color:var(--bw-ink);white-space:nowrap}
.bw-ro b{font-weight:700}
.bw-spacer{flex:1 1 auto}
.bw-tools{display:flex;align-items:center;gap:4px;flex-wrap:wrap}
.bw-tools a,.bw-bar button{display:inline-flex;align-items:center;justify-content:center;min-height:44px;
  padding:8px 12px;border-radius:10px;border:1px solid transparent;background:none;color:var(--bw-ink);
  font:inherit;font-size:14px;text-decoration:none;cursor:pointer}
.bw-tools a:hover,.bw-bar button:hover{border-color:var(--bw-line)}
.bw-bar button{border-color:var(--bw-line)}
.bw-note{max-width:1180px;margin:0 auto;padding:10px 14px 0;color:var(--bw-dim);font-size:13.5px;
  font-family:'Familjen Grotesk',system-ui,sans-serif;line-height:1.5}
.bw-note a{color:inherit}
.bw-foot{max-width:1180px;margin:0 auto;padding:26px 14px 44px;color:var(--bw-dim);font-size:13.5px;
  font-family:'Familjen Grotesk',system-ui,sans-serif;line-height:1.6;border-top:1px solid var(--bw-line);margin-top:26px}
.bw-foot h2{font-size:14px;color:var(--bw-ink);margin:0 0 8px}
.bw-doors{display:flex;flex-wrap:wrap;gap:6px;margin:0;padding:0;list-style:none}
.bw-doors a{display:inline-flex;min-height:44px;align-items:center;padding:8px 12px;border:1px solid var(--bw-line);
  border-radius:10px;text-decoration:none;color:var(--bw-ink);font-size:14px}
.bw-doors a[aria-current="page"]{font-weight:700;box-shadow:inset 0 -2px 0 var(--bw-ink)}
.bw-lede a,.bw-sub a,.bw-foot p a{color:var(--bw-ink);text-decoration:underline;
  text-underline-offset:2px;text-decoration-thickness:1px}
@media (max-width:700px){.bw-ro{font-size:12px}}

/* ══════════════════════════════════════════════════════════════════════════════════════
 * ★ "WHAT YOU ARE LOOKING AT" — the same words, in a control instead of under the app. ★
 *
 * The panel is ABSOLUTELY POSITIONED. That is the whole point and it is the one property that
 * must never be relaxed: in flow it would take height from the app, and a note that shrinks the
 * screen when you read it is the scrolling complaint with extra steps. Positioned, it costs the
 * app nothing open or closed.
 *
 * It is a `<details>`, so it works with no JavaScript — the demo's own standing rule is that
 * every sentence is readable in the bytes a crawler is served, and the words are in the document
 * whether the disclosure is open or not.
 *
 * The cap is `min(60vh, 420px)` with its own scroller and `overscroll-behavior:contain`: on a
 * short screen the note scrolls ITSELF and still cannot reach past its own edge into the app.
 * ════════════════════════════════════════════════════════════════════════════════════ */
.bw-why{position:relative}
.bw-why>summary{display:inline-flex;align-items:center;gap:7px;min-height:44px;padding:8px 12px;
  border-radius:10px;border:1px solid transparent;color:var(--bw-ink);font:inherit;font-size:14px;
  cursor:pointer;list-style:none;white-space:nowrap}
.bw-why>summary::-webkit-details-marker{display:none}
.bw-why>summary::after{content:"\203A";display:inline-block;transform:rotate(90deg);
  font-size:15px;line-height:1;color:var(--bw-dim)}
.bw-why[open]>summary::after{transform:rotate(-90deg)}
.bw-why>summary:hover,.bw-why[open]>summary{border-color:var(--bw-line)}
.bw-why-panel{position:absolute;right:0;top:calc(100% + 8px);z-index:5;
  width:min(520px,calc(100vw - 28px));max-height:min(60vh,420px);overflow-y:auto;
  overscroll-behavior:contain;background:var(--bw-paper);border:1px solid var(--bw-line);
  border-radius:12px;padding:16px 18px;box-shadow:0 12px 32px rgba(20,23,29,.16);
  color:var(--bw-dim);font-size:13.5px;line-height:1.6;text-align:left}
.bw-why-panel h2{font-size:14px;color:var(--bw-ink);margin:0 0 8px}
.bw-why-panel p{margin:0 0 10px}
.bw-why-panel p:last-child{margin-bottom:0}
.bw-why-panel b{color:var(--bw-ink)}
.bw-why-panel a{color:var(--bw-ink);text-decoration:underline;text-underline-offset:2px;
  text-decoration-thickness:1px}
/* ★ ON A PHONE THE PANEL IS ANCHORED TO THE BAR, NOT TO THE CONTROL. ★
   The first cut kept `.bw-why` as the containing block and centred with
   `left:50%; transform:translateX(-50%)` — but 50% of a 122px SUMMARY is not 50% of the
   screen, so a 355px panel hung ~120px off the left edge at 375 and its first words were
   unreadable. That is the #2131 class exactly ("The More menu drew off the side of the phone"),
   caught here by reading the render rather than by trusting the rule.
   `position:static` hands the containing block up to `.bw-bar` (which is already
   `position:relative`), so `left/right` are the SCREEN's edges and `top:100%` is the bar's
   own bottom — the panel spans the width under the bar and cannot hang off either end. */
@media (max-width:700px){
  .bw-why{position:static}
  .bw-why-panel{left:10px;right:10px;width:auto;max-height:min(70vh,420px);top:calc(100% + 6px)}
  .bw-why>summary{padding:8px 10px}
}

body{margin:0;background:var(--bw-g0);color:var(--bw-ink);font-family:'Familjen Grotesk',system-ui,sans-serif}
.bw-wrap{max-width:70ch;margin:0 auto;padding:40px 16px}
.bw-h1{font-family:'Cinzel',Georgia,serif;font-size:clamp(26px,4vw,38px);margin:0 0 14px;line-height:1.15}
.bw-wrap p{font-size:16.5px;line-height:1.65;margin:0 0 14px}
.bw-wrap a{color:inherit}
