/* The chrome: masthead, mega-menu, drawer, footer, dialog. Phase F stop F4.2.
 *
 * WHERE THIS COMES FROM. Every rule is ported from a generator whose output the
 * owner signed off, not re-derived from prose -- ops/mockups/README.md states
 * that reasoning and ops/docs/phase-f/f4-build.md section 7 requires it:
 *
 *   .mh-*   masthead   chrome_shared.py    F3.4, signed off 2026-08-31
 *   .mm-*   mega-menu  nav-build.py        F3.3, signed off 2026-08-29
 *   .dw-*   drawer     nav-build.py        F3.3, variant A
 *   .ftr-*  footer     footer-build.py     F3.5, signed off 2026-09-01
 *   .dlg-*  dialog     dialog-build.py     F3.6, signed off 2026-09-02
 *
 * FOUR THINGS ARE DELIBERATELY NOT COPIED, because a mockup is a picture of a
 * page inside another page and this is the page:
 *
 * 1. THE WIDTH SWITCH. The mockups select layout with .uc[data-w="390"] and
 *    friends, because a frame of fixed pixel width cannot ask the viewport. Here
 *    those become real media queries on ONE breakpoint, 1200px -- chrome.md
 *    section 3.1. Below it: hamburger, drawer, no phone in the row, mark-only
 *    logo, no mega-menu. At and above: mega-menu, phone in the row, the wordmark,
 *    no hamburger.
 * 2. THE FRAME SCOPE. Every mockup rule is prefixed .uc and positioned
 *    `absolute` against it. The drawer, its scrim and the dialog are `fixed`
 *    here: they belong to the viewport, not to an element.
 * 3. THE TOKENS. The mockups redeclare --brand and the rest on .uc / .mk. They
 *    are on :root in uc-ds.css and are not redeclared.
 * 4. THE VARIANT TOGGLES. nav-build.py carries both drawer variants behind
 *    [data-v]. Only variant A ships -- ADR 0037, the owner's decision of
 *    2026-08-29: a group expands in place, with its header sticky.
 *
 * ONE KNOWN DEPARTURE FROM A SIGNED-OFF ARTEFACT, and it is the specification's.
 * chrome_shared.py switches the wordmark on data-w="390" alone, so its 1010
 * frame draws the full lockup. chrome.md says mark-only below 1200 in section
 * 3.1's table and again in 3.3. The spec wins; the mockup carries a note saying
 * so and was not edited, because a signed-off artefact is an acceptance
 * criterion.
 *
 * .mm-* IS ALSO MMENU'S NAMESPACE. There is no clash because jquery.mmenu.all
 * .css leaves base.html in this same commit. Do not reintroduce it.
 */

/* ------------------------------------------------------------ page ground */
/* The first element rules in the project. F4.1 could carry none -- its whole
 * acceptance was that no existing page changed -- and that constraint is
 * deliberately given up here, which is what f4-build.md rule B1 anticipates:
 * F4.2 onward changes markup and CSS together and is verified by browsing.
 *
 * These reach EVERY page, including the ones not rewritten until F4.4 to F4.6,
 * so they are the smallest set that makes the chrome coherent and no more.
 *
 * THE RESET BELOW WAS ARGUED AGAINST HERE AND THE ARGUMENT EXPIRED. This block
 * said there must be no global margin or box-sizing reset, "because Bootstrap
 * still lays out the body of those pages and resetting under it would move
 * them". True while every page loaded Bootstrap. F4.3 made index_new.html and
 * flatpages/default.html the first templates to empty the legacy block -- so
 * they had NO reset at all, the browser's default `body{margin:8px}` applied,
 * and the page rendered inside an 8px frame. Reported on review 2026-09-05.
 *
 * IT IS A NO-OP ON THE LEGACY PAGES. Bootstrap 3 sets exactly these two things:
 * `*{box-sizing:border-box}` on every element and its pseudo-elements, and
 * `body{margin:0}`. Declaring them here changes nothing where Bootstrap is
 * loaded and supplies them where it is not, which is the only shape that works
 * while both kinds of page exist. ~~It stays after F4.7~~ -- it stayed at
 * F4.8a, and it is now the only copy rather than a duplicate. */
*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-base);
  -webkit-font-smoothing: antialiased;
}
/* Prices and counts line up. tnum is in the subset and verified to survive it. */
.tabular { font-variant-numeric: tabular-nums; }

/* --------------------------------------------------- the chrome's link styling */
/* WRITTEN TO HOLD BOOTSTRAP OFF, AND KEPT AT F4.8a WHEN BOOTSTRAP WENT.
 *
 * The header below says this block goes with bootstrap.min.css. It does not,
 * and the reason is worth stating: these rules were written to neutralise
 * Bootstrap's element-level defaults, but what they declare -- no underline on
 * the four navigation roots, white on hover for the masthead phone and cart --
 * is now the chrome's own appearance rather than a counterweight to somebody
 * else's. Deleting them does not restore a neutral state; it underlines every
 * masthead, menu, drawer and footer link and turns two of them blue on hover.
 *
 * So the rules stay and only their justification changes. Whether the chrome
 * should keep this appearance now that nothing forces it is a question for
 * someone looking at the rendered page, not for the stop that deleted the
 * stylesheet -- f4-build.md B1, one kind of change per stop.
 *
 * The scoping comment below is also spent: it explains why this is scoped to
 * the chrome's five roots rather than written as a global reset, and the reason
 * was that un-rewritten page bodies still laid out under Bootstrap. There are
 * no un-rewritten page bodies. The scoping is now simply narrow.
 *
 * WHAT IT WAS FOR, kept because it is the record of four real symptoms.
 * Reported on the F4.2 review, 2026-09-04, as four symptoms with one cause:
 *
 *   a:hover,a:focus{color:#23527c;text-decoration:underline}
 *       underlined the masthead phone AND recoloured it -- and #23527c is
 *       Bootstrap 3's untouched @link-hover-color, the value design-system.md
 *       section 5 records as the nearest thing the old stylesheet had to a brand
 *       colour. Bootstrap blue, on a teal masthead.
 *
 *       It also explains why «Сушилки для рук» alone looked wrong in the menu
 *       bar: it is the ONLY <a> among the eight top-level items -- the other
 *       seven are <button>, because they open panels -- so it alone took a text
 *       underline on top of its 3px brand border, which read as running into its
 *       neighbour.
 *
 *   input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}
 *       removed the field's clear control. The mockup has one because the mockup
 *       has no Bootstrap.
 *
 * ~~ALL OF THIS GOES AT F4.7 with bootstrap.min.css~~ -- superseded 2026-09-09,
 * see the head of this block. bootstrap.min.css went at F4.8a; these rules did
 * not, because by then they were doing their own work.
 *
 * THE FOCUS OUTLINE IS DELIBERATELY NOT TOUCHED HERE. */
/* `.dlg a` CAME OUT OF THIS LIST, 2026-09-07. The other four roots are
 * navigation -- a masthead, a menu, a drawer, a footer -- where an underline on
 * every item is noise. THE DIALOG HAS NO NAVIGATION IN IT. Both links it can
 * ever show are prose: the consent sentence's «политикой обработки персональных
 * данных», and the «Открыть страницу обратного звонка» that replaces the form
 * when the fetch fails. A link inside running text needs its underline, and
 * removing it left a teal word in a grey sentence that read as emphasis.
 * The owner reported it as looking unstyled. They now take uc-ds.css's base. */
.mh a, .mm a, .dw a, .ftr a,
.mh a:hover, .mm a:hover, .dw a:hover, .ftr a:hover,
.mh a:focus, .mm a:focus, .dw a:focus, .ftr a:focus {
  text-decoration: none;
}
/* Re-declared after the reset: the footer's list DOES underline on hover. That
 * one is ours and is in the signed-off F3.5 footer. */
.ftr-links a:hover { text-decoration: underline; }

/* Colour, for the chrome links that do not change it on hover. Bootstrap's
 * a:hover was what turned white text on teal blue; with it gone these are what
 * hold the hover colour, and they are load-bearing rather than defensive. */
.mh-phone:hover, .mh-phone:focus,
.mh-cart:hover, .mh-cart:focus { color: #fff; }
.dw-phone:hover, .dw-phone:focus,
.dw-link:hover, .dw-link:focus { color: var(--ink); }
.dw-kid:hover, .dw-kid:focus { color: var(--sec); }
.dw-util:hover, .dw-util:focus { color: #fff; }

/* -------------------------------------------------------------------- focus */
/* THE RING IS FOR THE KEYBOARD, AND NOT FOR A MOUSE CLICK. Both halves matter.
 *
 * What was here before was Bootstrap's, and it was wrong in two directions at
 * once. `a:focus{outline:5px auto -webkit-focus-ring-color}` fires on a POINTER
 * click, which is what put a ring on the logo when the owner clicked it -- and
 * because the rule names `a` and nothing else, the search submit, the cart, the
 * hamburger and every menu and drawer button had NO focus indication at all.
 * Links ringed on click; buttons invisible to the keyboard. The owner reported
 * the inconsistency; the inconsistency was the defect.
 *
 * :focus-visible is the distinction the browser already knows how to draw: it
 * matches keyboard focus and withholds itself from a pointer click. So each
 * control resets Bootstrap's ring on :focus and declares its own on
 * :focus-visible. Both selectors are one class plus one pseudo-class, which
 * beats `a:focus`, and the ring comes second so it wins where both match.
 *
 * DELETING THE RING INSTEAD WAS CONSIDERED AND DECLINED, 2026-09-04. It is how
 * somebody navigating by Tab knows where they are, and the mockups show none
 * only because they carry no Bootstrap rule to begin with -- they show none on
 * Tab either, which is worse than what was there.
 *
 * The `:focus` half of this block goes at F4.7 with Bootstrap; the
 * `:focus-visible` half is the chrome's own and stays. */
.mh-burger:focus, .mh-logo:focus, .mh-phone:focus, .mh-search-go:focus,
.mh-cart:focus, .dw-x:focus, .dw-util:focus, .ftr a:focus, .cb-btn:focus,
.mm-t:focus, .mm-h:focus, .mm-l:focus, .dw-phone:focus, .dw-row:focus,
.dw-link:focus, .dw-kid:focus, .dlg-x:focus { outline: 0; }

/* White, where the ground is the brand teal or the footer's dark blue. */
.mh-burger:focus-visible, .mh-logo:focus-visible, .mh-phone:focus-visible,
.mh-search-go:focus-visible, .mh-cart:focus-visible, .dw-x:focus-visible,
.dw-util:focus-visible, .ftr a:focus-visible, .cb-btn:focus-visible {
  outline: var(--focus-ring-inverse);
  outline-offset: var(--focus-offset);
}
/* Brand, on the light grounds: the menu bar and its panels, the drawer's rows,
 * the dialog's close. */
.mm-t:focus-visible, .mm-h:focus-visible, .mm-l:focus-visible,
.dw-phone:focus-visible, .dw-row:focus-visible, .dw-link:focus-visible,
.dw-kid:focus-visible, .dlg-x:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}
/* The search field takes :focus, NOT :focus-visible, and that is deliberate: a
 * text field you have clicked into is one you are about to type in, and it
 * should say so however the caret got there. .ds-input does the same. The offset
 * is inset because the field is flush against its submit -- an outward ring
 * would cross it. */
.mh-search input:focus {
  outline: var(--focus-ring-inverse);
  outline-offset: -2px;
}

/* -------------------------------------------------------------- masthead */
.mh { background: var(--brand); color: #fff; }
/* One row at every width, and every control in it is a 44px target -- C3.
 * The live site has 42x32, 36x34 and 36x34, measured at E1. */
.mh-row {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--gutter);
  min-height: 60px;
}
/* var(--shell), not 1180 -- ADR 0046 decision 3. The masthead is as wide as
 * the page it sits on, whatever that page declared. */
.mh-in { max-width: var(--shell); margin: 0 auto; }

.mh-burger {
  width: var(--target); height: var(--target);
  border: 0; background: transparent; cursor: pointer;
  display: flex; flex-direction: column; justify-content: center;
  gap: 5px; padding: 0 10px; flex: none;
}
.mh-burger span { display: block; height: 2px; background: #fff; border-radius: 2px; }

/* Both rules are element-qualified so neither out-specifies the other: a bare
 * .mh-logo-mark loses to .mh-logo img and the page draws both marks. */
/* min-WIDTH as well as min-height -- `S83`. Below 1200 the lockup is replaced by
 * the 64x64 mark at height:34px, so the anchor was 34 x 44: a 10px miss on
 * criterion 5, on the link home from every page, and the only masthead control
 * that missed it. Measured 2026-09-06, audit/2026-09-06-chrome-geometry.md
 * section 2. Inert at 1200 and above, where the full lockup is 192px wide. */
.mh-logo { display: flex; align-items: center; justify-content: center;
  min-height: var(--target); min-width: var(--target); flex: none; text-decoration: none; }
.mh-logo img { display: block; height: 30px; width: auto; }
.mh-logo img.mh-logo-full { display: none; }
.mh-logo img.mh-logo-mark { display: block; height: 34px; }

/* Digits, not a call icon: a number behind a tel: link is self-evidently
 * callable, and the icon was the one thing in the row carrying no information.
 *
 * translateY(4.2px) ALIGNS THE DIGITS TO УЛЬТРАКЛИМАТ'S BASELINE, not to the
 * image box. The mark's ink spans the full 64px of the artwork but the
 * wordmark's sits at y 24-54, so centring the image centres the mark and leaves
 * the digits high. Measured to a residual of 0.04px at a 30px logo and 16px
 * digits -- chrome.md 3.4.
 *
 * IT IS A WORKAROUND FOR THE ARTWORK AND SHOULD NOT SURVIVE. It exists because
 * the mark and the wordmark are one bitmap. Once the wordmark is its own
 * element -- the SVG chrome.md 3.3 asks the owner for -- align-items:baseline
 * does the same job at any size with nothing to re-measure. Tied to those two
 * font sizes: change either and re-measure. */
.mh-phone {
  display: none;
  align-items: center;
  min-height: var(--target);
  padding: 0 2px;
  flex: none;
  font-size: 16px; font-weight: var(--fw-semibold);
  color: #fff; text-decoration: none; white-space: nowrap;
  font-variant-numeric: tabular-nums; letter-spacing: .01em;
  transform: translateY(4.2px);
}

/* One field at every width. There is no expanding control and no dialog, which
 * is what closes S57 permanently -- there is no second implementation to fall
 * out of sync. chrome.md 3.2. */
.mh-search { display: flex; align-items: center; margin-left: auto; flex: 1; min-width: 0; }
.mh-search input {
  flex: 1; min-width: 0;
  height: var(--target); padding: 0 var(--s-3);
  border: 0; border-radius: var(--r-md) 0 0 var(--r-md);
  background: #fff; color: var(--ink);
  font: inherit; font-size: 14px;
}
.mh-search input::placeholder { color: var(--mut); }
/* The clear control, restored. Bootstrap removes it TWICE -- once on the cancel
 * pseudo-element and once with -webkit-appearance:none on the input itself --
 * and undoing only the pseudo-element does nothing, which is what the first
 * attempt at this did. Both are undone here.
 *
 * `auto` does not disturb the field: measured 336x44 with no border, a 6px
 * radius and a white ground both before and after, because every one of those
 * is declared above and a declared value beats the native appearance. */
.mh-search input { -webkit-appearance: auto; appearance: auto; }
.mh-search input::-webkit-search-cancel-button {
  -webkit-appearance: auto;
  appearance: auto;
  cursor: pointer;
}
/* C4 -- inert until the query is non-empty. 19.4% of the search log is the empty
 * query or spam, so the control carries that rule visually as well as
 * functionally: lighter while it does nothing, and the cart's own treatment once
 * it does. One state, told twice. */
.mh-search-go {
  width: var(--target); height: var(--target); flex: none;
  border: 0; border-radius: 0 var(--r-md) var(--r-md) 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.16); color: #fff; cursor: pointer;
}
.mh-search-go[disabled] {
  background: rgba(255,255,255,.08); color: rgba(255,255,255,.6);
  cursor: not-allowed;
}

.mh-cart {
  position: relative; flex: none;
  width: var(--target); height: var(--target);
  border-radius: var(--r-md);
  background: rgba(255,255,255,.16); color: #fff;
  display: flex; align-items: center; justify-content: center; text-decoration: none;
}
/* The badge is positioned in flow, not with two !important rules fighting an
 * absolutely centred logo. The absolute centring at uc.css:328-332 is what made
 * those necessary; it is gone. chrome.md 3.3, 3.6, criterion 11. */
.mh-badge {
  position: absolute; top: -5px; right: -5px;
  min-width: 19px; height: 19px; padding: 0 5px;
  border-radius: var(--r-pill);
  background: var(--accent); color: #fff;
  font-size: 11px; font-weight: var(--fw-semibold); line-height: 19px; text-align: center;
}

/* ------------------------------------------------------------ mega-menu */
.mm { display: none; position: relative; background: var(--surface); border-bottom: 1px solid var(--rule); }
/* THE MENU'S TEXT LANDS ON THE GUTTER, NOT ITS BOX -- the owner's, 2026-09-10.
 *
 * `.mm-t` carries `padding: … 15px`, which is its 44px target (ADR 0043) and
 * has to stay. With a plain `var(--gutter)` here that padding is counted twice
 * at the two ends of the row: the item's BOX aligned with the logo and the
 * page heading at x=50, and its first ink started at 65. Nothing on the page
 * reads a button's invisible padding, so the row read as indented against the
 * logo above it and the h1 below it.
 *
 * Subtracting the item's own inset pulls the box out by 15 so the TEXT lands on
 * the gutter. Every item keeps its full target on both sides; the first and
 * last simply extend into the gutter, which is what a nav rail should do.
 *
 * `max()` GUARDS THE NARROW END. Below 768 the gutter is 16, so the subtraction
 * would be 1px, and below that any future smaller gutter would go negative and
 * pull the row out of the shell. The floor is 0. */
.mm-bar { display: flex; flex-wrap: wrap; max-width: var(--shell); margin: 0 auto;
  padding: 0 max(0px, calc(var(--gutter) - 15px)); }
.mm-t {
  font: inherit; font-size: 14px; font-weight: var(--fw-semibold);
  padding: var(--s-3) 15px; border: 0; border-bottom: 3px solid transparent;
  background: transparent; color: var(--ink); cursor: pointer; white-space: nowrap;
}
/* The underline is the hover affordance of a top-level item, not a side effect
 * of its panel opening. Сушилки для рук has no panel and got nothing before both
 * states were declared. */
.mm-t:hover, .mm-t:focus-visible,
.mm-t[aria-expanded="true"] { color: var(--brand); border-bottom-color: var(--brand); }
.mm-t-link { text-decoration: none; display: inline-block; }

/* A real mega-menu overlays the page; it does not reflow it. */
.mm-p {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 20;
  background: var(--surface);
  border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule);
  padding: var(--s-5) 22px;
  box-shadow: 0 14px 28px -10px rgba(23,39,43,.22);
  /* N1-N3: it fades, and crossing three items quickly queues nothing -- a
     transition interrupts naturally, which is the .stop() the jQuery version
     needed and could get wrong. 200ms is measured from the live site. */
  opacity: 0; visibility: hidden;
  transition: opacity var(--t-fade) ease, visibility var(--t-fade);
}
.mm-p[data-open="true"] { opacity: 1; visibility: visible; }
.mm-panel { display: flex; gap: 22px; align-items: flex-start; max-width: var(--shell); margin: 0 auto; }
/* Columns grow to share the panel but stop at a readable measure, so a
 * two-column section does not stretch each column to half the viewport. The
 * 180px basis is what lets Отопление's five fit at 1200. */
.mm-col { flex: 1 1 180px; min-width: 0; max-width: 300px; display: flex; flex-direction: column; gap: 2px; }
.mm-h, .mm-l {
  display: flex; gap: var(--s-2); align-items: baseline;
  padding: 3px 0; font-size: 13px; line-height: 1.35; text-decoration: none;
}
.mm-h {
  font-size: 13.5px; font-weight: var(--fw-semibold); color: var(--ink);
  border-bottom: 1px solid var(--rule); padding-bottom: 6px; margin-bottom: 5px;
}
.mm-l { color: var(--sec); }
.mm-h:hover, .mm-l:hover { color: var(--brand); }

/* -------------------------------------------------------------- drawer */
/* Off-canvas and OVERLAYING, not pushing. Today's drawer pushes the page
 * sideways and keeps the hamburger visible, which is why census rows N8 and N9
 * specify a 100ms morph to an X. Overlaying puts the button behind the panel, so
 * that morph would play where nobody can see it: N8 and N9 are dropped against
 * the census verdict and an explicit 44px close button in the drawer's own title
 * bar replaces them. navigation.md section 6.
 *
 * fixed, not absolute: the mockup positions against its frame; this positions
 * against the viewport. */
.dw {
  position: fixed; top: 0; bottom: 0; left: 0; z-index: 60;
  width: 86%; max-width: 340px;
  display: flex; flex-direction: column;
  background: var(--surface); border-right: 1px solid var(--rule);
  box-shadow: var(--sh-drawer);
  visibility: hidden; transform: translateX(-102%);
  transition: transform var(--t-drawer) var(--ease), visibility var(--t-drawer);
}
/* Criterion 18: closed on load and on every page. A visitor who does not open it
 * sees the page, not the catalogue. */
.dw[data-open="true"] { visibility: visible; transform: translateX(0); }
.dw-scrim {
  position: fixed; inset: 0; z-index: 50;
  background: var(--scrim); border: 0; padding: 0; display: none;
}
.dw-scrim[data-open="true"] { display: block; }

.dw-title {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--s-3) var(--s-2) var(--s-3) 18px;
  background: var(--brand); color: #fff;
  font-size: 15px; font-weight: var(--fw-semibold); flex: none;
}
/* An explicit close. With the panel at 334px of a 390px screen the scrim is a
 * 56px strip -- a poor target and an invisible affordance. */
.dw-x { position: relative; width: var(--target); height: var(--target); flex: none; border: 0; background: transparent; cursor: pointer; }
.dw-x::before, .dw-x::after {
  content: ""; position: absolute; left: 13px; top: 21px;
  width: 18px; height: 2px; background: #fff; border-radius: 2px;
}
.dw-x::before { transform: rotate(45deg); }
.dw-x::after  { transform: rotate(-45deg); }

.dw-scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; }
/* The phone is the drawer's first row, as it is today. Criterion 19. */
.dw-phone {
  display: flex; align-items: center; gap: 10px;
  min-height: var(--target-row); padding: 13px 18px;
  border-bottom: 1px solid var(--rule);
  font-size: 15px; font-weight: var(--fw-semibold);
  color: var(--ink); text-decoration: none;
}
.dw-phone .ds-ic { width: 17px; height: 17px; fill: var(--brand); stroke: none; }

/* A group expands in place -- ADR 0037, the owner's decision of 2026-08-29,
 * against the recommendation. The sticky header ships with it: the control that
 * closes thirteen rows never leaves the screen. */
.dw-row, .dw-link {
  display: flex; width: 100%; align-items: center; justify-content: space-between;
  min-height: var(--target-row); padding: 13px 18px;
  border: 0; border-bottom: 1px solid var(--rule);
  background: transparent; color: var(--ink);
  font: inherit; font-size: 15px; text-align: left; text-decoration: none; cursor: pointer;
}
.dw-row[aria-expanded="true"] {
  position: sticky; top: 0; z-index: 3;
  background: var(--surface); box-shadow: 0 1px 0 var(--rule);
  color: var(--brand);
}
.dw-chev {
  width: 8px; height: 8px; flex: none;
  border-right: 2px solid var(--mut); border-bottom: 2px solid var(--mut);
  transform: rotate(45deg); transition: transform var(--t-fade) var(--ease);
}
.dw-row[aria-expanded="true"] .dw-chev { transform: rotate(-135deg); border-color: var(--brand); }
.dw-kids { display: none; }
.dw-kids[data-open="true"] { display: block; }
.dw-kid {
  display: flex; align-items: center;
  min-height: var(--target-row); padding: 11px 18px 11px 32px;
  border-bottom: 1px solid var(--rule); background: var(--ground);
  font-size: 14px; color: var(--sec); text-decoration: none;
}

/* Five service links close the drawer, TWO columns wide. Three would give a
 * 111px target at 390px and these labels run to nine characters. */
.dw-utils { display: grid; grid-template-columns: 1fr 1fr; background: var(--ink); }
.dw-util {
  display: flex; align-items: center;
  min-height: var(--target-row); padding: 14px 12px;
  border-top: 1px solid rgba(255,255,255,.1);
  font-size: 13.5px; color: #fff; text-decoration: none; opacity: .92;
}
.dw-util:nth-child(odd) { border-right: 1px solid rgba(255,255,255,.1); }

/* -------------------------------------------------------------- footer */
/* G1: the grid FILLS the row. Three col-sm-3 fill nine of twelve and leave a
 * whole empty column at every width from 768 up -- three tracks, not three of
 * twelve. Criterion 13. */
.ftr { background: var(--footer-ground); color: var(--footer-ink); margin-top: 28px; }
.ftr-in { max-width: var(--shell); margin: 0 auto; }
.ftr-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; padding: 30px var(--gutter) 22px; align-items: start; }
.ftr-col p { margin: 0 0 6px; }
.ftr-h {
  margin: 0 0 var(--s-3);
  font-size: var(--fs-eyebrow); letter-spacing: .1em; text-transform: uppercase;
  color: var(--footer-head); font-weight: var(--fw-semibold);
}
.ftr a { color: #fff; text-decoration: none; border-bottom: 1px solid rgba(255,255,255,.28); }
.ftr a:hover { border-bottom-color: #fff; }
/* G2: every row is a 44px target. Today all 27 footer anchors render 16.5px tall
 * and the tel: link is 115x17 -- not one of them is a target. Criterion 19.
 *
 * .ftr-legal a JOINED THIS LIST 2026-09-06, `S84`. It is the newest link in the
 * footer -- added for `S66` after this rule was written -- and it was the one
 * anchor of nine still rendering 15px tall, which is the height every footer
 * link had BEFORE the rewrite. audit/2026-09-06-chrome-geometry.md section 4.
 * A rule that lists its subjects by name is a rule the next link misses, and
 * this is the second time: `.ftr a` above lists five and had to. */
.ftr-tel a, .ftr-mail a, .ftr-links a,
.ftr-legal a { display: inline-flex; align-items: center; min-height: var(--target); }
/* AND THE UNDERLINE HAS TO COME OFF THE BOX AND GO BACK ON THE TEXT.
 *
 * `.ftr a` draws the link affordance as a border-bottom, which hugs the text
 * while the anchor is inline. Give that anchor a 44px inline-flex box and the
 * border goes to the BOTTOM OF THE BOX -- an underline floating roughly 14px
 * clear of the words it belongs to.
 *
 * This is not new with `S84`: `.ftr-mail a` has been 44px since F4.2 and its
 * underline has been detached ever since, on every page. `.ftr-tel a` and
 * `.ftr-links a` never showed it because both set border-bottom:0 below --
 * which is the same fix, arrived at without the reason being written down.
 * Fixed here for both, because `S84` would otherwise have added a second copy
 * of a defect that was already on screen. Seen on the replica, 2026-09-06.
 *
 * text-decoration rather than the border: it follows the text, it wraps with
 * it, and the offset keeps it off the descenders. The colour is the border's. */
.ftr-mail a, .ftr-legal a {
  border-bottom: 0;
  text-decoration: underline;
  text-decoration-color: rgba(255,255,255,.28);
  text-underline-offset: 3px;
}
/* The line has to be re-declared on hover, not only its colour: the reset above
 * carries `.ftr a:hover { text-decoration: none }` at the same specificity, so
 * a hover rule that sets only the colour loses the underline on hover. */
.ftr-mail a:hover, .ftr-legal a:hover {
  text-decoration: underline;
  text-decoration-color: #fff;
}
.ftr-tel { margin: 0; font-size: 19px; font-weight: var(--fw-semibold); }
.ftr-tel a { border-bottom: 0; }
.ftr-mail { margin: 2px 0 var(--s-2); }
.ftr-hrs { margin-top: 10px; font-size: 13px; line-height: 1.5; color: var(--footer-mut); }
.ftr-links { list-style: none; margin: 0; padding: 0; }
.ftr-links li { margin: 0; }
.ftr-links a { border-bottom: 0; }
.ftr-links a:hover { text-decoration: underline; }

.ftr-bar {
  margin: 0 22px; padding: var(--s-4) 0;
  border-top: 1px solid rgba(255,255,255,.14);
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px;
}
.ftr-mark { display: block; height: 22px; width: auto; opacity: .9; }
.ftr-org, .ftr-copy, .ftr-legal { margin: 0; font-size: var(--fs-eyebrow); color: var(--footer-mut); }
.ftr-copy { margin-left: auto; }
/* G3: the fixed callback button covers the bottom 34px of the footer at every
 * width, measured, against padding:0 today. The reserve is 50px of SPACE and
 * nothing else -- the dashed rule that marked it went 2026-09-03, the owner's
 * amendment: a dashed line in a footer is an annotation, and the footer carries
 * none. Criterion 20. */
.ftr-reserve { height: 50px; margin: 0 22px; }

/* The fixed callback control. Green because it is the one action that is neither
 * the primary teal nor the hand-applied accent. */
.cb-btn {
  position: fixed; bottom: 0; right: 25px; z-index: 40;
  height: 34px; padding: 0 14px;
  border: 0; border-radius: var(--r-md) var(--r-md) 0 0;
  background: var(--callback); color: #fff;
  font: inherit; font-size: 14px; font-weight: var(--fw-semibold); cursor: pointer;
}

/* -------------------------------------------------------------- dialog */
/* THE hidden ATTRIBUTE HAS TO BE RESTORED HERE, and this rule is not optional.
 *
 * `hidden` is styled by the user agent as `display: none` at the LOWEST possible
 * specificity, so any class rule that sets `display` beats it. .dlg and .dlg-foot
 * both set `display: flex`, which meant the dialog rendered OPEN on every page
 * from first paint and could not be closed -- setDialog(false) sets .hidden, and
 * .hidden did nothing. Reported on the F4.2 review, 2026-09-04.
 *
 * Scoped to these three rather than declared globally as [hidden]{display:none}:
 * the un-rewritten templates still lay out under Bootstrap, and a global
 * !important on an attribute they also use is a change to pages this stage is
 * not rewriting.
 *
 * ANY FUTURE .dlg-* RULE THAT SETS display MUST BE ADDED HERE. */
.dlg[hidden], .dlg-scrim[hidden], .dlg-form[hidden],
.dlg-foot[hidden] { display: none; }

/* D1: every control is a 44px target. Six of seven fail today at every width and
 * the close is 12.6 x 21 -- the smallest target in the chrome. Criterion 25. */
.dlg-scrim { position: fixed; inset: 0; z-index: 70; background: var(--scrim-dialog); }
.dlg {
  position: fixed; z-index: 71; left: 50%; top: 48px; transform: translateX(-50%);
  width: min(var(--form-w), calc(100% - 32px));
  display: flex; flex-direction: column;
  background: var(--surface); border-radius: var(--r-lg);
  box-shadow: var(--sh-dialog); overflow: hidden;
}
/* THE HEIGHT CAP IS MEASURED, NOT CHOSEN. D1's 44px controls cost 95px against
 * today's dialog, and at 1010x700 -- the tightest viewport E5 measured, and it
 * is a laptop, not a phone -- the error state came to 713px against 604px of
 * room. So the BODY scrolls and the head does not: the submit and the consent
 * slot are on screen at every height, which is what criterion 28 requires.
 * The footer scrolls with the body -- the owner's call on review, 2026-09-01,
 * reversing the round's first answer. */
.dlg { max-height: calc(100vh - 96px); }
.dlg-head { flex: none; }
/* AND THE SCROLLER IS THE FORM, NOT THE BODY -- `S85`, 2026-09-06.
 *
 * The cap above was right and did nothing, because nothing under it could
 * shrink. .dlg-body is not a child of .dlg: .dlg-form sits between them, and a
 * plain block flex item takes its content height as its automatic minimum, so
 * the form refused to shrink below its 583px, the body was never squeezed, its
 * overflow-y never engaged, and .dlg's overflow:hidden took 44px off the
 * bottom. At 1010 x 700 -- E5's tightest viewport, and a laptop, not a phone --
 * 26px of the submit button were cut off and 18px were left showing.
 *
 * THE SIGNED-OFF MOCKUP HAD THIS RIGHT and the build did not: mockups/
 * dialog-build.py puts `min-height:0;flex:1;overflow-y:auto` on the state
 * wrapper, which is this element. The overflow was moved to .dlg-body somewhere
 * between the artefact and the template. ADR 0038: the mockup is the criterion.
 *
 * It also keeps the owner's call of 2026-09-01 -- the head is pinned and the
 * FOOTER SCROLLS WITH THE BODY, because both are inside the form. Measured
 * 2026-09-06, audit/2026-09-06-chrome-geometry.md section 5. */
.dlg-form { display: flex; flex-direction: column; min-height: 0; flex: 1;
  overflow-y: auto; }

.dlg-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  padding: 10px 10px 10px var(--s-5); border-bottom: 1px solid var(--rule);
}
.dlg-head h2 { margin: 0; font-size: 18px; font-weight: var(--fw-semibold); }
/* 44x44 against 12.6x21 today. It is the single worst target in the chrome and
 * the one a visitor reaches for first. */
.dlg-x {
  width: var(--target); height: var(--target); flex: none;
  border: 0; border-radius: var(--r); background: transparent; color: var(--sec);
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.dlg-x:hover { background: var(--ground); color: var(--ink); }
.dlg-x .ds-ic { width: 22px; height: 22px; }

/* No overflow of its own: the form scrolls, above. A nested scroller here can
 * never engage and only hides where the real one is. */
.dlg-body { padding: 18px var(--s-5) 4px; }
.dlg-intro { margin: 0 0 var(--s-4); font-size: 14px; color: var(--sec); }

.dlg-foot {
  margin-top: 14px; padding: 14px var(--s-5) 18px;
  border-top: 1px solid var(--rule);
  display: flex; flex-direction: column; gap: var(--s-3);
}
/* D7 and criterion 34: the submit is inert until consent is given, and it uses
 * C4's treatment -- the search submit carries the same rule. One idea, told the
 * same way in both places. The button primitive is uc-ds.css's. */
.dlg-go { width: 100%; }

.dlg-sent { padding-bottom: 18px; text-align: center; }
.dlg-tick { margin: 6px 0 10px; }
.dlg-tick .ds-ic { width: var(--target); height: var(--target); stroke: var(--ok); stroke-width: 2.4; }
.dlg-sent-h { margin: 0 0 6px; font-size: 19px; font-weight: var(--fw-semibold); }
.dlg-sent-p { margin: 0 0 6px; font-size: 14px; color: var(--sec); }

/* --------------------------------------------------------- 1200 and up */
/* THE ONE BREAKPOINT. chrome.md 3.1: no element's existence depends on a
 * breakpoint (C1), with a single stated exception -- the phone, which is in the
 * row where there is no drawer and the drawer's first item where there is, and
 * never on both at once (criterion 6). */
@media (min-width: 1200px) {
  .mh-burger { display: none; }
  .mh-phone { display: flex; }
  .mh-logo img.mh-logo-full { display: block; }
  .mh-logo img.mh-logo-mark { display: none; }
  .mh-search { flex: none; width: 380px; margin-left: auto; }
  .mm { display: block; }
  /* The drawer cannot be reached above 1200 -- there is no hamburger -- but a
     viewport resized while it is open would leave it hanging over the page. */
  .dw, .dw-scrim { display: none; }
}

/* ----------------------------------------------------------- below 768 */
@media (max-width: 767.98px) {
  .ftr-grid { grid-template-columns: 1fr; gap: 26px; padding: 24px var(--gutter) var(--s-4); }
  .ftr-bar { display: block; margin: 0 var(--s-4); }
  .ftr-mark { margin-bottom: var(--s-3); }
  .ftr-org, .ftr-copy, .ftr-legal { margin: 0 0 var(--s-2); }
  .ftr-copy { margin-left: 0; }
  .ftr-reserve { margin: 0 var(--s-4); }
  .dlg { top: var(--s-4); width: calc(100% - 20px); max-height: calc(100vh - 32px); }
  .dlg-body { padding: var(--s-4) var(--s-4) 4px; }
  .dlg-foot { padding: 14px var(--s-4) var(--s-4); }
}

/* --------------------------------------------------------------- motion */
/* Scoped to the chrome's own moving parts. A global `*` rule would reach every
 * un-rewritten page's Bootstrap collapse as well. */
@media (prefers-reduced-motion: reduce) {
  .mm-p, .dw, .dw-chev { transition: none; }
}

/* ------------------------------------------------------- a rewritten page */
/* The wrapper a template uses once it has stopped asking for Bootstrap's
 * .container. Only /callback/ uses it at F4.2; F4.3 to F4.6 bring the rest. */
/* THE SHELL IS THE ONE SHELL; the PROSE COLUMN is 720 and starts at the left.
 *
 * The owner's, 2026-09-10. `.uc-page` used to be 720 centred, so on a flatpage
 * or /callback/ the text began 230px right of the masthead's left edge and the
 * page read as a different width from every other one. Every shell now starts
 * where the chrome starts; what varies is how far the CONTENT runs, which is a
 * measure and not a page width.
 *
 * ~~AND THE TEXT IS NOT CAPPED~~ -- the owner's, 2026-09-10. A 720px column was
 * drawn first and rejected: «let text take all the width of a shell, production
 * doesn't have it and i find it fine». Production's Bootstrap ran prose the full
 * `.container`, so this is what the site has always done.
 *
 * WHAT KEEPS A FRAME IS A FORM, not prose -- the cart's `.ct-form` and the
 * callback page's, because a form is a column of controls and reads as an
 * object; a paragraph is not. */
.uc-page { max-width: var(--shell); margin: 0 auto;
  padding: var(--s-6) var(--gutter) var(--s-10); }
.uc-page h1 { font-size: 26px; font-weight: var(--fw-semibold); margin: 0 0 var(--s-3); }
.dlg-loading { color: var(--mut); font-size: 14px; }
