/* Malkai Design System — mid-office component layer (v0.1)
   ═══════════════════════════════════════════════════════════════════════════
   The operational surface: enquiry queue, enquiry record, itinerary builder.
   NOT the guest landing — that is an editorial surface with its own type scale,
   full-bleed photography and no operational chrome. It links styles.css (tokens
   + fonts) and nothing from this file. Keep it that way; ds:lint's surface-leak
   rule watches the typography half of the same boundary.

   Provenance: lifted verbatim from design-system/index.html's <style> block,
   where this layer already existed but was never exported — styles.css was 11
   lines and shipped no components at all, so every prototype re-implemented a
   button. Four independent buttons, 1294 lines of inline CSS.

   Three reconciliations were made during the lift, each closing a real defect:
     · .mk-btn gains `gap: 6px` (present in enquiry + builder, absent in the
       showcase's copy).
     · .mk-btn-sm gains the ::after hit expander from enquiry-inbox. Without it a
       36px button is below --mk-tap-min on touch — spec finding A9.
     · .mk-btn-primary gains both blocked states. `[aria-disabled="true"]` is not
       decoration: it keeps the Send button focusable so a keyboard user can
       reach it and learn why it is blocked (spec finding A4). Never replace it
       with `disabled`.

   Everything here is built on the semantic aliases (--mk-accent, --mk-text,
   --mk-surface-*, --mk-hairline) rather than the raw brand tokens, which is what
   makes [data-theme="dark"] actually work. A page that writes --mk-camel-dark
   directly will not remap. ds:lint's semantic-alias rule catches that.

   Usage: <link rel="stylesheet" href="../../design-system/styles.css">
          <link rel="stylesheet" href="../../design-system/components.app.css">
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Utilities ───────────────────────────────────────────────────────────── */
.mk-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* [hidden] must WIN. Any authored `display` on the element silently defeats the
   attribute — this bit three separate times before it earned a component-layer
   rule (payment's .gen-claims and .sync-list, the library's Clear button, each
   discovered live as "why is this rendering"). A rule earned in one place is
   carried to every place it applies. */
[hidden] { display: none !important; }

/* A jump must land visibly (A2/C6 — the rule was written in the builder's own CSS
   and still broken three times: a scroll alone reads as "the button did nothing").
   Every jump in the product goes through shell.js's mkLandOn(), which scrolls and
   stamps this class on the landing element. Self-clearing animation; colour is not
   the signal — motion + the outline are. */
.mk-flash { animation: mk-flash 1.6s ease-out 1; }
@keyframes mk-flash {
  0%, 55% { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
  100%    { outline: 2px solid transparent; outline-offset: 2px; }
}

/* ── Arrangement — the layer this system did not have ────────────────────────
   Earned 2026-07-14, from the iteration audit. Ten review rounds, and
   LAYOUT/GROUPING is the only category that never decays: 5 → 10 → 8 → 7 → 4 → 3
   → 8 → 7 → 3 → 6, a flat line across five days, ~51% of every round's findings.
   Every category that DID decay — icons, colour, the `·` separator, the tag — did
   so the same way: the rule stopped being prose and became a component. Composition
   had no component to become. So a grouping finding could only land as an edit to
   one page's CSS, and the next surface (phone shell → inbox cell → room row → stay
   card) re-earned it honestly. «Опять та же ошибка» was not inattention. It was a
   missing layer.

   The measurement that named it: 425 raw-px spacing declarations across the eight
   prototypes and ZERO uses of --mk-space-*. The scale has existed since v0.1 and
   was used exactly never — because the lint allowlist is wider than the scale, so
   raw px always passed. An optional token is not a token. (The same law as lint:ds
   itself: prose is lost, counted checks hold.)

   These five primitives are not new inventions — they are the shapes the eight
   pages already wrote 124 times by hand (gap:10px ×23, gap:8px ×22, gap:12px ×16,
   flex-direction:column ×24, align-items:center ×45). Naming them is what lets a
   grouping rule travel. Every gap here comes from the scale; a page that needs a
   number the scale does not have is a page making a composition claim, and it says
   so out loud in the concepts file. */

/* Vertical flow. The default relationship between stacked things. */
.mk-stack       { display: flex; flex-direction: column; gap: var(--mk-space-3); }
.mk-stack-tight { gap: var(--mk-space-2); }
.mk-stack-loose { gap: var(--mk-space-5); }
/* N2: a stack of two facts that read as one block wants hairline air (4px) — and a
   stack holding a header's right edge aligns its lines to the END, not the start. */
.mk-stack-hair  { gap: var(--mk-space-1); }
.mk-stack-end   { align-items: flex-end; }
/* …and a stack that CENTRES its children on the column: the empty/settled face a panel
   shows when there is nothing to act on — a medallion over its line (07-20). */
.mk-stack-center { align-items: center; }
/* A vertical hairline BETWEEN items in a row — it takes its height from the row, so it
   never has to be measured. Use it where two clusters share a header and belong to
   different objects (07-20: the record's owner and the record's status). */
.mk-rule-y { flex: none; align-self: stretch; width: 1px; background: var(--mk-hairline); }
/* A stack that FILLS its parent's height and gives every child an equal share of it, so a
   column of panels ends on one line however much each has to say (07-18: the builder's rail —
   To confirm · Approvals · Prices). `min-height:0` on the children is the load-bearing part:
   without it a flex child refuses to shrink below its content and the column overruns the
   viewport instead of dividing it. Pair with .mk-widget-fill so each share scrolls internally. */
.mk-stack-fill  { height: 100%; overflow: hidden; }
.mk-stack-fill > * { flex: 1 1 0; min-height: 0; }

/* Horizontal flow that WRAPS. Things that sit beside each other and, when the
   phone runs out of room, go under each other rather than squeezing. */
.mk-cluster       { display: flex; flex-wrap: wrap; align-items: center; gap: var(--mk-space-2); }
.mk-cluster-tight { gap: var(--mk-space-1); }
.mk-cluster-loose { gap: var(--mk-space-4); }
/* A cluster whose items are different HEIGHTS and must hang from the same top edge — an
   avatar beside a person's name and the stack of facts under it (G2, the builder's roster).
   Centring there floats the avatar to the middle of the paragraph beside it. */
.mk-cluster-top   { align-items: flex-start; }

/* The two-end row: something on the left, something on the right. THE cure for the
   label-left/value-right disease (M8, M6-11 — «я не знаю, что мне, на колени
   стать?»): below the stack threshold it does not squeeze the value into a wrapping
   column, it stacks label ABOVE value, which is what a phone wants and what was
   dictated three separate times. The behaviour lives here once instead of being
   re-decided per page. */
.mk-split {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--mk-space-3); flex-wrap: wrap;
}
.mk-split > * { min-width: 0; }
@media (max-width: 520px) {
  .mk-split { flex-direction: column; align-items: stretch; gap: var(--mk-space-1); }
}
/* Opt out when the pair genuinely fits — a short label and a short value. */
.mk-split-hold { flex-direction: row; align-items: baseline; gap: var(--mk-space-3); }

/* Responsive column field. Set --mk-grid-min per use; the column count follows the
   width instead of being asserted at a breakpoint. Replaces seven bespoke
   grid-template-columns declarations, no two of which agreed. */
.mk-grid {
  display: grid; gap: var(--mk-space-4);
  grid-template-columns: repeat(auto-fit, minmax(var(--mk-grid-min, 220px), 1fr));
}

/* One widget body read as TWO columns with a hairline between them. Born inside the
   builder's Enquiry widget (the brief beside the guests) and asked for a second time
   on the record's Enquiry details (the facts beside the communication) — the second
   ask is what makes it a primitive instead of one more page's own grid.
   The divider is INSET from the band's edges, never a full-bleed cut: it separates two
   readings, it does not chop the card in half. Set --mk-panes-split where the two do
   not deserve equal room. Under 768 the columns stack and the hairline lies down with
   them, or a vertical rule would be drawing a column that is no longer there. */
.mk-panes { display: grid; gap: 0; grid-template-columns: var(--mk-panes-split); }
.mk-pane { min-width: 0; padding: 14px 20px 16px; }
.mk-pane + .mk-pane { position: relative; }
.mk-pane + .mk-pane::before { content: ""; position: absolute; left: 0; top: 12px; bottom: 12px;
                              width: 1px; background: var(--mk-hairline); }
@media (max-width: 767px) {
  .mk-panes { grid-template-columns: minmax(0,1fr); }
  .mk-pane + .mk-pane::before { left: 20px; right: 20px; top: 0; bottom: auto; width: auto; height: 1px; }
}

/* A named group with a head — one zone, one job (his round-M ask: the rail is not a
   scroll of rows, it is separate WIDGETS). The head is what makes a group a group;
   a stack of rows with no head is the thing that keeps reading as soup. */
/* THE CARD DRESS — one frame for every container. A widget IS a card (his call at
   assembly, 07-17). The dress is 1px solid --mk-hairline-mid + --mk-radius-md,
   --mk-shadow-card when the card stands on the page ground. His 08-02 call: the plain
   hairline is too faint to hold a frame — it stays the ink of separators INSIDE a
   surface; the mid step is the frame (the template cards had already found it).
   Cards, widgets and tables all wear it — two frame weights on one page read as two
   families of boxes. --mk-hairline-strong is not a container colour: it belongs to
   instruments (form controls, menus, popovers, dialogs, .mk-add-row) and to the dash. */
.mk-widget {
  border: 1px solid var(--mk-hairline-mid); border-radius: var(--mk-radius-md);
  /* The widget is a PLATE, in both themes. It was a well on dark for one day — see
     --mk-surface-inset in tokens.css for why that came back out: the theme's deepest value
     is also the page's, so the well read as a hole rather than a container. */
  background: var(--mk-surface-inset); overflow: hidden;
}
/* The head owns the ARRANGEMENT — title left, hint/action right, baseline-aligned — and
   deliberately not the typography. A primitive that also dictated the title's face would
   silently restyle every surface it was migrated onto, which is how a "pure refactor" becomes
   an unasked-for redesign. Type stays with the component or the page. */
.mk-widget-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--mk-space-3);
}
.mk-widget-head > :first-child { margin: 0; }
/* A visibly separate, consistently sized widget header. Use this when the title
   belongs to the card chrome rather than the scrolling/body content.
   07-20: the height became a variable. A page that stands a widget beside the record's
   own card heads (the property card in the builder) needs the two bands to agree — the
   eye reads a 48 next to a 64 as a mistake, not a hierarchy. The default stays 48; a
   surface opts into the taller band by setting `--mk-widget-head-h` on the widget. */
.mk-widget-head-framed {
  box-sizing: border-box; height: var(--mk-widget-head-h, 48px); padding: 0 14px;
  align-items: center; flex: none; border-bottom: 1px solid var(--mk-hairline);
}
/* The compact operational widget title. It sits between body copy and the 20px
   panel-title step: enough hierarchy to name the zone without competing with a
   property or page title. It uses the standard text ink so every card header is
   equally legible (`#1D1F1D` in the light theme). */
.mk-widget-title {
  margin: 0; font-family: var(--mk-font-app); font-size: 18px; line-height: 1.3;
  font-weight: 600; color: var(--mk-text-muted);
}
.mk-widget-body { min-width: 0; }
/* A widget given a fixed height: the head STAYS and the body scrolls inside it, so the widget
   never grows past its share. `.mk-widget-scroll` marks the part that moves. */
.mk-widget-fill { display: flex; flex-direction: column; }
.mk-widget-fill > .mk-widget-head { flex: none; }
/* The widget's own answer when content does not fit (Ihor, 08-11): a full-width
   foot control that extends the zone — promoted from the builder's page-local
   .wl-more. The chevron answers hover in the accent, the stay header's own
   manner; aria-expanded flips it. */
.mk-widget-more { display: flex; width: 100%; align-items: center; justify-content: center;
                  gap: 6px; padding: 8px 0; border: 0; border-top: 1px solid var(--mk-hairline);
                  background: none; cursor: pointer; color: var(--mk-text-muted);
                  font: inherit; font-size: var(--mk-text-sm); }
.mk-widget-more:hover, .mk-widget-more.is-hover { background: var(--mk-wash-faint); color: var(--mk-accent); }
.mk-widget-more:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-widget-more svg { width: 14px; height: 14px; flex: none; fill: currentColor; stroke: none;
                      transition: transform .15s; }
.mk-widget-more[aria-expanded="true"] svg { transform: rotate(180deg); }
.mk-widget-scroll { flex: 1 1 auto; min-height: 0; overflow-x: hidden; overflow-y: auto;
                    overscroll-behavior: contain; }

/* Scroll cue — a real overlay element rather than a styled native scrollbar.
   macOS may suppress native scrollbars even when their WebKit pseudo-element is
   styled, which made the previous hover cue invisible. The overlay is quiet at
   rest, 4px on hover/focus, 6px while moving, and never reserves layout space. */
:where(.mk-widget-scroll, .mk-person-list-scroll, .mk-scroll-cue) {
  position: relative; scrollbar-width: none;
}
:where(.mk-widget-scroll, .mk-person-list-scroll, .mk-scroll-cue)::-webkit-scrollbar {
  width: 0; height: 0;
}
.mk-scroll-indicator {
  position: absolute; z-index: 4; right: 2px; width: 4px; min-height: 32px;
  border-radius: 999px; background: var(--mk-hairline-strong); opacity: 0;
  pointer-events: none; transition: opacity .18s ease, width .18s ease,
    right .18s ease, background-color .18s ease;
}
:where(.mk-widget-scroll, .mk-person-list-scroll, .mk-scroll-cue).mk-has-scroll:hover > .mk-scroll-indicator,
:where(.mk-widget-scroll, .mk-person-list-scroll, .mk-scroll-cue).mk-has-scroll:focus-within > .mk-scroll-indicator {
  opacity: .9;
}
:where(.mk-widget-scroll, .mk-person-list-scroll, .mk-scroll-cue).mk-is-scrolling > .mk-scroll-indicator {
  right: 1px; width: 6px; opacity: 1; background: var(--mk-text-muted);
}

/* ── Typography roles ────────────────────────────────────────────────────── */
/* A schedule date is a dedicated date figure, not a screen title. Its 24px / 500 face
   and tabular numerals leave the generic 24px / 600 Inter heading unchanged. */
.mk-schedule-date {
  font-family: var(--mk-font-app); font-size: var(--mk-text-lx); font-weight: 500;
  line-height: 1.2; color: var(--mk-text-muted);
  font-variant-numeric: lining-nums tabular-nums;
}

/* ── Buttons ─────────────────────────────────────────────────────────────── */
/* the control voice — Inter 500 at 14px, the same one the nav links, a select's
   value and .mk-field-label wear. It carried .02em of its own until 07-29. */
.mk-btn { font-family: var(--mk-font-app); font-size: var(--mk-text-sm); font-weight: 500;
          display: inline-flex; align-items: center; justify-content: center; gap: 6px;
          min-height: var(--mk-btn-height); padding: 10px 20px;
          border-radius: var(--mk-radius-sm); border: 1px solid transparent; cursor: pointer;
          background: transparent; color: var(--mk-text-muted); text-decoration: none;
          white-space: nowrap; }
/* Documented states. A reference page has to SHOW hover and focus, not ask the reader
   to produce them — but a page that paints its own copy of the hover face drifts from
   this file the first time the face changes. So the component states them once and
   answers to both: the pseudo-class the browser sets, and the class the documentation
   sets. `.is-hover` and `.is-focus` are documentation hooks; product markup never
   carries them. (Ihor, 08-05 — the states matrix.) */
.mk-btn:focus-visible,
.mk-btn.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }

/* Optional leading icon. `order:-1` makes the component contract defensive: even if a
   runtime template writes the glyph after its label, Primary and Secondary still follow
   the workspace's icon-left reading order. Buttons without the slot keep their exact
   geometry; the slot scales with the shared button size ladder. */
.mk-btn > .mk-btn-icon { order: -1; width: 16px; height: 16px; flex: none;
                         fill: none; stroke: currentColor;
                         stroke-width: var(--mk-icon-stroke);
                         stroke-linecap: butt; stroke-linejoin: miter; }
.mk-btn.mk-btn-xs > .mk-btn-icon { width: 12px; height: 12px; }
.mk-btn:is(.mk-btn-s, .mk-btn-sm) > .mk-btn-icon { width: 14px; height: 14px; }
.mk-btn.mk-btn-m > .mk-btn-icon { width: 16px; height: 16px; }
.mk-btn.mk-btn-l > .mk-btn-icon { width: 18px; height: 18px; }
.mk-btn.mk-btn-xl > .mk-btn-icon { width: 20px; height: 20px; }

.mk-btn-primary { background: var(--mk-accent-fill); color: var(--mk-accent-contrast); }
/* Hover is a VALUE, not a filter (Ihor, 08-19: «мало затемняется кнопка primary», said
   of both themes). `brightness(.94)` moved the light face 1.13:1 from rest — a step
   nobody sees without the two sides touching — and on dark it multiplied a fill that
   had nowhere to go. --mk-accent-fill-hover is measured against rest in each theme
   instead, at 1.34:1 and 1.32:1. The 1px edge in the REST fill comes with it: on dark
   the sunken face alone reads 2.86:1 on a card, under the 3:1 floor a shape answers to,
   and the outline keeps the button's own boundary at 3.78:1 while the plate inside it
   sinks. On light both values are dark browns, so the edge is invisible there and the
   rule stays one rule. */
/* The guard is not decoration. `.mk-btn-primary:hover` and `.mk-btn-primary[aria-
   disabled="true"]` carry the SAME specificity, so the blocked rule below won its
   background only by being later in the file — and won nothing at all for the edge this
   rule adds, which is how a blocked primary came to grow a gold rim under the pointer
   (Ihor, 08-19: «в блок состоянии при наведении появляется всё равно жёлтый ховер»). A
   state that is not accepting the press does not answer the pointer, so it is excluded
   here rather than repaired afterwards. */
.mk-btn-primary:hover:not([aria-disabled="true"], :disabled, [aria-busy="true"]),
.mk-btn-primary.is-hover:not([aria-disabled="true"], :disabled, [aria-busy="true"]) {
                           background: var(--mk-accent-fill-hover);
                           border-color: var(--mk-accent-fill); }

/* Secondary wears the brand-gold outline (Ihor's verdict, 07-15, the add-hotel round —
   tried on the concept sheet, approved for the layer). Softened accent, not full-strength:
   an outline is a border, not a fill. */
/* the edge takes the PRIMARY's fill value, so the two tiers read as one family — a
   border is a shape (3:1 floor) and #8C6E05 clears it at 3.78:1, where the same value
   as a label would not (Ihor asked for this twice, 08-18) */
.mk-btn-secondary { border-color: var(--mk-accent-fill); }
/* the icon follows the label instead of a hardcoded --mk-black, which measured
   1.15:1 on the dark card — invisible (found 08-18 while matching the buttons) */
.mk-btn-secondary > .mk-btn-icon { stroke: currentColor;
                                    stroke-width: calc(var(--mk-icon-stroke) + .25); }
/* The wash alone left the edge unchanged, which is most of what a secondary IS
   (Ihor, 08-19: «не меняется цвет border, хотя можно было бы поменять»). The edge now
   answers the pointer in the direction its ground allows: deeper on light, brighter on
   dark, both carried by --mk-accent-edge-hover. */
.mk-btn-secondary:hover:not([aria-disabled="true"], :disabled),
.mk-btn-secondary.is-hover:not([aria-disabled="true"], :disabled) {
                             background: var(--mk-accent-wash);
                             border-color: var(--mk-accent-edge-hover); }
/* blocked secondary: the gold leaves, the hover dies (Ihor 07-16 — the Reserved slot
   button); the label speaks QUIET, like the blocked primary's (08-01 — muted went
   near-black in the ink pass and stopped reading as blocked) */
.mk-btn-secondary[aria-disabled="true"] { border-color: var(--mk-hairline-strong);
                                          color: var(--mk-text-quiet); cursor: not-allowed; }
.mk-btn-secondary:is(:disabled, [aria-disabled="true"]) > .mk-btn-icon { stroke: currentColor;
                                                                        stroke-width: var(--mk-icon-stroke); }
.mk-btn-secondary[aria-disabled="true"]:hover,
.mk-btn-secondary[aria-disabled="true"].is-hover { background: none; }
[data-theme="dark"] .mk-btn-secondary > .mk-btn-icon { stroke: currentColor;
                                                       stroke-width: var(--mk-icon-stroke); }

/* The QUIET tier is the editorial link (round Q, 07-12, concepts-quiet-button.md).
   Two treatments are dead and may not return: the dashed outline (C15, 07-11 — dashes mean
   emptiness, never press-me) and the light accent fill / background hover (07-12 — «не под
   наш стиль… ховер на бэкграунде — херня»). A quiet control is accent ink on nothing; hover
   slides in a hairline underline and deepens the ink — the pressed-state language of every
   text link since the web began. Lineage: tertiary / text button. Padding is a
   modifier of this tier, not another kind of button. */
.mk-btn-ghost { color: var(--mk-accent-ink-strong); }
.mk-btn-ghost:hover,
.mk-btn-ghost.is-hover { background: none; text-decoration: underline;
                      text-underline-offset: 4px; text-decoration-thickness: 1px;
                      filter: brightness(.92); }

/* Optional padding property. A quiet button may sit flush with a fact line without
   becoming a separate tier; its 44px height and all interaction states remain intact. */
.mk-btn-flush { padding: 0; }

/* Legacy alias retained for existing prototypes while their markup migrates. New work
   uses `.mk-btn mk-btn-ghost mk-btn-flush` so priority and padding stay orthogonal. */
.mk-btn-quiet { display: inline-flex; align-items: center; gap: 6px; border: 0; background: none;
                padding: 2px 4px; font-family: var(--mk-font-app); font-size: var(--mk-text-sm);
                font-weight: 600; color: var(--mk-accent-ink-strong); cursor: pointer; position: relative;
                border-radius: var(--mk-radius-sm); }
.mk-btn-quiet::after { content: ""; position: absolute; inset: -10px -6px; } /* 44px touch floor */
.mk-btn-quiet:hover,
.mk-btn-quiet.is-hover { text-decoration: underline; text-underline-offset: 4px;
                      text-decoration-thickness: 1px; filter: brightness(.92); }
.mk-btn-quiet:focus-visible,
.mk-btn-quiet.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
/* Blocked quiet controls. Two gaps the states matrix found (08-05): the Quiet
   tier had no blocked face at all, and the legacy alias answered only `:disabled` — the
   one attribute this system tells you never to use, because it takes the control out of
   the tab order and the reason with it. Both now answer aria-disabled, the way Secondary
   already did. No prototype blocks a quiet control yet, so nothing shipped changes. */
.mk-btn-ghost[aria-disabled="true"] { color: var(--mk-text-quiet); cursor: not-allowed; }
.mk-btn-ghost[aria-disabled="true"]:hover,
.mk-btn-ghost[aria-disabled="true"].is-hover { text-decoration: none; filter: none; }

.mk-btn-quiet[aria-disabled="true"] { color: var(--mk-text-quiet); cursor: not-allowed; }
.mk-btn-quiet[aria-disabled="true"]:hover,
.mk-btn-quiet[aria-disabled="true"].is-hover { text-decoration: none; filter: none; }

.mk-btn-quiet:disabled { color: var(--mk-text-muted); opacity: .45; cursor: not-allowed;
                         text-decoration: none; filter: none; }

/* Terminal action for an itinerary timeline. This is deliberately its own component,
   not a page-local ghost button: the 24px node masks the connector and the registered
   plus SVG stays mathematically centred across every Builder state. */
.mk-add-stay {
  position: relative; z-index: 1; display: inline-flex; flex-direction: column;
  align-items: center; justify-content: flex-start; gap: 5px;
  min-width: 58px; min-height: var(--mk-tap-min); padding: 0;
  border: 0; border-radius: var(--mk-radius-sm); background: none;
  font-family: var(--mk-font-app); font-size: var(--mk-text-sm); font-weight: 500;
  line-height: 18px; color: var(--mk-accent); cursor: pointer; text-decoration: none;
}
.mk-add-stay-mark {
  display: grid; place-items: center; width: 24px; height: 24px; flex: none;
  border: 1px solid currentColor; border-radius: 50%; background: var(--mk-surface);
}
.mk-add-stay-icon {
  display: block; width: 16px; height: 16px; fill: none; stroke: currentColor;
  stroke-width: var(--mk-icon-stroke); stroke-linecap: butt; stroke-linejoin: miter;
}
.mk-add-stay-label { white-space: nowrap; }
.mk-add-stay:hover { text-decoration: none; }
.mk-add-stay:hover .mk-add-stay-label,
.mk-add-stay.is-hover .mk-add-stay-label {
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px;
}
/* 08-10, his read: the 12% wash was invisible on the grey ground the timeline stands
   on — the hover answered in a whisper. The node now fills at 24% accent, a fill the
   eye actually catches, while the ink and geometry hold still. */
.mk-add-stay:hover .mk-add-stay-mark,
.mk-add-stay.is-hover .mk-add-stay-mark {
  background: color-mix(in srgb, var(--mk-accent) 24%, var(--mk-surface));
}
.mk-add-stay:focus-visible,
.mk-add-stay.is-focus {
  outline: 2px solid var(--mk-camel-mid); outline-offset: 3px;
}
/* 07-30, his read: the disabled node was the loudest thing in a row it is not part of.
   The sunken cream FILLED it, which is what an active control does, and the ring —
   1px of currentColor — outweighed the label beside it. Off state now reads as one
   quiet family: no fill at all, the ring steps back to the hairline, and mark and label
   keep the same muted ink. The fill can leave safely because the timeline rail was
   taught to stop at this node's border instead of running under it — it is no longer
   load-bearing as a mask. */
.mk-add-stay[aria-disabled="true"] { color: var(--mk-text-muted); cursor: not-allowed; }
.mk-add-stay[aria-disabled="true"] .mk-add-stay-mark { background: none; border-color: var(--mk-hairline); }
.mk-add-stay[aria-disabled="true"]:hover .mk-add-stay-label { text-decoration: none; }
.mk-add-stay[aria-disabled="true"]:hover .mk-add-stay-mark { background: none; }

/* A contextual action borrows the ink of the state plate it lives inside: an active hold,
   a warning gate, a confirmed supplier. The geometry still comes from .mk-btn;
   only the local semantic tone is supplied through --mk-btn-context.
   This replaces one-off `in-main` / `in-ghost` faces in the Builder payment widget. */
.mk-btn-context { --mk-btn-context: var(--mk-accent); color: var(--mk-btn-context); }
.mk-btn.mk-btn-context {
  border-color: color-mix(in srgb, var(--mk-btn-context) 60%, transparent);
  background: transparent;
}
.mk-btn.mk-btn-context:hover {
  background: color-mix(in srgb, var(--mk-btn-context) 10%, transparent);
  filter: none;
}
.mk-btn-ghost.mk-btn-context:hover,
.mk-btn-quiet.mk-btn-context:hover {
  color: var(--mk-btn-context); background: transparent;
}

.mk-btn-destructive { color: var(--mk-error-text); border-color: var(--mk-error-edge); }
.mk-btn-destructive:hover,
.mk-btn-destructive.is-hover { background: color-mix(in srgb, var(--mk-error) 6%, transparent); }

/* Generic blocked state: fade the affordance. */
.mk-btn[disabled] { opacity: .38; cursor: not-allowed; }

/* A filled primary must not merely fade — a 38%-opacity camel still reads as camel.
   It swaps the fill for a solid NEUTRAL GREY plate instead (his catch, 07-23: the old
   surface-sunken fill dissolved into any sunken band and the button read as bare text —
   a blocked primary must still read as a filled button, just grey). `opacity: 1` because
   the generic rule above also matches. `[aria-disabled="true"]` is not decoration: it
   keeps the button in the tab order so a keyboard user can reach it and hear why it is
   blocked. `disabled` would remove it and the blocking rule would be invisible to anyone
   not using a mouse (spec finding A4). */
.mk-btn-primary[aria-disabled="true"],
.mk-btn-primary:disabled { background: color-mix(in srgb, var(--mk-text) 19%, var(--mk-surface));
                           /* QUIET ink, not muted (his 08-01 catch on the specimen): when Body
                              darkened to .88 the blocked label came out near-black and the
                              button read enabled-but-grey. A blocked action speaks in the
                              absence register. */
                           color: var(--mk-text-quiet);
                           cursor: not-allowed; filter: none; opacity: 1; }
/* BUSY is not BLOCKED (Ihor 07-25, concept B of busy-button-concepts.html): a working
   primary keeps its own colour, pressed one shade toward the ink — the grey above stays
   reserved for blocked-with-a-reason. Pair with .mk-btn-spin inside the label.
   The face is a TOKEN per theme, not a mix toward --mk-text: that mix reads "one shade
   toward the ink" only where the ink is dark. On the dark theme --mk-text is near-white
   and the same expression LIFTED the pale ink accent to #D7C898 — a washed plate under a
   near-white label, with the loader's ring at 1.4:1 against it. Neither read (Ihor,
   08-19: «не видно ни лоадера, ни текста»). */
.mk-btn-primary[aria-busy="true"] { background: var(--mk-accent-busy);
                                    border-color: var(--mk-accent-fill);
                                    color: var(--mk-accent-contrast); cursor: progress; }
/* the in-button loader ring — inked from currentColor so it reads on any button family */
.mk-btn-spin { width: 16px; height: 16px; flex: none; box-sizing: border-box;
               border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
               border-top-color: currentColor; border-radius: 50%;
               animation: mk-btn-spin .8s linear infinite; }
@keyframes mk-btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .mk-btn-spin { animation: none; } }

/* Button size scale. The unmodified .mk-btn remains M for compatibility; explicit
   modifiers make the size deliberate in new work. S keeps a compact 36px face but
   expands its hit area, while L and XL are the mobile/prominent actions. */
.mk-btn-xs { min-height: var(--mk-btn-height-xs); padding: 3px 10px;
             font-size: var(--mk-text-xs); position: relative; }
.mk-btn-xs::after { content: ""; position: absolute; inset: -8px 0; }
.mk-btn-s, .mk-btn-sm { min-height: var(--mk-btn-height-s); padding: 6px 14px;
                        font-size: var(--mk-text-sm); position: relative; }
.mk-btn-s::after, .mk-btn-sm::after { content: ""; position: absolute; inset: -6px 0; }
.mk-btn-m { min-height: var(--mk-btn-height-m); padding: 10px 20px;
            font-size: var(--mk-text-sm); }
.mk-btn-l { min-height: var(--mk-btn-height-l); padding: 12px 24px;
            font-size: var(--mk-text-sm); }
.mk-btn-xl { min-height: var(--mk-btn-height-xl); padding: 15px 28px;
             font-size: var(--mk-text-md); }

/* Split button: the named left face performs the current action; only the separated
   chevron face opens the attached menu. Both faces inherit one priority and size, while
   the shared edge stays visible enough to make the two hit targets unambiguous. */
.mk-split-btn { display: inline-flex; align-items: stretch; }
.mk-split-btn > .mk-btn { border-radius: 0; }
.mk-split-btn > .mk-split-btn-action {
  border-radius: var(--mk-radius-sm) 0 0 var(--mk-radius-sm);
}
.mk-split-btn > .mk-split-btn-toggle {
  flex: none; padding-inline: 0;
  border-left-color: color-mix(in srgb, currentColor 32%, transparent);
  border-radius: 0 var(--mk-radius-sm) var(--mk-radius-sm) 0;
}
.mk-split-btn-action > svg { width: 14px; height: 14px; flex: none; fill: none;
                             stroke: currentColor; stroke-width: var(--mk-icon-stroke);
                             stroke-linecap: butt; stroke-linejoin: miter; }
.mk-split-btn-toggle > .mk-split-btn-chevron { width: 12px; height: 12px;
                                               fill: currentColor; stroke: none;
                                               transition: transform .15s ease; }
.mk-split-btn-toggle[aria-expanded="true"] > .mk-split-btn-chevron {
  transform: rotate(180deg);
}
.mk-split-btn-toggle.mk-btn-xs { width: var(--mk-btn-height-xs); }
.mk-split-btn-toggle.mk-btn-s,
.mk-split-btn-toggle.mk-btn-sm { width: var(--mk-btn-height-s); }
.mk-split-btn-toggle.mk-btn-m { width: var(--mk-btn-height-m); }
.mk-split-btn-toggle.mk-btn-l { width: var(--mk-btn-height-l); }
.mk-split-btn-toggle.mk-btn-xl { width: var(--mk-btn-height-xl); }
/* Chevron steps up one seat per size — the old 12/14/16/18 read lost in its face,
   worst at S (his 08-02 call: «иконку шеврона нужно увеличить, особенно в маленьких»). */
.mk-split-btn-toggle.mk-btn-xs > .mk-split-btn-chevron { width: 12px; height: 12px; }
.mk-split-btn-toggle:is(.mk-btn-s, .mk-btn-sm) > .mk-split-btn-chevron { width: 14px; height: 14px; }
.mk-split-btn-toggle.mk-btn-m > .mk-split-btn-chevron { width: 16px; height: 16px; }
.mk-split-btn-toggle.mk-btn-l > .mk-split-btn-chevron { width: 18px; height: 18px; }
.mk-split-btn-toggle.mk-btn-xl > .mk-split-btn-chevron { width: 20px; height: 20px; }

/* ── Toast — brief system feedback, never the durable home of a state ────── */
/* It hangs BELOW the header band, not over it (Ihor, 07-28, on the builder: at
   20px the status toast landed square on the header's own status and its buttons
   — the control she just pressed disappeared under the word about pressing it).
   The floor was tried the same hour and read as gone: at the bottom edge of a
   1600px canvas the message is outside where she is looking. --mk-toast-top
   carries the clearance for every shell page at once. */
.mk-toast-region { position: fixed; top: var(--mk-toast-top); right: 20px; z-index: 90;
                   width: min(380px, calc(100vw - 40px)); pointer-events: none; }
.mk-toast { display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px;
            background: var(--mk-surface); color: var(--mk-text-muted);
            border: 1px solid var(--mk-hairline-strong); border-left: 3px solid var(--mk-success-text);
            /* the rail's corners are square — a 3px edge under a rounded corner shows
               a sliver of ground through the gap (his 08-02 rule) */
            border-radius: 0 var(--mk-radius-sm) var(--mk-radius-sm) 0; box-shadow: var(--mk-shadow-pop);
            font-family: var(--mk-font-app); font-size: var(--mk-text-sm); line-height: 1.45;
            pointer-events: auto; }
.mk-toast + .mk-toast { margin-top: 8px; }
.mk-toast .ico { width: 18px; height: 18px; flex: none; color: var(--mk-success-text);
                 fill: none; stroke: currentColor; stroke-width: var(--mk-icon-stroke);
                 stroke-linecap: butt; stroke-linejoin: miter; }
.mk-toast.is-warning { border-left-color: var(--mk-warning); }
.mk-toast.is-warning .ico { color: var(--mk-warning-text); }
.mk-toast.is-error { border-left-color: var(--mk-error); }
.mk-toast.is-error .ico { color: var(--mk-error-text); }
.mk-toast-copy { min-width: 0; }
.mk-toast-copy strong { display: block; color: var(--mk-text); font-weight: 600; }
.mk-toast-copy span { display: block; margin-top: 2px; }
@media (max-width: 767px) {
  /* the phone keeps the same clearance token — its screen header is shorter than
     the desktop band, so the message simply sits under it */
  .mk-toast-region { left: 12px; right: 12px; width: auto; }
}

/* M6-7 (07-12 — the review's most-repeated instruction: «опять та же ошибка…
   десять раз»): on the phone an action is a FULL-WIDTH tap target. The rule
   failed four times as a dictated instruction; as the component's default it
   cannot fail. Sibling buttons stack — they never shrink side by side.
   .mk-btn-inline is the deliberate, written opt-out for a control that
   genuinely shares a row (a small action beside a title). */
@media (max-width: 767px) {
  .mk-btn { width: 100%; }
  .mk-btn-inline { width: auto; }
}

/* ── Icon button ─────────────────────────────────────────────────────────────
   The weakest thing in the system before 2026-07-09: the builder's ↑ ↓ arrows measured
   21×21px and its × was 19×20px, drawn in muted ink with no border and no hover surface.
   ds:lint:visual now measures both the hit area and the icon's own contrast (3:1 — the
   non-text floor), which is the same legibility rule we already applied to text.

   Action glyphs use --mk-text: a control is not a caption. Disclosure triangles are the
   deliberate exception — they are quiet structural markers, so every one rests in
   --mk-text-muted whether it sits at the left or right edge. The visual box is 32px so a
   toolbar stays dense; ::after carries it to 44px for a thumb. It always has an aria-label
   naming the OBJECT — never a bare "Remove" (spec finding A5). */
.mk-icon-btn {
  display: inline-flex; align-items: center; justify-content: center; position: relative;
  width: 32px; height: 32px; flex: none; padding: 0;
  border: 1px solid transparent; border-radius: var(--mk-radius-sm);
  background: transparent; color: var(--mk-text-muted);
  font: inherit; font-size: 16px; line-height: 1; cursor: pointer;
}
.mk-icon-btn::after { content: ""; position: absolute; inset: -6px; } /* 32px → 44px on touch */
.mk-icon-btn:hover,
.mk-icon-btn.is-hover,
.mk-icon-btn[aria-haspopup="menu"][aria-expanded="true"] {
  background: var(--mk-surface-sunken);
  border-color: var(--mk-accent);
}
.mk-icon-btn:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-icon-btn:disabled { color: var(--mk-text-muted); opacity: .45; cursor: not-allowed; }
.mk-icon-btn:disabled:hover { background: transparent; border-color: transparent; }
.mk-icon-btn svg { width: 16px; height: 16px; fill: none; stroke: currentColor;
                   stroke-width: var(--mk-icon-stroke);
                   stroke-linecap: butt; stroke-linejoin: miter; }
/* Disclosure icon-buttons keep the compact 32px face but use a 20px triangle. The
   former 16px glyph was too slight inside the square in property/widget headers and
   the enquiry queue. Summary's inline 12px disclosure marks intentionally stay small. */
.mk-icon-btn svg:is(.chev, .caret) {
  width: 20px; height: 20px; fill: currentColor; stroke: none;
}
/* One disclosure ink across widget heads, service rows and enquiry-table rows. Placement
   must not make the same triangle appear stronger or weaker. */
.mk-icon-btn:has(svg[data-mk-icon="chevron"]),
.mk-icon-btn:has(svg[data-mk-icon="caret"]) { color: var(--mk-text-muted); }

/* A visible icon-button face whose hit area belongs to its complete positioned row.
   The row must provide position:relative; sibling controls must sit outside that row. */
.mk-icon-btn-stretched { position: static; }
.mk-icon-btn-stretched::after { inset: 0; }

/* Delete wears its consequence. A grey trash face read as one more quiet tool (Ihor,
   08-10): the destructive icon button rests in error ink and answers hover with the
   error tint — the same grammar as the destructive button tier. */
.mk-icon-btn-destructive { color: var(--mk-error-text); }
.mk-icon-btn-destructive:hover,
.mk-icon-btn-destructive.is-hover { background: var(--mk-error-tint); border-color: var(--mk-error-text); }

/* Icon-button scale. The bare component remains M; explicit modifiers are used in
   new work. Compact faces keep a 44px invisible target, while L/XL expose more of
   that target visually for prominent and mobile controls. */
.mk-icon-btn-s { width: var(--mk-icon-btn-size-s); height: var(--mk-icon-btn-size-s); }
.mk-icon-btn-s::after { inset: -8px; }
.mk-icon-btn-s svg { width: 14px; height: 14px; }
.mk-icon-btn-m { width: var(--mk-icon-btn-size-m); height: var(--mk-icon-btn-size-m); }
.mk-icon-btn-m::after { inset: -6px; }
.mk-icon-btn-m svg { width: 16px; height: 16px; }
.mk-icon-btn-l { width: var(--mk-icon-btn-size-l); height: var(--mk-icon-btn-size-l); }
.mk-icon-btn-l::after { inset: -2px; }
.mk-icon-btn-l svg { width: 18px; height: 18px; }
.mk-icon-btn-xl { width: var(--mk-icon-btn-size-xl); height: var(--mk-icon-btn-size-xl); }
.mk-icon-btn-xl::after { inset: 0; }
.mk-icon-btn-xl svg { width: 20px; height: 20px; }

.mk-icon-btn-destructive { color: var(--mk-error-text); }
.mk-icon-btn-destructive:hover { background: var(--mk-error-wash);
                                 border-color: color-mix(in srgb, var(--mk-error) 30%, transparent); }

/* Tooltip: a short visible explanation attached to one trigger. The neutral classes
   own the contract; the icon-prefixed aliases keep existing prototype markup live. */
.mk-tooltip-wrap,
.mk-icon-tooltip-wrap { position: relative; display: inline-flex; align-items: center; }
.mk-tooltip,
.mk-icon-tooltip {
  --mk-tip-bg: color-mix(in srgb, var(--mk-camel-light) 28%, var(--mk-surface));
  position: absolute; left: 50%; bottom: calc(100% + 8px); z-index: 60;
  transform: translateX(-50%); padding: 6px 11px; border-radius: var(--mk-radius-md);
  background: var(--mk-tip-bg); color: var(--mk-text-muted);
  font-family: var(--mk-font-app); font-size: var(--mk-text-sm); font-weight: 400;
  line-height: 18px; white-space: nowrap; pointer-events: none;
  opacity: 0; visibility: hidden;
  transition: opacity .15s ease, visibility 0s linear .15s;
}
.mk-tooltip::after,
.mk-icon-tooltip::after {
  content: ""; position: absolute; left: 50%; top: 100%; transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: var(--mk-tip-bg);
}
.mk-tooltip.is-left,
.mk-icon-tooltip { left: auto; bottom: auto; top: 50%; right: calc(100% + 8px); transform: translateY(-50%); }
.mk-tooltip.is-left::after,
.mk-icon-tooltip::after {
  left: 100%; top: 50%; transform: translateY(-50%);
  border-color: transparent; border-left-color: var(--mk-tip-bg);
}
.mk-tooltip.is-right { left: calc(100% + 8px); right: auto; bottom: auto; top: 50%; transform: translateY(-50%); }
.mk-tooltip.is-right::after { left: auto; right: 100%; top: 50%; transform: translateY(-50%);
                              border-color: transparent; border-right-color: var(--mk-tip-bg); }
.mk-tooltip.is-bottom { top: calc(100% + 8px); bottom: auto; }
.mk-tooltip.is-bottom::after { top: auto; bottom: 100%; border-color: transparent;
                               border-bottom-color: var(--mk-tip-bg); }
.mk-tooltip-info {
  width: max-content; max-width: min(280px, calc(100vw - 32px));
  padding: 10px 12px; white-space: normal; text-align: left; line-height: 20px;
}
.mk-tooltip-wrap:hover .mk-tooltip,
.mk-tooltip-wrap:has(:focus-visible) .mk-tooltip,
.mk-tooltip.is-visible,
.mk-icon-tooltip-wrap:hover .mk-icon-tooltip,
.mk-icon-tooltip-wrap:has(.mk-icon-btn:focus-visible) .mk-icon-tooltip {
  opacity: 1; visibility: visible;
  transition: opacity .15s ease .1s, visibility 0s linear .1s;
}

/* A row of icon buttons. 8px is the minimum gap that keeps two -6px hit expanders from
   overlapping, which would let one button steal the other's taps. */
.mk-icon-row { display: inline-flex; align-items: center; gap: 8px; }

/* A complete row that acts. Domain layouts may place media, facts or a trailing chevron
   inside it, but the interactive face and focus contract are shared. */
.mk-row-action {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: var(--mk-tap-min);
  padding: 10px 12px; border: 0; border-radius: var(--mk-radius-sm);
  background: transparent; color: var(--mk-text-muted); font: inherit; text-align: left; cursor: pointer;
}
/* Translucent ink, not the opaque band (Ihor, 08-11): the band was mixed over WHITE
   and vanished inside widgets, whose ground it nearly matched. Ink over transparent
   darkens whatever the row sits on. The go mark answers in the accent — the promise
   lights when the row does. */
.mk-row-action:hover, .mk-row-action.is-hover { background: color-mix(in srgb, var(--mk-text) 8%, transparent); }
.mk-row-action:hover .go, .mk-row-action.is-hover .go { color: var(--mk-accent); }
.mk-row-action:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-row-action.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
/* Blocked keeps its seat but goes grey and dead-quiet: no hover answer, no promise
   mark (Ihor, 08-11 — if the state exists at all, it must not invite). */
.mk-row-action[aria-disabled="true"] { color: var(--mk-text-quiet); cursor: default; }
.mk-row-action[aria-disabled="true"]:hover,
.mk-row-action[aria-disabled="true"].is-hover { background: transparent; }
/* 08-10, his read: the muted go mark vanished at the row's far edge. The promise mark
   is the component's contract, so the component styles it: 18px, full text ink, the
   registry stroke — a promise no one can see promises nothing. */
.mk-row-action .go {
  width: 18px; height: 18px; flex: none;
  fill: none; stroke: currentColor; stroke-width: var(--mk-icon-stroke);
  stroke-linecap: butt; stroke-linejoin: miter;
  color: var(--mk-text);
}

/* A named button stretched over a composed row whose other facts remain outside the button.
   The positioned row is the containing block; the visible label keeps its native size. */
.mk-stretched-action { position: static; border: 0; background: none; padding: 0;
                       color: inherit; font: inherit; text-align: left; cursor: pointer; }
.mk-stretched-action::after { content: ""; position: absolute; inset: 0; }
.mk-stretched-action:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }

/* Add-to-this-zone row: a full-width secondary action with enough copy room to name the
   destination and explain what will be added. It is an action, so the border is solid. */
.mk-add-row {
  display: inline-flex; align-items: center; gap: 12px; width: 100%; min-height: var(--mk-tap-min);
  padding: 12px 14px; border: 1px solid var(--mk-hairline-strong);
  border-radius: var(--mk-radius-sm); background: transparent; color: var(--mk-text-muted);
  font: inherit; text-align: left; cursor: pointer;
}
.mk-add-row:hover { background: var(--mk-surface-raised); border-color: var(--mk-accent); }
.mk-add-row:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-add-row .mk-add-row-hint { flex: 1; color: var(--mk-text-quiet); font-size: var(--mk-text-sm); }
.mk-add-row .mk-add-row-label { color: var(--mk-accent); font-size: var(--mk-text-sm); font-weight: 500; }

/* ── Forms & controls ────────────────────────────────────────────────────── */
.mk-field { display: block; max-width: 420px; margin-bottom: var(--mk-space-4); }
.mk-label { display: block; font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs); letter-spacing: .1em;
            text-transform: uppercase; color: var(--mk-text-label); margin-bottom: 6px; }
/* A FIELD's label — the word naming one input or select (his 07-26 call on the
   Commit pair: caps stay on SECTION labels; over a control the caps read as a
   banner and 600 reads as a row title — the field voice is sentence case, medium). */
.mk-field-label { display: block; font-size: var(--mk-text-sm); font-weight: 500;
                  color: var(--mk-text-muted); }
.mk-field .mk-field-label { margin-bottom: 6px; }
/* 07-17, his call at assembly: the raised-cream fill LEFT the inputs — a field is a
   field, white like the dropdown faces (closes the input-vs-dropdown fill split that
   stood open since round 9). */
.mk-input { width: 100%; min-height: var(--mk-tap-min); padding: 10px 16px;
            /* 07-30, his call, REVERSING the 07-26 step-rounder rule: the 4px input read
               inconsistent beside the select face and the date field, both on
               --mk-radius-sm. One radius for every control — the dropdown's was right. */
            border-radius: var(--mk-radius-sm); border: 1px solid var(--mk-hairline-strong);
            background: var(--mk-surface); color: var(--mk-text-muted);
            font-family: var(--mk-font-app); font-size: var(--mk-text-sm); }
/* DEPRECATED (08-02, his call, REPEALING 07-26's warm-on-white): the warm resting
   variant split the family — a gold select stood beside a grey date field on the same
   card, and the cream fill read as a second colour. ONE resting dress on every ground:
   surface fill, strong edge. The warm edge is the INTERACTION voice now — hover and
   focus — not a resting dress. The strong border carries the field's boundary on
   white, which is what the cream well was compensating for. Class kept as a no-op so
   existing markup renders the base dress. */
.mk-input-warm { }
.mk-input::placeholder { color: var(--mk-text-quiet); }
/* A select resting on its placeholder (an empty-value option selected) reads muted — the
   same grey the date field's empty label wears (.mk-daterange-empty). Real options keep
   ink so the open list never looks disabled. */
select.mk-input:has(option[value=""]:checked) { color: var(--mk-text-muted); }
select.mk-input option { color: var(--mk-text-muted); }
.mk-input:hover:not(:disabled):not([readonly]),
.mk-input.is-hover,
.mk-search.is-hover .mk-input { border-color: var(--mk-hairline-accent); }
/* the ENGAGED field wears the warm edge — every focus, not only the keyboard's
   (08-02: rest is grey, gold is hover and focus, one rule for the whole family) */
.mk-input:focus:not([readonly]),
.mk-input.is-focus, .mk-search.is-focus .mk-input { border-color: var(--mk-hairline-accent); }
.mk-input:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 1px; }
.mk-input.is-focus, .mk-search.is-focus .mk-input { outline: 2px solid var(--mk-camel-mid); outline-offset: 1px; }
.mk-input[aria-invalid="true"] { border-color: var(--mk-error); }
.mk-input[aria-invalid="true"]:focus-visible { outline-color: var(--mk-error); }
.mk-input[readonly] { background: var(--mk-surface-raised); border-color: var(--mk-hairline);
                      cursor: default; }
.mk-input:disabled, .mk-input[aria-disabled="true"] { opacity: .38; cursor: not-allowed; }
textarea.mk-input { min-height: 88px; resize: vertical; line-height: 1.5; }
/* ── Control size scale (08-02, mirrors the button scale) ─────────────────────
   One set of modifiers for the whole field family — .mk-input, .mk-select-face,
   .mk-daterange, .mk-stepper. Defaults stay what each component ships (input and
   date range M, the select face S) so no page reflows silently; the modifier makes
   a size deliberate, and a ROW of mixed controls should share one. No XL: a field
   is never the page's dominant act the way a button is. */
/* Compound selectors on purpose: each component's own resting height is declared
   later in this file and would out-cascade a bare .mk-ctl-* at equal specificity —
   the select face's M was silently staying 36 (his catch, 08-02). Heights are
   border-box so a row of mixed controls sits on one line, to the pixel. */
.mk-input.mk-ctl-s, .mk-select-face.mk-ctl-s, .mk-daterange.mk-ctl-s {
  box-sizing: border-box; min-height: var(--mk-btn-height-s); }
.mk-input.mk-ctl-m, .mk-select-face.mk-ctl-m, .mk-daterange.mk-ctl-m {
  box-sizing: border-box; min-height: var(--mk-btn-height-m); }
.mk-input.mk-ctl-l, .mk-select-face.mk-ctl-l, .mk-daterange.mk-ctl-l {
  box-sizing: border-box; min-height: var(--mk-btn-height-l); }
.mk-input.mk-ctl-s { padding: 6px 12px; }
.mk-input.mk-ctl-l { padding: 12px 18px; font-size: var(--mk-text-md); }
.mk-select-face.mk-ctl-s { padding: 6px 12px; }
.mk-select-face.mk-ctl-m { padding: 8px 14px; }
.mk-select-face.mk-ctl-l { padding: 10px 16px; font-size: var(--mk-text-md); }
.mk-daterange.mk-ctl-s { padding: 0 12px; }
.mk-daterange.mk-ctl-l { padding: 0 16px; font-size: var(--mk-text-md); }
.mk-stepper.mk-ctl-s .mk-stepper-btn { width: var(--mk-btn-height-s); height: var(--mk-btn-height-s); }
.mk-stepper.mk-ctl-l .mk-stepper-btn { width: var(--mk-btn-height-l); height: var(--mk-btn-height-l); }

.mk-field-help,
.mk-field-error { display: block; margin-top: 6px; font-size: var(--mk-text-sm); line-height: 1.4; }
.mk-field-help { color: var(--mk-text-muted); }
.mk-field-error { color: var(--mk-error-text); font-weight: 500; }
.mk-field-error[hidden] { display: none; }

/* ── Search — THE search face, one component everywhere (M6-14, the fourth or
   fifth ask: «нормальное поле серча» — magnifier IN the field, fully rounded).
   The component existed while pages hand-rolled their own (.fsearch, a bare
   combobox input) — which is exactly how the ask happened five times. It is now
   the only legal search face: the manifest names it, and lint:ds errors on a
   search input outside it. */
.mk-search { position: relative; max-width: 420px; }
.mk-search .glyph { position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
                    color: var(--mk-text-muted); font-size: var(--mk-text-sm); pointer-events: none; }
.mk-search svg.glyph { width: 18px; height: 18px; fill: none; stroke: currentColor;
                       stroke-width: var(--mk-icon-stroke);
                       stroke-linecap: butt; stroke-linejoin: miter; }
.mk-search .mk-input { padding-left: 40px; border-radius: 999px;
                       /* white, not the cream well (Ihor, 07-17: on the picker modal the
                          raised tint read as a YELLOW fill — a search face is a field,
                          not a well; default is the surface itself) */
                       background: var(--mk-surface); }
/* The browser's native search cancel varies by platform and can bring a foreign
   blue glyph into the field. Search owns one registry close control instead. */
.mk-search input[type="search"]::-webkit-search-cancel-button { appearance: none; }
.mk-search:has(.mk-search-clear) .mk-input { padding-right: 42px; }
.mk-search-clear { position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
                   display: inline-flex; align-items: center; justify-content: center;
                   width: 32px; height: 32px; padding: 0; border: 0;
                   border-radius: 50%; background: transparent; color: var(--mk-text-muted);
                   cursor: pointer; }
.mk-search-clear[hidden] { display: none; }
.mk-search-clear:hover, .mk-search-clear.is-hover { background: var(--mk-surface-sunken);
                                                    color: var(--mk-text); }
.mk-search-clear:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 1px; }
.mk-search-clear svg { width: 16px; height: 16px; fill: none; stroke: currentColor;
                       stroke-width: var(--mk-icon-stroke); stroke-linecap: butt;
                       stroke-linejoin: miter; }

/* ── Select-face — a menu button dressed as the field it is (S-A “The Select”,
   concepts-status-control.md, PV3-6). The closed face shows the VALUE as plain
   text (with its dot when the value is a status) and a trailing chevron, on the
   control radius — never a pill: statuses ARE pills in this product, and a
   pill-that-is-a-control confuses the castes (Polaris Badge: informational,
   never interactive; Linear: controls and pills sit on different radius tiers).
   Opens an attached .mk-menu; the chevron flips. Phone: the full-width row. */
.mk-select-face { display: inline-flex; align-items: center; gap: 8px;
                  /* M like every field (his call, 08-02 — the face shipped S while inputs
                     shipped M and the family never sat on one line); dense rows opt into
                     .mk-ctl-s */
                  min-height: var(--mk-btn-height-m); padding: 8px 14px;
                  border: 1px solid var(--mk-hairline-strong); border-radius: var(--mk-radius-sm);
                  /* WHITE always (Ihor, 07-27, on the queue's filter bar): the transparent
                     face of 07-17 read as one family only on white grounds — on the page
                     ground it went beige. The fill is now explicit surface white; warm
                     stays the on-white-card variant below. */
                  background: var(--mk-surface); color: var(--mk-text-muted);
                  font-family: var(--mk-font-app); font-size: var(--mk-text-sm); font-weight: 350;
                  cursor: pointer; text-align: left; position: relative;
                  white-space: nowrap; /* a face holds ONE value; it widens, never wraps */ }
/* Ihor, 07-16 live (round 9): the face is NOT bold — a select holds a value, it does not
   shout it; and the full-sunken hover read too yellow next to the time field's quieter
   mix — both now share the time field's hover. Applies to every dropdown by design.
   Same session, next pass: the CLOSED face carries NO fill at all — the raised cream read
   as a second colour against white time fields on the same row; the border is the control.
   07-17, at assembly: the split CLOSED his way — mk-input lost the raised cream too, so
   every field and face on a row reads as one family of white controls. */
.mk-select-face::after { content: ""; position: absolute; inset: -4px 0; } /* 36px → 44px touch */
.mk-select-face:disabled,
.mk-select-face[aria-disabled="true"] { opacity: .38; cursor: not-allowed; }
/* 08-02, the one interaction voice: the hover warms the EDGE, not the fill — the
   band wash of 07-16 was the fill speaking while the fields' hover spoke through
   the border; one family, one voice. */
.mk-select-face:hover, .mk-select-face.is-hover { border-color: var(--mk-hairline-accent); }
/* Ihor, 07-13 live: the OPEN control goes WHITE — the sunken/raised tint merged with the
   status pill's own shell and the two read as one yellow mass. Open ≠ hover: the open
   state separates the control from the value it holds. */
/* the ENGAGED control shows one face across the family (his catch, 08-02): a text
   field under the caret shows the 2px ring (the browser counts typing focus as
   focus-visible), so the OPEN face and the OPEN date field wear the same ring —
   engagement must not look stronger on one control than its neighbour */
.mk-select-face[aria-expanded="true"],
.mk-select-face[aria-expanded="true"]:hover { background: var(--mk-surface);
                                              border-color: var(--mk-hairline-accent);
                                              outline: 2px solid var(--mk-camel-mid);
                                              outline-offset: 1px; }
.mk-select-face:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-select-face.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
/* DEPRECATED (08-02, with .mk-input-warm): the face rests grey on every ground;
   the warm edge is hover and open. Kept as a no-op for existing markup. */
.mk-select-face-warm { }

/* A unit toggle — two joined segments choosing HOW one value is expressed (% or OMR).
   Lineage: segmented control / unit switcher. Born 07-27 on the Payment widget: the
   %-vs-amount mode lived in the share menu as «Fixed amount…» and read as one more
   value — a MODE is not a choice in the value list, it stands beside the field.
   (The theme-switch round cut was tried the same day and overturned by his eye: a
   text label forces the knob into an oval, and the oval reads poorly — the square
   segments stand. Sized to the currency label's readable minimum.) */
.mk-unit-toggle { display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr;
                  border: 1px solid var(--mk-hairline-strong);
                  border-radius: var(--mk-radius-sm); overflow: hidden; flex: none; }
/* compact vertical cut (his 07-27 ask, after the toggle moved to the widget head —
   it no longer sits in a row of 36px faces, so it drops to a quiet 28px) */
.mk-unit-toggle button { min-height: 26px; padding: 1px 6px; border: 0;
                         background: none; font: inherit; font-size: var(--mk-text-sm);
                         font-weight: 500;  /* one weight in BOTH states (Ihor 07-27) —
                                               the fill says the choice, never the type */
                         color: var(--mk-text-muted); cursor: pointer; text-align: center; }
.mk-unit-toggle button + button { border-left: 1px solid var(--mk-hairline-strong); }
/* the picked segment wears the brand accent, its label in the contrast ink
   (Ihor 07-27 — the quiet camel wash read as a hover, not a choice) */
.mk-unit-toggle button[aria-pressed="true"] { color: var(--mk-accent-contrast);
  background: var(--mk-accent-fill); }
/* the unpressed segment washes lightly under the cursor — the page claimed this
   from day one and the layer never carried it (caught by the 08-10 state matrices) */
.mk-unit-toggle button:hover:not([aria-pressed="true"]),
.mk-unit-toggle button.is-hover:not([aria-pressed="true"]) {
  background: var(--mk-surface);
  color: var(--mk-accent);
  outline: 1px solid var(--mk-accent);
  outline-offset: -1px;
}
.mk-unit-toggle button:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-unit-toggle button.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
/* DEPRECATED (08-02, with .mk-input-warm): the toggle rests grey everywhere. */
.mk-unit-toggle-warm { }
/* 14px (Ihor, 07-13 live: «стрелочку шеврона сделай побольше») — it leaves the L4 micro
   band, so its weight steps down toward the family */
/* The face's glyph is the dropdowns' own filled «▾» (Ihor 07-27 — see .mk-select-wrap):
   the svg the markup still carries is retired by CSS so every page inherits the verdict. */
.mk-select-face .chev { display: inline-flex; align-items: center; justify-content: center;
                        width: 16px; height: 16px; flex: none; margin-left: auto;
                        color: var(--mk-text-muted); transition: transform .15s; }
/* the registry's filled `caret` — the same solid triangle, now drawn rather than typed */
/* 08-10, his read: the 16px triangle was too small to read as the face's affordance.
   20px matches the disclosure triangle — one filled-triangle size across the system. */
.mk-select-face .chev svg { width: 20px; height: 20px; fill: currentColor; stroke: none; }
.mk-select-face[aria-expanded="true"] .chev { transform: rotate(180deg); }
/* the bare-svg shape of the same glyph — the component owns its size, so a page can
   never hand it a smaller one */
.mk-select-face > svg.caret { width: 20px; height: 20px; flex: none; margin-left: auto;
                              fill: currentColor; stroke: none; color: var(--mk-text-muted);
                              transition: transform .15s; }
.mk-select-face[aria-expanded="true"] > svg.caret { transform: rotate(180deg); }
/* The MULTI dropdown (Ihor, 08-11): chosen values ride the face as the filter-chip
   component, and the face grows by WRAPPING — a closed list of many never hides
   behind a "3 selected" count. A chip's cross removes its value without opening
   the list; the caret keeps the trailing seat. */
.mk-select-face.mk-select-multi { height: auto; flex-wrap: wrap; padding-block: 6px;
                                  row-gap: 4px; }
.mk-select-face.mk-select-searchable { cursor: text; color: var(--mk-text); }
.mk-select-searchable::after { display: none; }
.mk-select-searchable:focus-within,
.mk-select-searchable.is-open { background: var(--mk-surface);
                                border-color: var(--mk-hairline-accent);
                                outline: 2px solid var(--mk-camel-mid);
                                outline-offset: 1px; }
.mk-select-searchable .mk-select-query { flex: 1; min-width: 0; margin: 0; padding: 0;
                                         border: 0; outline: 0; background: transparent;
                                         color: inherit; font: inherit; line-height: inherit; }
.mk-select-searchable .mk-select-query::placeholder { color: var(--mk-text-quiet); opacity: 1; }
.mk-select-searchable.is-open > svg.caret { transform: rotate(180deg); }
@media (max-width: 767px) {
  .mk-select-face { width: 100%; min-height: var(--mk-tap-min); padding: 8px 14px; }
}

/* ── Gloss — a compressed term that explains itself on demand (M6-4: «unclear
   tags get a small tooltip — hover on desktop, tap on mobile — one sentence max»).
   Lineage: abbr/definition tooltip; the dotted underline is that pattern's own
   mark (text decoration, not a border — the no-dashed-affordance rule is about
   borders on pressables). The tip is one sentence, wired by shell.js
   (mkWireGloss): hover + focus show it, tap toggles it, Esc and outside close. */
.mk-gloss { display: inline; border: 0; background: none; padding: 0; margin: 0;
            font: inherit; color: inherit; cursor: help; position: relative;
            text-decoration: underline dotted color-mix(in srgb, var(--mk-text) 45%, transparent);
            text-underline-offset: 3px; }
/* a 20px text line is a control — it carries its own touch floor (48px: 44
   exact is flaky against the checker's strict < at subpixel zoom) */
.mk-gloss::before { content: ""; position: absolute; inset: -14px -8px; }
.mk-gloss:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-gloss-tip { position: absolute; bottom: calc(100% + 8px); left: 0; z-index: 60;
                width: max-content; max-width: min(300px, 82vw);
                background: var(--mk-surface); border: 1px solid var(--mk-hairline-strong);
                border-radius: var(--mk-radius-sm); box-shadow: var(--mk-shadow-pop);
                padding: 8px 12px; font-family: var(--mk-font-app); font-size: var(--mk-text-sm);
                font-weight: 350; letter-spacing: normal; text-transform: none;
                color: var(--mk-text-muted); text-align: left; line-height: 20px; white-space: normal; }
.mk-gloss-tip.tip-right { left: auto; right: 0; }

.mk-select-wrap { position: relative; max-width: 420px; }
/* The dropdown wears the FILLED «▾» (Ihor 07-27, reversing the same evening's stroke
   cut before it landed anywhere else): the drawn chevron is the fold/disclosure mark
   all over the app — a dropdown is its own kind of element and keeps its own glyph. */
/* 20px glyph (08-10, his read: still too small at 16 — every dropdown's triangle grows
   to the disclosure size, one filled-triangle weight across the system) */
.mk-select-wrap::after { content: "▾"; position: absolute; right: 14px; top: 50%;
                         transform: translateY(-50%); color: var(--mk-text-muted);
                         font-size: 20px; pointer-events: none; }
/* the 07-26 radius rule applied (selects stay on --mk-radius-sm; only free-text
   inputs sit a step rounder) — the select had been riding .mk-input's 4px */
select.mk-input { appearance: none; -webkit-appearance: none; padding-right: 38px; cursor: pointer;
                  border-radius: var(--mk-radius-sm); }

/* ── The dash — ONE style (Ihor, 07-16, fixed after rounds of drift): every dashed line
   is 1px, ink --mk-hairline-dash (08-02: a 3/2 dash lays ink on 60% of its length, so
   the ink overshoots the solid strong to READ as strong). A CSS dashed border at 1px
   draws 3px dash / 2px gap (pixel-measured in Chrome), so a gradient-drawn rail must
   match it exactly: 1px wide, the .mk-rail pattern. Never a 2px rail, never another
   period, never a solid hairline token as the ink. */
.mk-rail { position: absolute; width: 1px;
           background: repeating-linear-gradient(to bottom,
             var(--mk-hairline-dash) 0 3px, transparent 3px 5px); }

.mk-choice-group { max-width: 420px; margin: 0 0 var(--mk-space-4); padding: 0;
                   border: 0; }
.mk-choice-legend { display: block; margin-bottom: 6px; padding: 0;
                    font-size: var(--mk-text-sm); font-weight: 500; color: var(--mk-text-muted); }
.mk-check,
.mk-radio { display: flex; align-items: center; gap: 10px; min-height: var(--mk-tap-min);
            font-size: var(--mk-text-sm); cursor: pointer; }
/* the box is DRAWN, not borrowed (Ihor 07-27): the UA's native check glyph sits a
   full weight grade heavier than the house icon family — the mark here is the same
   thin stroke the icons wear. 18→20, Ihor 07-16. */
.mk-check input { appearance: none; -webkit-appearance: none; width: 20px; height: 20px;
                  margin: 0; flex: none; cursor: pointer; position: relative;
                  background: var(--mk-surface); border: 1px solid var(--mk-hairline-strong);
                  border-radius: 1px; /* 3→1, Ihor 07-27 */ }
.mk-check input:checked { background: var(--mk-accent-fill); border-color: var(--mk-accent-fill); }
.mk-check input:checked::after { content: ""; position: absolute; left: 4px; top: 5px;
                                 width: 10px; height: 5px;
                                 border-left: 1.5px solid var(--mk-accent-contrast);
                                 border-bottom: 1.5px solid var(--mk-accent-contrast);
                                 transform: rotate(-45deg); }
.mk-radio input { appearance: none; -webkit-appearance: none; width: 20px; height: 20px;
                  margin: 0; flex: none; cursor: pointer; position: relative;
                  background: var(--mk-surface); border: 1px solid var(--mk-hairline-strong);
                  border-radius: 50%; }
.mk-radio input:checked { border-color: var(--mk-accent); }
/* The checked mark is a FILL, and the token comment for --mk-accent names it as one — "a
   checked box, a switch track" sit on --mk-accent-fill. Both read the INK here until 08-19,
   which cost nothing while light kept one value for both roles and turned a checked radio and
   a live switch into bright yellow blocks the moment the dark ink was allowed to be bright. */
.mk-radio input:checked::after { content: ""; position: absolute; inset: 4px;
                                 border-radius: 50%; background: var(--mk-accent-fill); }
/* the family's one hover voice: the unchecked box warms its edge under the cursor */
.mk-check:hover input:not(:checked):not(:disabled),
.mk-radio:hover input:not(:checked):not(:disabled),
.mk-check.is-hover input:not(:checked):not(:disabled),
.mk-radio.is-hover input:not(:checked):not(:disabled) { border-color: var(--mk-hairline-accent); }
.mk-check input:focus-visible,
.mk-radio input:focus-visible,
.mk-check.is-focus input,
.mk-radio.is-focus input { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-check.disabled,
.mk-radio.disabled,
.mk-check:has(input:disabled),
.mk-radio:has(input:disabled) { opacity: .38; cursor: not-allowed; }
.mk-check.disabled input,
.mk-radio.disabled input,
.mk-check input:disabled,
.mk-radio input:disabled { cursor: not-allowed; }

.mk-switch { display: flex; align-items: center; gap: 12px; min-height: var(--mk-tap-min);
             font-size: var(--mk-text-sm); cursor: pointer; }
.mk-switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.mk-switch .track { width: 36px; height: 20px; border-radius: 999px; flex: none;
                    background: var(--mk-surface-sunken); border: 1px solid var(--mk-hairline-strong);
                    position: relative; transition: background .15s; }
.mk-switch .track::after { content: ""; position: absolute; top: 2px; left: 2px;
                           width: 14px; height: 14px; border-radius: 50%;
                           background: var(--mk-surface); border: 1px solid var(--mk-hairline-strong);
                           transition: transform .15s; }
.mk-switch input:checked + .track { background: var(--mk-accent-fill); border-color: var(--mk-accent-fill); }
.mk-switch input:checked + .track::after { transform: translateX(16px); }
.mk-switch input:focus-visible + .track { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-switch:has(input:disabled) { opacity: .38; cursor: not-allowed; }

/* ── Date picker ──────────────────────────────────────────────────────────────
   One field that opens a single-date or range calendar (behaviour: window.mkDateRange in shell.js).
   Born 2026-07-20 to unify two hand-rolled copies — the builder's property-search
   .drange and the enquiry-inbox filter rail — into one component. Adaptive: the
   range popover shows TWO months side by side when the viewport is wide, one on a phone;
   single-date mode always shows one month.
   The trigger sizes to its content (min-width), never stretched to fill its column. */
.mk-daterange {
  display: inline-flex; align-items: center; gap: 8px;
  min-height: var(--mk-tap-min); min-width: 190px; max-width: 100%; padding: 0 14px;
  background: var(--mk-surface); border: 1px solid var(--mk-hairline-strong);
  border-radius: var(--mk-radius-sm); cursor: pointer;
  font: inherit; font-size: var(--mk-text-sm); color: var(--mk-text-muted);
  font-variant-numeric: lining-nums tabular-nums; white-space: nowrap;
}
.mk-daterange:hover,
.mk-daterange[aria-expanded="true"],
.mk-daterange.is-hover { border-color: var(--mk-hairline-accent); }
.mk-daterange[aria-expanded="true"] { outline: 2px solid var(--mk-camel-mid);
                                      outline-offset: 1px; }
.mk-daterange:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-daterange.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-daterange > svg { width: 16px; height: 16px; flex: none; color: var(--mk-icon-ink);
  fill: none; stroke: currentColor; stroke-width: var(--mk-icon-stroke);
  stroke-linecap: butt; stroke-linejoin: miter; }
.mk-daterange-label { overflow: hidden; text-overflow: ellipsis; }
/* namespaced state class — a bare .empty collides with pages' empty-state styles */
.mk-daterange-label.mk-daterange-empty { color: var(--mk-text-quiet); }

/* Increment/decrement field. The group is the control boundary; each end button keeps the
   44px target and the value stays stable in the middle. */
.mk-stepper { display: inline-flex; align-items: center; overflow: hidden;
              border: 1px solid var(--mk-hairline-strong);
              border-radius: var(--mk-radius-sm); background: var(--mk-surface); }
.mk-stepper:hover, .mk-stepper:focus-within,
.mk-stepper.is-hover, .mk-stepper.is-focus { border-color: var(--mk-hairline-accent); }
.mk-stepper-btn { width: 44px; height: 44px; padding: 0; border: 0; background: none;
                  color: var(--mk-text-muted); font: inherit; font-size: var(--mk-text-md);
                  cursor: pointer; }
.mk-stepper-btn:hover, .mk-stepper-btn.is-hover { background: var(--mk-surface-band); }
.mk-stepper-btn:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-stepper-btn.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-stepper-btn[aria-disabled="true"] { color: var(--mk-text-muted); cursor: not-allowed; }
.mk-stepper-value { min-width: 26px; padding: 0 4px; text-align: center;
                    font-size: var(--mk-text-sm); font-variant-numeric: tabular-nums; }

.mk-calpop {
  position: fixed; z-index: 70;
  background: var(--mk-surface); border: 1px solid var(--mk-hairline-strong);
  border-radius: var(--mk-radius-md); box-shadow: var(--mk-shadow-card); padding: 14px 16px 16px;
}
.mk-cal-months { display: flex; gap: 24px; }
.mk-cal-month { width: 224px; }
.mk-cal-mhead { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.mk-cal-title { font-weight: 600; font-size: var(--mk-text-sm); color: var(--mk-text-muted); }
.mk-cal-nav, .mk-cal-nav-spacer { width: 32px; height: 32px; flex: none; }
.mk-cal-nav { border: 0; background: none; cursor: pointer; border-radius: var(--mk-radius-sm);
  color: var(--mk-text-muted); font-size: 16px; }
.mk-cal-nav:hover { background: var(--mk-surface-sunken); }
.mk-cal-nav:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-cal-dow, .mk-cal-days { display: grid; grid-template-columns: repeat(7, 1fr); }
.mk-cal-dow span { text-align: center; font-family: var(--mk-font-app-caps);
  font-size: var(--mk-text-xs); letter-spacing: .08em; text-transform: uppercase;
  color: var(--mk-text-muted); padding-bottom: 6px; }
.mk-cal-days button { height: 32px; border: 0; background: none; cursor: pointer;
  font: inherit; font-size: var(--mk-text-sm); color: var(--mk-text-muted);
  border-radius: var(--mk-radius-sm); font-variant-numeric: lining-nums tabular-nums; }
.mk-cal-days button:hover, .mk-cal-days button.is-hover { background: var(--mk-surface-sunken); }
.mk-cal-days button:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-cal-days .pad { visibility: hidden; }
.mk-cal-days button.inr { background: var(--mk-accent-wash-strong); border-radius: 0; }
.mk-cal-days button.sel { background: var(--mk-accent-fill); color: var(--mk-accent-contrast); }

/* ── Filter chip ─────────────────────────────────────────────────────────────
   One component, not two. Both prototypes used it identically in the semantic
   sense — a <button aria-pressed> inside role="group" aria-label="Filter by …" —
   and differed only in dress: the queue drew an inset ring on the badge shell,
   the builder filled a 999px pill with solid camel. The ring is canonical, and
   it is also the only version that meets the touch minimum: the builder's pill
   sat at 32px with no hit expander (spec finding A9).
   Selection is read from aria-pressed, never from a class — the state is exposed
   to assistive tech before it is exposed to CSS. */
/* Grey, not the badge shell (08-06, Ihor: «серенький» — the chip is the reader's own
   tool and must not borrow the status family's tint). The primary form is the APPLIED
   filter: the chip appears when a filter lands, wears the value, and its cross removes
   it. The aria-pressed toggle form below stays while the experiences flags wear it. */
.mk-chip { display: inline-flex; align-items: center; gap: 6px; position: relative;
           font-family: var(--mk-font-app); font-size: var(--mk-text-sm); font-weight: 400; letter-spacing: .01em;
           white-space: nowrap; background: var(--mk-wash); color: var(--mk-text-muted);
           border: 1px solid var(--mk-hairline); border-radius: var(--mk-radius-compact);
           padding: 5px 10px; cursor: pointer; }
/* the removing cross — 12px, drawn from the registry's close, riding the label */
.mk-chip .mk-chip-x { width: 12px; height: 12px; flex: none; fill: none;
                      stroke: currentColor; stroke-width: var(--mk-icon-stroke);
                      stroke-linecap: butt; stroke-linejoin: miter; opacity: .7; }
.mk-chip:hover .mk-chip-x, .mk-chip.is-hover .mk-chip-x { opacity: 1; }
.mk-chip::after { content: ""; position: absolute; inset: -8px 0; } /* 44px touch hit */
.mk-chip:hover, .mk-chip.is-hover { background: var(--mk-wash-strong); }
.mk-chip:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-chip.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-chip[aria-pressed="true"] { border-color: var(--mk-accent); box-shadow: inset 0 0 0 1px var(--mk-accent); }
.mk-chip:disabled { opacity: .4; cursor: not-allowed; }
.mk-chip .n { font-family: var(--mk-font-app); font-size: var(--mk-text-xs); color: var(--mk-text-muted); }
.mk-chip .mk-dot { width: 7px; height: 7px; }
/* A dashed border is not an affordance (C15, Ihor 07-11 — asked for before and captured
   nowhere; captured now). Dashes on a CONTROL read as placeholder, never as press-me.
   The product's dash vocabulary is: emptiness (.mk-empty), a drop target mid-drag, a
   pending/not-yet zone, a quiet separator — and NOTHING pressable. A quiet control gets
   a light fill or a solid hairline instead. `.mk-chip.toggle` used to be the dashed
   variant; the base chip + aria-pressed ring already say everything it said, so the
   variant class survives in markup as documentation only. */
.mk-chip.toggle { border-style: solid; }

/* ── Option list ─────────────────────────────────────────────────────────────
   A list of choices: a combobox's listbox, a "add this to which day?" chooser. One set of
   classes, because the two differ only in role — `<li role="option">` driven by
   aria-activedescendant, or `<button>`s in a `role="group"` driven by real focus.

   The load-bearing decision is `[aria-disabled="true"]`, and it is the same decision the
   Send button already made (spec finding A4): **an option the system will refuse is shown,
   not hidden, and it states its reason.** A silently shortened list teaches nothing — the
   agent never learns that the Falaj walk is a Hajar experience, only that it is missing.
   So the blocked option keeps its place, carries `.mk-opt-why`, and stays reachable.

   It is dimmed by INK, never by `opacity`. A 40%-opacity sentence is a contrast failure that
   ds:lint:visual will measure and reject; --mk-text-muted is a token that already clears AA
   on every ground we put it on. And colour is not the only signal here — the reason is a
   sentence, so a monochrome screenshot still says why the option cannot be taken.

   The active-option highlight is `[aria-selected="true"]`, read from the attribute rather
   than a class, for the reason .mk-chip gives: the state reaches assistive tech before it
   reaches CSS, so the two cannot drift. */
.mk-optlist { list-style: none; margin: 0; padding: 4px; max-height: 264px; overflow: auto;
              display: flex; flex-direction: column; gap: 2px; }

/* `flex: none` is load-bearing. .mk-optlist is a COLUMN flex container with a max-height, so
   flex-shrink applies down the main axis — vertically. Past the scroll threshold every option
   was squeezed to its own min-height and its provider line spilled out through the bottom edge.
   Three demo rows never crossed the threshold, so the pattern page looked perfect and the
   builder's ten-row picker was broken. Only a browser says this; no static rule can. */
.mk-opt { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; flex: none;
          width: 100%; text-align: left; min-height: var(--mk-tap-min);
          padding: 8px 10px; border: 1px solid transparent; border-radius: var(--mk-radius-sm);
          background: transparent; color: var(--mk-text-muted);
          font: inherit; font-size: var(--mk-text-sm); cursor: pointer; }
.mk-opt:hover, .mk-opt.is-hover { background: var(--mk-surface-sunken); }
/* An option that shows its subject — the picker's media card (D-05, review 07-10:
   experiences are BROWSED, not only searched; «шукаєш гелікоптер — візуалізація
   відразу кинеться в глаза»). The thumb is the option's picture, full width;
   the state line says where the item already is. Geometry (heights, tile art)
   belongs to the surface using it. */
.mk-opt-thumb { display: block; position: relative; width: 100%; overflow: hidden; }
.mk-opt-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mk-opt-state { display: block; font-size: var(--mk-text-xs); color: var(--mk-text-muted); }
.mk-opt:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-opt.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-opt[aria-selected="true"] { background: var(--mk-accent-wash);
                                /* the fill alone says the choice (08-06, Ihor: an outlined
                                   option read as a focus ring, not a selection) */
                                border-color: transparent; }
.mk-opt[aria-selected="true"]::after {
  content: ""; flex: none; width: 11px; height: 6px; margin: 0 4px 0 auto;
  border-left: 2px solid var(--mk-accent); border-bottom: 2px solid var(--mk-accent);
  transform: rotate(-45deg) translateY(-1px);
}

.mk-opt-main { flex: 1; min-width: 0; }
.mk-opt-name { display: block; font-weight: 600; }
.mk-opt-sub { display: block; font-size: var(--mk-text-xs); color: var(--mk-text-muted); }
.mk-opt.mk-opt-with-media { align-items: center; flex-wrap: nowrap; }
.mk-opt-media { display: block; flex: none; width: 44px; height: 44px;
                border-radius: var(--mk-radius-sm); object-fit: cover; }

/* A blocked option: muted ink, a stated reason, and it does not move. */
.mk-opt[aria-disabled="true"] { cursor: not-allowed;
                                background: var(--mk-wash-faint); }
.mk-opt[aria-disabled="true"] .mk-opt-name { color: var(--mk-text-muted); font-weight: 500; }
.mk-opt[aria-disabled="true"]:hover { background: var(--mk-wash-faint); }
.mk-opt-why { flex: none; font-size: var(--mk-text-xs); color: var(--mk-warning-text); }

/* Search results with no supporting body use the menu's regular reading voice. Weight
   is reserved for a title that needs to lead a second line. The attached panel alone
   receives elevation: ordinary option lists keep their existing surface contract. */
.mk-search-results-wrap > .mk-optlist { box-shadow: var(--mk-shadow-pop); }
.mk-search-results-wrap .mk-opt-main > .mk-opt-name:only-child { font-weight: 400; }

/* Why this result matched the query. Not weight — a warm block follows the typed
   fragment. The earlier 14% wash disappeared against the white panel, so Search uses
   a deliberately stronger local mix without making the complete row look selected. */
.mk-hit { padding: 0 2px; border-radius: 2px;
          background: color-mix(in srgb, var(--mk-accent) 30%, var(--mk-surface));
          color: inherit; font-weight: inherit; text-decoration: none; }

.mk-optgroup-label { flex: none; font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs);
                     letter-spacing: .1em; text-transform: uppercase; color: var(--mk-text-muted);
                     padding: 10px 10px 4px; }

/* ── Inline combobox ─────────────────────────────────────────────────────────
   Expands in place; it does not float. A popover would have to escape the catalog's
   `overflow: auto`, fight the mobile bottom sheet for z-index, and reposition itself when the
   software keyboard opens. An inline disclosure has none of those problems, on either surface. */
.mk-combo { display: flex; flex-direction: column; gap: 8px; margin-top: 8px;
            padding: 8px; border: 1px solid var(--mk-hairline-strong);
            border-radius: var(--mk-radius-md); background: var(--mk-surface-raised); }
.mk-search-results-wrap { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.mk-combo .mk-input { max-width: none; }
.mk-combo .mk-search { max-width: none; }
.mk-search-results-wrap .mk-input,
.mk-search-results-wrap .mk-search { max-width: none; }
/* The pill is the boundary: a combo whose field is the search face sheds its own
   box — a rounded field inside a bordered square read as a leftover frame
   (Ihor, v3 review: «вокруг поиска осталась какая-то обводка»). */
.mk-combo:has(> .mk-search) { border: 0; padding: 0; background: none; }

/* The inline form of the empty state — same component, sized for a well rather than a page. */
.mk-empty.sz-s { padding: var(--mk-space-4); max-width: none; }
.mk-empty.sz-s .glyph { margin-bottom: var(--mk-space-2); }
.mk-empty.sz-s .title { font-size: var(--mk-text-md); }
.mk-empty.sz-s .hint { margin-bottom: 0; }

/* ── Back link — quiet directional navigation to a preceding or parent view. */
.mk-backlink { display: inline-flex; align-items: center; gap: 6px; min-height: var(--mk-tap-min);
               font-size: var(--mk-text-sm); color: var(--mk-accent); text-decoration: none; }
.mk-backlink:hover, .mk-backlink.is-hover { color: color-mix(in srgb, var(--mk-accent) 72%, var(--mk-text)); text-decoration: underline;
                                             text-decoration-color: currentColor;
                                             text-decoration-thickness: 2px; text-underline-offset: 3px; }
.mk-backlink:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-backlink.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }

/* ── Links ───────────────────────────────────────────────────────────────── */
.mk-link { color: var(--mk-accent); text-decoration: underline;
           text-decoration-color: currentColor; text-decoration-thickness: 1px;
           text-underline-offset: 3px; }
.mk-link:hover, .mk-link.is-hover { color: color-mix(in srgb, var(--mk-accent) 72%, var(--mk-text)); text-decoration-color: currentColor;
                                    text-decoration-thickness: 2px; }
.mk-link:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; border-radius: 1px; }
.mk-link.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; border-radius: 1px; }

/* An object name is a door to its destination: full ink and the outline open mark at rest,
   a quiet underline for recognition, and a clear accent shift on intent. */
.mk-object-link { color: var(--mk-text); text-decoration: underline;
                  text-decoration-color: currentColor; text-decoration-thickness: 1px;
                  text-underline-offset: 3px; }
.mk-object-link:hover, .mk-object-link.is-hover { color: color-mix(in srgb, var(--mk-accent) 72%, var(--mk-text)); text-decoration-color: currentColor;
                                                  text-decoration-thickness: 2px; }
.mk-object-link > svg[data-mk-icon="open"],
.mk-object-link > .mk-object-link-mark {
  width: 14px; height: 14px; margin-left: 4px; vertical-align: -2px;
  fill: none; stroke: currentColor; stroke-width: var(--mk-icon-stroke);
  stroke-linecap: butt; stroke-linejoin: miter;
}
.mk-object-link:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px;
                                border-radius: 1px; }
.mk-object-link.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px;
                           border-radius: 1px; }

/* Phone numbers and similar act-on-a-fact links keep the accent at rest. */
.mk-contact-link { color: var(--mk-accent); text-decoration: none; font-weight: inherit; }
.mk-contact-link:hover, .mk-contact-link.is-hover { color: color-mix(in srgb, var(--mk-accent) 72%, var(--mk-text)); text-decoration: underline;
                                                    text-decoration-color: currentColor;
                                                    text-decoration-thickness: 2px; text-underline-offset: 3px; }
.mk-contact-link:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px;
                                 border-radius: 1px; }
.mk-contact-link.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px;
                            border-radius: 1px; }

/* Card behavior: extend its named object link across the positioned card's bounds. */
.mk-stretched-link { position: static; }
.mk-stretched-link::after { content: ""; position: absolute; inset: 0; }

/* A complete navigation card. Unlike .mk-row-action it is an anchor and owns its border. */
.mk-nav-card { display: flex; min-width: 0; color: inherit; text-decoration: none;
               border: 1px solid var(--mk-hairline-mid); border-radius: var(--mk-radius-md);
               background: var(--mk-surface); }
.mk-nav-card:hover, .mk-nav-card.is-hover { border-color: var(--mk-hairline-accent); }
.mk-nav-card:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-nav-card.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }

/* Pagination is one non-wrapping navigation row. The visible page window gets narrower
   upstream; the controls themselves never split Previous and Next onto a second line. */
.mk-pagination { display: flex; align-items: center; justify-content: center;
                 flex-wrap: nowrap; gap: var(--mk-space-2); max-width: 100%;
                 white-space: nowrap; }

/* Numbered pagination. Previous/Next remain ordinary small ghost buttons; only a page
   number needs this compact square face and aria-current state. */
.mk-page-btn { min-width: 32px; height: 32px; padding: 0 6px;
               border: 1px solid transparent; border-radius: var(--mk-radius-sm);
               background: none; color: var(--mk-text-muted); font: inherit;
               font-size: var(--mk-text-sm); cursor: pointer; position: relative; }
.mk-page-btn::after { content: ""; position: absolute; inset: -6px 0; }
/* Translucent ink, not an opaque band (Ihor, 08-11): the band was mixed over WHITE,
   so on the grey widget ground the hover all but vanished — the same disease the
   add-stay wash had on 08-10. Ink over transparent darkens whatever ground the
   footer actually sits on. */
.mk-page-btn:hover, .mk-page-btn.is-hover { background: color-mix(in srgb, var(--mk-text) 8%, transparent); }
/* The gap in a long set: a silent 32px seat for the ellipsis — never a button,
   never a target. Quiet ink, because it speaks about absent numbers. */
.mk-page-gap { min-width: 32px; height: 32px; display: inline-flex; align-items: center;
               justify-content: center; color: var(--mk-text-quiet);
               font-size: var(--mk-text-sm); user-select: none; }
.mk-page-btn[aria-current="page"] { background: var(--mk-surface);
                                     border-color: var(--mk-camel-mid);
                                     font-weight: 600; cursor: default; }
.mk-page-btn:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-page-btn.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }

@media (max-width: 600px) {
  .mk-pagination { gap: 2px; }
}

/* ── Empty state ─────────────────────────────────────────────────────────── */
.mk-empty { background: var(--mk-surface-raised); border: 1px dashed var(--mk-hairline-dash);
            border-radius: var(--mk-radius-md); padding: var(--mk-space-7) var(--mk-space-5);
            text-align: center; max-width: 560px; }
/* The registers inside an empty state (his 08-01 call, made on the builder's rail
   widgets and applied to the composition everywhere): the HEADLINE reads in body —
   it is still the panel's answer — only the explanation under it is quiet, and the
   glyph sits one register below the words it decorates. */
.mk-empty .glyph { font-size: 20px; color: var(--mk-text-quiet); display: block; margin-bottom: var(--mk-space-3); }
.mk-empty .title { font-family: var(--mk-font-app); font-weight: 600; font-size: var(--mk-text-lg);
                   color: var(--mk-text-muted); margin: 0 0 var(--mk-space-1); }
.mk-empty .hint { font-size: var(--mk-text-sm); color: var(--mk-text-quiet); margin: 0 0 var(--mk-space-4); }

/* ── Status badge ────────────────────────────────────────────────────────────
   One uniform shell for every status; only the dot changes. The label carries
   identity, the dot colour encodes phase (DECIDED 2026-07-02, variant B). */
.mk-badge { display: inline-flex; align-items: center; font-weight: 500;
            white-space: nowrap; background: var(--mk-badge-bg); border: 1px solid var(--mk-hairline);
            color: var(--mk-text-muted);
            border-radius: 999px; font-size: var(--mk-text-sm); gap: 6px; padding: 4px 12px; }
.mk-dot { border-radius: 50%; flex: none; width: 8px; height: 8px;
          box-shadow: inset 0 0 0 1px var(--mk-wash-press); }
.mk-badge.sz-s { font-size: var(--mk-text-xs); gap: 4px; padding: 3px 8px; }
.mk-badge.sz-s .mk-dot { width: 7px; height: 7px; }
.mk-badge.sz-l { font-size: var(--mk-text-md); gap: 8px; padding: 6px 12px; }
.mk-badge.sz-l .mk-dot { width: 10px; height: 10px; }
/* A commercially-important attribute — not a lifecycle status — wears the gold
   tint (PV3-8: «repeat Leitner client» rendered as an unreadable grey tag; a
   repeat client is money and relationship, and it must be findable at a glance).
   Lineage: Polaris attention Badge. Still read-only: a pill is never a control. */
.mk-badge.tone-gold { background: color-mix(in srgb, var(--mk-accent) 12%, transparent);
                      border-color: color-mix(in srgb, var(--mk-accent) 38%, transparent);
                      color: var(--mk-text-muted); }
.mk-badge .bico { width: 13px; height: 13px; flex: none; color: var(--mk-accent);
                  fill: none; stroke: currentColor;
                  stroke-width: var(--mk-icon-stroke);
                  stroke-linecap: butt; stroke-linejoin: miter; }

/* Availability and money states in Builder cards are labels, not lifecycle dots.
   Their icon + word carry the meaning and the restrained tint makes the current
   answer visible inside a dense card header. */
.mk-status-label { display: inline-flex; align-items: center; gap: 6px;
                   padding: 2px 10px 2px 8px; border-radius: 999px;
                   font-size: var(--mk-text-sm); font-weight: 350; line-height: 1.45;
                   flex: none; white-space: nowrap; }
.mk-status-label > svg { width: 18px; height: 18px; flex: none; color: currentColor;
                         fill: none; stroke: currentColor;
                         stroke-width: var(--mk-icon-stroke);
                         stroke-linecap: butt; stroke-linejoin: miter; }
/* EVERY LABEL VARIANT WEARS AN EDGE (Ihor, 08-19). .mk-badge and .mk-tag — Lifecycle and
   Compact — have carried `1px solid var(--mk-hairline)` since they were written; the State
   variants had a tint and nothing else, so one component's three variants had two different
   anatomies. They now share it: shell, optional mark, label. The edge is the status ink at
   an opacity, so it composites over whatever plate and ground it lands on and needs no
   per-theme value. It also does real work — a 1px line answers to 3:1 as a shape, and it is
   what lets the plate stay the quiet .22 veil instead of the deep block that overshot. The
   error takes 65% where the others take 55%: red carries the least luminance of any hue, so
   the same opacity buys a dimmer line (2.74:1 against 3.42–4.07 for the rest). */
.mk-status-label.is-success { color: var(--mk-success-text);
  background: color-mix(in srgb, var(--mk-success-tint) 55%, var(--mk-surface));
  border: 1px solid color-mix(in srgb, var(--mk-success-text) 55%, transparent); }
.mk-status-label.is-warning { color: var(--mk-warning-text);
  background: color-mix(in srgb, var(--mk-warning-tint) 55%, var(--mk-surface));
  border: 1px solid color-mix(in srgb, var(--mk-warning-text) 55%, transparent); }
.mk-status-label.is-error { color: var(--mk-error-text);
  background: color-mix(in srgb, var(--mk-error-tint) 45%, var(--mk-surface));
  border: 1px solid color-mix(in srgb, var(--mk-error-text) 65%, transparent); }
/* The 55%/45% mix above is a LIGHT-theme recipe: there the tint is an opaque pastel and
   mixing it toward white gives the pill a wash instead of a block. On dark the tint is
   already an alpha veil, so the mix dilutes a dilution and the plate came out at 1.13:1
   against the card — invisible (Ihor, 08-19). On this ground the tint token IS the plate;
   every other tint consumer in the file already uses it undiluted. */
[data-theme="dark"] .mk-status-label.is-success { background: var(--mk-success-tint); }
[data-theme="dark"] .mk-status-label.is-warning { background: var(--mk-warning-tint); }
[data-theme="dark"] .mk-status-label.is-error   { background: var(--mk-error-tint); }
.mk-status-label.sz-s { gap: 4px; padding: 1px 7px 1px 6px; font-size: var(--mk-text-xs); }
.mk-status-label.sz-s > svg { width: 14px; height: 14px; }
.mk-status-label.sz-l { gap: 8px; padding: 4px 12px 4px 10px; font-size: var(--mk-text-md); }
.mk-status-label.sz-l > svg { width: 20px; height: 20px; }

/* ── SLA marker ──────────────────────────────────────────────────────────── */
.mk-sla { font-family: var(--mk-font-app); font-size: var(--mk-text-xs); display: inline-flex; gap: 6px;
          align-items: center; padding: 3px 9px; border-radius: 999px;
          border: 1px solid var(--mk-hairline); }
.mk-sla.ok { background: var(--mk-surface-sunken); }
.mk-sla.warn { background: var(--mk-warning-tint); color: var(--mk-warning-text); }
.mk-sla.breach { background: var(--mk-error-tint); color: var(--mk-error-text); font-weight: 400; }
.mk-sla.sz-s { padding: 2px 7px; }
.mk-sla.sz-l { padding: 5px 11px; font-size: var(--mk-text-md); }

/* ── List ───────────────────────────────────────────────────────────────────
   A list is a reading structure first. The base resets browser furniture;
   explicit variants add bullets, registry icons, or structured view-only rows.
   Navigation and actions keep their own Link / Row action semantics instead of
   turning an otherwise quiet list item into a hidden target. */
.mk-list { margin: 0; padding: 0; list-style: none; color: var(--mk-text); }
.mk-list > li + li { margin-top: var(--mk-space-2); }
.mk-list-bulleted { padding-left: 20px; list-style: disc; }
.mk-list-bulleted > li { padding-left: 4px; }
.mk-list-icon > li { display: grid; grid-template-columns: 20px minmax(0, 1fr);
                     gap: var(--mk-space-3); align-items: start; }
.mk-list-icon > li > svg { width: 18px; height: 18px; margin-top: 2px;
                           fill: none; stroke: currentColor;
                           stroke-width: var(--mk-icon-stroke);
                           stroke-linecap: butt; stroke-linejoin: miter;
                           color: var(--mk-accent); }
.mk-list-structured { border-top: 1px solid var(--mk-hairline); }
.mk-list-structured > li,
.mk-list-item { display: grid; grid-template-columns: 24px minmax(0, 1fr) max-content;
                gap: var(--mk-space-3); align-items: center; min-height: 52px;
                margin: 0; padding: 8px 0; border-bottom: 1px solid var(--mk-hairline); }
.mk-list-leading { display: inline-flex; align-items: center; justify-content: center;
                   width: 24px; height: 24px; color: var(--mk-accent); }
.mk-list-leading > svg { width: 18px; height: 18px; fill: none; stroke: currentColor;
                         stroke-width: var(--mk-icon-stroke);
                         stroke-linecap: butt; stroke-linejoin: miter; }
.mk-list-copy { min-width: 0; display: grid; gap: 2px; }
.mk-list-title { font-size: var(--mk-text-md); font-weight: 500; line-height: 22px; }
.mk-list-description { font-size: var(--mk-text-sm); line-height: 20px;
                       color: var(--mk-text-muted); }
.mk-list-trailing { font-size: var(--mk-text-sm); color: var(--mk-text-muted);
                    text-align: right; white-space: nowrap; }
@media (max-width: 767px) {
  .mk-list-structured > li,
  .mk-list-item { grid-template-columns: 24px minmax(0, 1fr); }
  .mk-list-trailing { grid-column: 2; text-align: left; white-space: normal; }
}

/* ── Dense table ─────────────────────────────────────────────────────────────
   Below --mk-bp-md (768px) a dense table becomes a card list. It never scrolls
   horizontally. The page owns that switch; this file owns both appearances. */
/* A table is a card too — it wears the card dress, not a heavier outline of its own. */
.mk-table { width: 100%; border-collapse: collapse; background: var(--mk-surface);
            border: 1px solid var(--mk-hairline-mid); border-radius: var(--mk-radius-md); overflow: hidden;
            box-shadow: var(--mk-shadow-card); }
.mk-table th { font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs); font-weight: 350; letter-spacing: .1em;
               text-transform: uppercase; color: var(--mk-text-muted); text-align: left;
               padding: 10px var(--mk-table-pad-x); border-bottom: 1px solid var(--mk-hairline);
               background: var(--mk-surface-sunken); }
.mk-table td { font-size: var(--mk-table-font); line-height: 20px;
               padding: var(--mk-table-pad-y) var(--mk-table-pad-x);
               border-bottom: 1px solid var(--mk-hairline); vertical-align: middle; }
.mk-table tr:hover td, .mk-table tr.is-hover td { background: var(--mk-surface-band-strong); }

/* ── Sortable column ─────────────────────────────────────────────────────────
   The header cell keeps aria-sort; the button inside it is what gets pressed, because a
   <th> is not a control. The direction is carried by the caret glyph, not by colour — a
   monochrome screenshot must still say which column is sorted and which way.
   Ihor 2026-07-09: sorting first, column separators later ("посмотрим, как это будет
   выглядеть без них"). */
th.mk-sortable { padding: 0; }
th.mk-sortable .mk-sort {
  display: flex; align-items: center; gap: 6px; width: 100%; min-width: 0;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit; text-align: left;
  background: transparent; border: 0; cursor: pointer;
  padding: 10px var(--mk-table-pad-x); min-height: var(--mk-tap-min);
}
/* The label truncates; the caret never does. A clipped sort indicator on a narrow column
   is the one part of the header that must survive — it is the only thing that says which
   column is sorted. */
th.mk-sortable .mk-sort .lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
th.mk-sortable .mk-sort:hover { color: var(--mk-text-label); background: var(--mk-wash); }
th.mk-sortable .mk-sort:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
/* The caret is an SVG, not a text glyph. ds:lint caught the first attempt: a 10px "▲" is a
   10px character, and the 13px floor is a rule about text. An icon is held to 3:1 contrast
   instead — which is the right threshold, and which a shrunken glyph would have dodged.
   It is always present, so the header does not reflow when a column becomes sorted.
   Ihor 2026-07-27: the single faint triangle on every column read as noise, not a control.
   The glyph is a chevron pair on every sortable column, and BOTH halves stay when the column
   sorts — his second round overturned the swap to a single centred chevron ("когда ты
   убираешь одну из стрелок, оно не очень правильно"). Direction is carried by emphasis:
   the active half goes accent and heavier, the idle half stays faint. */
/* 12px micro mark — the L4 exception (concepts-icon-language.md): stays heavy */
th.mk-sortable .mk-sort .caret {
  flex: none; width: 12px; height: 12px; color: var(--mk-text-muted);
  fill: none; stroke: currentColor; stroke-width: var(--mk-icon-stroke);
  stroke-linecap: butt; stroke-linejoin: miter;
}
/* The sorted column keeps the whole pair; the active half speaks through weight and accent,
   the idle half fades further back. The title itself keeps the header ink — the caret alone
   marks the column, so the row of titles stays one even tone. */
th[aria-sort="ascending"] .mk-sort .caret,
th[aria-sort="descending"] .mk-sort .caret { opacity: 1; }
th[aria-sort="ascending"] .mk-sort .caret .up,
th[aria-sort="descending"] .mk-sort .caret .dn { stroke: var(--mk-accent); stroke-width: 2; }
th[aria-sort="ascending"] .mk-sort .caret .dn,
th[aria-sort="descending"] .mk-sort .caret .up { opacity: .35; }
th:not([aria-sort]) .mk-sort .caret,
th[aria-sort="none"] .mk-sort .caret { opacity: .45; }
/* ── The expanding row ────────────────────────────────────────────────────────
   Lifted into the layer 08-06 from the enquiry queue, where it shipped as the
   page-local qrow/xrow pair and was then copied into the builder's lists and the
   approval page — four surfaces, one pattern, no shared name. The decisions are
   the queue's own (07-27): clicking the ROW expands it in place; the record's one
   door is the guest's NAME, dressed as a link; the caret button holds the row's
   right corner and mirrors the open state; the detail content sits on WHITE while
   the open row keeps its wash. The prototypes still carry their local copies —
   reconciling them onto these names is its own pass. */
.mk-table tr.mk-row-expand { cursor: pointer; }
.mk-table tr.mk-row-expand:hover td, .mk-table tr.mk-row-expand.is-hover td { background: var(--mk-wash-faint); }
.mk-table tr.mk-row-expand:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-table tr.mk-row-expand.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-table tr.mk-row-expand.is-open td {
  background: color-mix(in srgb, var(--mk-surface-sunken) 55%, transparent);
  border-bottom-color: transparent;
}
.mk-table tr.mk-row-detail > td { padding: 0; border-bottom: 1px solid var(--mk-hairline);
                                  background: var(--mk-surface); }
.mk-table .mk-row-detail-panel { padding: 4px var(--mk-table-pad-x) 18px; }
/* the caret is the DS icon-button; only the rotation belongs to the pattern —
   DOWN at rest, UP when open (our chevrons never point sideways) */
.mk-table .mk-icon-btn[aria-expanded] svg { transition: transform .16s ease; }
.mk-table .mk-icon-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  .mk-table .mk-icon-btn[aria-expanded] svg { transition: none; }
}

.mk-guest { font-weight: 500; }
.mk-subline { font-size: var(--mk-text-sm); color: var(--mk-text-muted); margin-top: 2px; }
.mk-subline.dup { color: var(--mk-warning-text); }
.mk-src { font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs); letter-spacing: .08em;
          text-transform: uppercase; color: var(--mk-text-muted); }
.mk-sla-cell { font-family: var(--mk-font-app); font-size: var(--mk-text-xs); white-space: nowrap; }
.mk-sla-cell.warn { color: var(--mk-warning-text); }
.mk-sla-cell.breach { color: var(--mk-error-text); font-weight: 600; }
.mk-sla-cell.idle { color: var(--mk-text-quiet); }

/* ── Enquiry card (the table's mobile form) ──────────────────────────────── */
.mk-card { background: var(--mk-surface); border: 1px solid var(--mk-hairline-mid);
           border-radius: var(--mk-radius-md); padding: 12px 14px;
           min-height: var(--mk-tap-min); box-shadow: var(--mk-shadow-card); cursor: default; }
/* Only a card with a real stretched door advertises navigation. Documentation may
   hold the same visual state with .is-hover without introducing a live target. */
.mk-card:has(.mk-stretched-link) { cursor: pointer; }
.mk-card:has(.mk-stretched-link):hover, .mk-card.is-hover {
  background: var(--mk-surface); box-shadow: var(--mk-shadow-pop);
}
/* The card and its named door answer as one target. A sibling tool keeps its own
   hover voice, so hovering that control does not also warm the record link. */
.mk-card:has(.mk-stretched-link):hover:not(:has(button:hover)) .mk-object-link,
.mk-card.is-hover .mk-object-link {
  color: color-mix(in srgb, var(--mk-accent) 72%, var(--mk-text)); text-decoration-color: currentColor;
  text-decoration-thickness: 2px;
}
/* Focus lands on the named link semantically, but the complete card answers visually. */
.mk-card .mk-stretched-link:focus-visible { outline: none; }
.mk-card:has(.mk-stretched-link:focus-visible), .mk-card.is-focus {
  outline: 2px solid var(--mk-camel-mid); outline-offset: 2px;
}
.mk-card:has(.mk-stretched-link:focus-visible) .mk-object-link,
.mk-card.is-focus .mk-object-link {
  color: var(--mk-accent); text-decoration-color: var(--mk-accent);
}
.mk-card .row1 { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.mk-card .name { font-weight: 500; font-size: var(--mk-text-md); line-height: 22px; }
.mk-card .row2 { font-size: var(--mk-text-sm); line-height: 20px; margin-top: 4px; opacity: .85; }
.mk-card .row3 { display: flex; align-items: center; gap: 12px; margin-top: 6px;
                 font-family: var(--mk-font-app); font-size: var(--mk-text-xs); letter-spacing: .06em;
                 color: var(--mk-text-muted); }
.mk-card .row3 .owner { margin-left: auto; }

/* ── Shell: topbar + primary navigation ──────────────────────────────────────
   Concept B, "the Ledger" — Ihor's verdict 2026-07-10
   (_bmad-output/design-specs/pages/05-nav-shell/concepts.md).
   The sidebar is the product's table of contents: the three movements
   (ux-strategy §3 — Understand · Compose · Commit) are visible group headings,
   entries are words, not icons. Below 768px it becomes a horizontal strip that
   scrolls — never a hamburger, because hiding the navigation is exactly the
   defect this shell exists to close (review 2026-07-10, B-1).
   Undesigned entries render and lead to an honest stub (prototypes/shell/);
   their presence is deliberate — the client reviews the whole product shape.
   Before this block every prototype carried its own copy-pasted .topbar. */

/* Skip link — first tab stop on every shell page. */
.mk-skip { position: absolute; left: 0; top: 0; transform: translateY(-200%);
           display: inline-flex; align-items: center; min-height: var(--mk-tap-min);
           padding: 10px 16px; background: var(--mk-surface); color: var(--mk-text-muted);
           font-size: var(--mk-text-sm); z-index: 30; }
.mk-skip:focus-visible { transform: none; outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }

/* Topbar. min-height pins the bar's geometry whether or not a page mounts the
   search slot, so navigation never jumps between pages. */
.mk-topbar { display: flex; align-items: center; gap: 20px; min-height: 72px;
             padding: 14px var(--mk-page-pad); border-bottom: 1px solid var(--mk-hairline);
             background: var(--mk-surface); }
.mk-topbar-wordmark { flex: none; white-space: nowrap; text-decoration: none; position: relative;
                      font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs); font-weight: 350;
                      letter-spacing: .18em; text-transform: uppercase; color: var(--mk-text-muted); }
.mk-topbar-wordmark::after { content: ""; position: absolute; inset: -14px 0; } /* 20px text → 48px touch */
a.mk-topbar-wordmark:hover { color: var(--mk-text-label); }
.mk-topbar-wordmark:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
/* The search slot is OPTIONAL and only mounts where a search actually works
   (today: the enquiry inbox). A decorative search field is a lie about scope —
   see concepts.md question 3. Pages without it use .mk-topbar-spacer instead. */
.mk-topbar-search { flex: 1; display: flex; justify-content: center; min-width: 0; }
.mk-topbar-search .mk-input { max-width: 420px; }
.mk-topbar-spacer { flex: 1; }
.mk-topbar-id { flex: none; display: flex; align-items: baseline; gap: 8px; white-space: nowrap; }
/* Theme toggle — an .mk-icon-btn in the topbar (shell.js binds it). State is
   carried by aria-pressed and the swapped glyph, never by colour alone. */
.mk-theme-toggle .ico-sun { display: none; }
[data-theme="dark"] .mk-theme-toggle .ico-sun { display: block; }
[data-theme="dark"] .mk-theme-toggle .ico-moon { display: none; }
.mk-topbar-id .role { font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs);
                      letter-spacing: .1em; text-transform: uppercase; color: var(--mk-text-label); }
.mk-topbar-id .name { font-size: var(--mk-text-sm); font-weight: 500; }

/* Shell frame: nav column + main. The nav column stretches with the page so
   its hairline runs the full height; the sticky inner keeps the entries in
   view on long pages. */
.mk-shell { display: flex; align-items: stretch;
            /* fill the viewport below the 72px topbar (+1px border) so the nav's
               hairline doesn't stop mid-screen on short pages */
            min-height: calc(100dvh - 73px); }
.mk-main { flex: 1; min-width: 0; }
.mk-nav { flex: none; width: var(--mk-nav-w); border-right: 1px solid var(--mk-hairline);
          /* same surface as the topbar — on the page ground it blended into the beige
             (Ihor, 2026-07-10); --mk-surface keeps the match in both themes */
          background: var(--mk-surface); }
.mk-nav-inner { position: sticky; top: 0; max-height: 100dvh; overflow-y: auto;
                /* the rail's scroll is its own — reaching its end must not hand
                   the wheel to the page (Ihor, 07-27, the 13″ MacBook pass) */
                overscroll-behavior: contain;
                /* The floor, third cut (07-28). 40px read as a hole on tall glass; his
                   number is 18 — the rail's own measure, the same as its ceiling, so
                   the column closes with the rhythm it opened on. With the account
                   block's own 8 that is 26px of real air under the name. */
                display: flex; flex-direction: column; padding: 18px 12px 18px; }

/* Sidebar-first desktop shell. The brand and account controls live in the rail,
   while the topbar remains in the DOM as the source for the phone tab bar. */
@media (min-width: 768px) {
  body.mk-sidebar-shell > .mk-topbar { display: none; }
  body.mk-sidebar-shell .mk-shell { min-height: 100dvh; }
  body.mk-sidebar-shell .mk-nav-inner { min-height: 100dvh; }
}
.mk-nav-brand { display: block; padding: 4px 12px 34px; text-decoration: none; }
.mk-nav-brand-mark { display: block; width: 34px; height: 34px; }
[data-theme="dark"] .mk-nav-brand-mark { filter: invert(1); }
.mk-nav-brand--wordmark { display: flex; flex-direction: column; align-items: center;
                          padding-inline: 12px; }
.mk-nav-brand-wordmark { display: block; width: 100%; height: auto; }
[data-theme="dark"] .mk-nav-brand-wordmark { filter: invert(1); }
/* The ordinary brand subtitle keeps the established caps treatment. A wordmark
   masthead narrows that role to Subtitle 3 below: one centred 12px line. */
.mk-nav-brand-sub { display: block; margin-top: 8px;
                    font-family: var(--mk-font-app-caps); font-size: var(--mk-text-sm);
                    font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
                    line-height: 1.35; color: var(--mk-text-label); }
/* the descriptor under the wordmark speaks for the BRAND, so it takes the brand's own
   caps face (his 07-31 ask). Size unchanged — subtitle-3 exists for this line alone. */
.mk-nav-brand--wordmark .mk-nav-brand-sub { font-family: var(--mk-font-brand-caps);
                                            font-size: var(--mk-text-wordmark); font-weight: 400;
                                            text-align: center; white-space: nowrap; }
.mk-nav-brand:hover .mk-nav-brand-sub { color: var(--mk-text-label); }
.mk-nav-brand:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px;
                              border-radius: var(--mk-radius-sm); }
.mk-nav-tail { margin-top: auto; padding-top: 14px; border-top: 1px solid var(--mk-hairline); }
/* A label and a CONTROL centre on each other. .mk-split aligns on the baseline, which is
   right when both sides are text — a name and its value — but a switch has no baseline to
   meet, so the word hung eight pixels below the track (his 07-28 catch). The row also
   holds its direction: .mk-split stacks under 520px, and a toggle sitting under its own
   label has stopped being a row. */
.mk-nav-theme { padding: 8px 12px; flex-direction: row; align-items: center; }
.mk-nav-theme-label { font-size: var(--mk-text-sm); color: var(--mk-text-muted); }
.mk-theme-switch { position: relative; flex: none; width: 48px; height: 26px; padding: 0;
                   cursor: pointer; border-radius: 999px;
                   border: 1px solid var(--mk-hairline-strong);
                   background: var(--mk-wash-strong);
                   transition: background .15s ease, border-color .15s ease; }
/* The knob is light in BOTH states; what changed 08-06 (Ihor, two passes) is the
   rest state's visibility: the borderless white knob melted into the pale track, so
   at rest it carries the strong hairline. When on, the gold track is the contrast
   and the border leaves. (The first cut made the resting knob gold; he pulled it
   back the same day — «светлее, белым или серым, с небольшим бордером».) */
.mk-theme-switch-knob { position: absolute; top: 2px; left: 2px; width: 20px; height: 20px;
                        border-radius: 50%; background: var(--mk-accent-contrast);
                        border: 1px solid var(--mk-hairline-strong);
                        box-shadow: 0 1px 2px rgba(0,0,0,.25);
                        display: inline-flex; align-items: center; justify-content: center;
                        transition: transform .15s ease; }
/* the glyph sits on a near-white knob in BOTH states, so it cannot use a text
   register: --mk-text-muted is near-white on dark and the sun vanished into the knob
   (1.00:1, found 08-18). The accent fill reads 4.85:1 there and ties the control to
   its own track. */
.mk-theme-switch-knob svg { width: 12px; height: 12px; color: var(--mk-accent-fill); fill: none;
                            stroke: currentColor;
                            /* +.4 over the icon stroke: at 12px inside a 20px knob the standard
                               weight read as a hairline, whatever colour it wore */
                            stroke-width: calc(var(--mk-icon-stroke) + .4);
                            stroke-linecap: butt; stroke-linejoin: miter; }
.mk-theme-switch .ico-sun { display: inline; }
.mk-theme-switch .ico-moon { display: none; }
/* ON repaints the TOGGLE, not the track (Ihor, 08-18 — REVISING the 08-06 cut,
   which put the gold on the track and kept the knob light). That worked on beige:
   a white knob on a gold track. On dark it broke outright — the knob is
   --mk-surface, so a DARK circle sat on a saturated gold block and the control
   read as one orange pill with a hole in it. Colouring the knob instead is
   theme-agnostic: the knob carries the accent, the track keeps the same neutral
   wash it wears at rest, and position plus colour say the state. */
/* ON fills the TRACK and leaves the knob alone (Ihor, 08-18, third pass on this
   control). The 08-06 cut had it right in principle — gold track, light knob — and
   only failed on dark because the knob was --mk-surface, i.e. dark. The knob now
   takes --mk-accent-contrast, the value that exists precisely to read on an accent
   fill, so it is near-white in BOTH themes and the pattern stops being beige-only. */
.mk-theme-switch[aria-pressed="true"] { background: var(--mk-accent-fill);
                                        border-color: var(--mk-accent-fill); }
/* the moon sits ON the near-white knob, so it speaks in ink — gold on near-white
   was the accent talking to itself (Ihor, 08-18). --mk-ink, not --mk-black: the
   token file's own rule is to reach for the brand near-black first. */
.mk-theme-switch[aria-pressed="true"] .mk-theme-switch-knob svg { color: var(--mk-accent-fill); }
.mk-theme-switch[aria-pressed="true"] .mk-theme-switch-knob { transform: translateX(22px);
                                                               border-color: transparent; }

.mk-theme-switch[aria-pressed="true"] .ico-sun { display: none; }
.mk-theme-switch[aria-pressed="true"] .ico-moon { display: inline; }
.mk-theme-switch:hover,
.mk-theme-switch.is-hover {
  border-color: var(--mk-accent);
  box-shadow: 0 0 0 1px var(--mk-accent);
}
.mk-theme-switch:hover .mk-theme-switch-knob,
.mk-theme-switch.is-hover .mk-theme-switch-knob { border-color: var(--mk-accent); }
.mk-theme-switch[aria-pressed="true"]:hover,
.mk-theme-switch[aria-pressed="true"].is-hover {
  /* a ring, not a refill: swapping the whole track's colour under the cursor read as
     the control changing state rather than answering the pointer (Ihor, 08-18) */
  box-shadow: 0 0 0 1px var(--mk-accent-deep);
}
/* No ring on the pressed knob under the pointer. It carried --mk-surface first — a
   black outline around a near-white knob on dark — and then inherited the generic
   hover's --mk-accent ring, which is the same problem in gold (Ihor, 08-18, twice).
   The track's own hover ring answers the pointer; the knob does not need to. */
.mk-theme-switch[aria-pressed="true"]:hover .mk-theme-switch-knob,
.mk-theme-switch[aria-pressed="true"].is-hover .mk-theme-switch-knob { border-color: transparent; }
.mk-theme-switch:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-theme-switch.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
.mk-nav-user { padding: 10px 12px 8px; margin-top: 6px;
               /* the account block rides the viewport's floor — it needs real air
                  under it, not a 2px sliver (Ihor, 07-27, the 13″ MacBook pass) */
               border-top: 1px solid var(--mk-hairline); }
.mk-nav-user-avatar { width: 38px; height: 38px; border-radius: 50%; flex: none;
                      display: inline-flex; align-items: center; justify-content: center;
                      background: var(--mk-surface-sunken);
                      border: 1px solid var(--mk-hairline-strong);
                      font-family: var(--mk-font-app-caps); font-size: var(--mk-text-sm); }
/* the person leads, the role steps back — at 500 the name read quieter than the
   caps line under it, which is backwards (seen 07-29, once the role took the
   house caps size) */
.mk-nav-user > span:last-child { display: grid; gap: 2px; min-width: 0; }
.mk-nav-user-name { display: block; font-size: var(--mk-text-md); font-weight: 600; line-height: 1.25; }
.mk-nav-user-role { display: block; font-family: var(--mk-font-app);
                    font-size: var(--mk-text-sm); font-weight: 350; letter-spacing: 0;
                    text-transform: none; color: var(--mk-text-muted); line-height: 1.3; }

.mk-nav-group + .mk-nav-group { margin-top: 18px; }
/* The sidebar shell used to zero this gap, which left a group heading pressed
   against the last link of the group BEFORE it — proximity tied the label to
   the wrong group (Ihor, 08-06). The rail keeps its own 18px measure. */
/* Admin is set apart — the shell's one entry that is about the product, not
   the journey. A divider, not a heading: it belongs to no movement. */
.mk-nav-group.mk-nav-foot { border-top: 1px solid var(--mk-hairline); padding-top: 18px; }
/* The group heading is Subtitle 2. Caps and tracking distinguish the group
   from its entries; muted ink keeps this structural label secondary. */
.mk-nav-heading { display: block; font-family: var(--mk-font-app-caps); font-size: var(--mk-text-sm);
                  font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
                  color: var(--mk-text-quiet); padding: 0 12px 6px;
                  /* pinned: the rail must measure the same on every page — docs.css
                     sets body line-height 1.5 and product pages leave it unset, and
                     the 4px-per-heading drift made the rail jump between pages
                     (Ihor, 08-10). `normal` here refuses the inherited 1.5 and lands
                     on the face's own metric — the product's rendering, exactly. */
                  line-height: normal; }
.mk-nav-list { list-style: none; margin: 0; padding: 0; }
.mk-nav-link { position: relative; display: flex; align-items: center; gap: 8px;
               min-height: var(--mk-tap-min); padding: 10px 12px;
               border-radius: var(--mk-radius-sm); text-decoration: none;
               font-family: var(--mk-font-app); font-size: var(--mk-text-sm);
               font-weight: 500; letter-spacing: 0; color: var(--mk-text-muted); }
.mk-nav-link:hover,
.mk-nav-link.is-hover { background: var(--mk-accent-wash); }
.mk-nav-link:focus-visible,
.mk-nav-link.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-nav-link[aria-disabled="true"] { color: var(--mk-text-quiet); cursor: not-allowed; }
.mk-nav-link[aria-disabled="true"]:hover { background: transparent; }
/* Current entry: accent bar + accent-tint fill + aria-current — never colour
   alone. (Was a surface fill + shadow, invisible once the nav itself moved to
   --mk-surface — Ihor, 2026-07-10.) */
.mk-nav-link[aria-current] { background: var(--mk-accent-wash-strong); }
.mk-nav-link[aria-current]::before { content: ""; position: absolute; left: 0; top: 8px; bottom: 8px;
                                     width: 3px; border-radius: 999px; background: var(--mk-accent); }
/* Not-yet-designed marker: `tbd`, one word for all of them (Ihor, 29 Jul).
   It used to be graded — `soon` for a roadmap surface, `tbd` where the entry
   itself was an open client question — and the grades bought nothing in the
   rail: five entries reading five ways is noise around a single answer, and
   `soon` was a promise about timing that nobody had made.
   The entry it sits on is a <span>: same ink, same hover, no destination.
   No style of its own (Ihor, 29 Jul): it IS the section eyebrow, greyed. A nav tag and a
   section title are the same kind of small caps label, and inventing a third size and a
   third tracking for this one was how the rail ended up disagreeing with the record. */
.mk-nav-tag { margin-left: auto; font-family: var(--mk-font-app-caps); font-size: var(--mk-text-sm);
              font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
              color: var(--mk-text-label); }

/* ── Claims — who is making this claim? (round A 07-11 → round A2 07-12,
   concepts-claim-author.md · concepts-claim-author-a2.md)
   THE LAW, after four failed readers on this square inch: a HUMAN claim is
   WORDS; a MACHINE claim is a NAMEPLATE; ABSENCE is an INSTRUCTION.
   · Machine: the rectangular nameplate with the system's literal shortname —
     PMS, THAWANI, ERP — plus the tail (.who) carrying instance + freshness
     ("· Barkaa · 09:14"). The one authorship mark that decoded on every read:
     it is not a code, it is the system's name in letters.
   · Human: the verb and the author's name, in words — "Confirmed — Amira
     Said · Tue 14:20". No token. The initials monogram died 07-12 («эти
     буквы… никто до этого не додумается» — the fourth reader), after the
     dot code died 07-11 (three readers). A mark that needs the spec is
     private notation, not a grammar.
   · Unattested: the empty ring died the same day («в UI-библиотеках нет
     такого паттерна»). Absence is written as the ACTION OWED, never a hole:
     "Not confirmed yet — call Hassan · last try 11:40". The claim is the
     chase note (P2: the waiting is the product's centre).
   The door left open: when the CRM brings staff photos (R-Q2), a real face
   may slot in BESIDE the name — progressive, like an email client; never
   replacing the words, and never falling back to initials.
   `booked` (a PMS fact) and `confirmed` (a colleague's word) still never
   share an identity (model §2): nameplate-present = machine, name-in-words =
   person, and the two dialects keep their type treatments (caps vs sentence).
   Tokenless stays tokenless: first-party facts, mixed roll-ups, "not
   required" lines (not claims at all).
   Valence is a separate axis: .is-ok / .is-pending / .is-blocked colour the
   claim; authorship stays in the words — colour is never the only signal. */
.mk-claim { display: inline-flex; align-items: center; gap: 6px; min-width: 0; color: var(--mk-text-muted); }
.mk-claim .who { color: var(--mk-text-muted); font-weight: 350; letter-spacing: normal; text-transform: none; }
.mk-claim .tok { flex: none; display: inline-flex; align-items: center; justify-content: center;
                 font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs); font-weight: 600;
                 letter-spacing: 0; text-transform: uppercase; }
.mk-claim-machine { font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs);
                    letter-spacing: .08em; text-transform: uppercase; }
.mk-claim-machine .tok { height: 20px; padding: 0 6px; border-radius: 4px; letter-spacing: .06em;
                         background: color-mix(in srgb, currentColor 12%, transparent); }
.mk-claim-human { font-family: var(--mk-font-app); font-size: var(--mk-text-sm); }

/* mk-tag — one compact, read-only label. The warm shell is shared by product
   classifications and design-system tokens; typography tells those two jobs apart.
   Product words stay in the application face. Token names opt into .mk-tag-token and
   the documented code face. Facts remain elements, never a «·»-glued string. */
.mk-tag { display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px;
          border: 1px solid var(--mk-hairline); border-radius: 999px;
          font-family: var(--mk-font-app); font-size: var(--mk-text-sm); color: var(--mk-text);
          background: var(--mk-surface-sunken); white-space: nowrap; }
.mk-tag.sz-s { gap: 4px; padding: 1px 6px; font-size: var(--mk-text-xs); }
.mk-tag.sz-l { gap: 8px; padding: 4px 10px; font-size: var(--mk-text-md); }
.mk-tag-token { font-family: var(--mk-font-code); }
          /* full ink, not muted — grey chips flirt with AA and read weak (Ihor, 07-14).
             It sat at 13px until 07-29, when the quiet 13px body was struck out. */
/* mk-tag-pms REMOVED 07-14, hours after birth: PMS codes never show to an agent
   (Ihor's final law — «нахуя LUX-W, если она видит Garden room?»). The code mapping is
   the integration's private business. */
.mk-claim.is-ok { color: var(--mk-success-text); }
.mk-claim.is-pending { color: var(--mk-warning-text); }
/* A third valence, added 2026-07-11 with the provider-first catalogue: the claim is not
   pending, it is REFUSED. An expired supplier contract is not "waiting" — it is a promise
   the company cannot keep, and the agent is one click from selling it. It reads as a
   refusal, and it says so in words as well as colour. */
.mk-claim.is-blocked { color: var(--mk-error-text); font-weight: 600; }
/* M6-11 (07-12), corrected the same night at the v3 review: the first cut stacked
   every child on its own grid row, which made continuation lines OPEN with the
   «·» separator («где ты видел, чтобы строчка начиналась с bullet point?»). The
   rule as Ihor stated it: the nameplate and its words share ONE line; long words
   wrap at FULL width underneath — never in a ragged column beside the token, and
   a separator ends a line, it never starts one. Block flow does exactly this:
   the token rides the first text line, the words stream after it and wrap edge
   to edge. */
@media (max-width: 767px) {
  .mk-claim:has(> .tok) { display: block; min-width: 0; }
  .mk-claim:has(> .tok) > .tok { vertical-align: baseline; margin-right: 6px; }
}

/* The send gate, decomposed — term cells of the answer bar (1b concept B).
   Each term is a REAL control: activating it jumps to the first failing item
   of that term. The cells summarise; the items stay the source of truth. */
.mk-answer-terms { display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap; }
.mk-term { display: inline-flex; align-items: center; gap: 8px; min-height: var(--mk-tap-min);
           padding: 8px 14px; background: var(--mk-surface);
           border: 1px solid var(--mk-hairline-strong); border-radius: var(--mk-radius-sm);
           cursor: pointer; font: inherit; text-align: left; }
.mk-term:hover { background: var(--mk-surface-sunken); }
.mk-term:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }
/* Phone: stacked, the term cells wore the secondary button's face and the send
   zone became a wall of six identical bordered rows — statuses and actions
   indistinguishable (pilot blind walk L2 + the cold reader's #2). A term is a
   CLAIM you can open: on the phone it sheds the button box and wears a trailing
   chevron — the disclosure grammar, not the action grammar. */
@media (max-width: 767px) {
  .mk-term { border: 0; background: none; padding: 6px 2px; width: 100%; }
  .mk-term::after { content: ""; flex: none; width: 8px; height: 8px; margin-left: auto;
                    border-right: 1.75px solid var(--mk-text-muted);
                    border-bottom: 1.75px solid var(--mk-text-muted);
                    transform: rotate(-45deg); }
}

/* ── Menu — an attached popover of choices (R5/R6, 07-12). Lineage: menu button /
   picker — the pattern every product uses for "change this value". The control that
   opens it IS the thing the choice changes (a status chip, an assignee) — never a
   detached panel: the record's bottom ask-panel died 07-12 («модалка снизу,
   непонятно к чему она относится»). Esc and outside-click close it; arrows walk it;
   the object-naming ask (pattern 15) survives as the confirm step a state-changing
   menu hands over to. Items use menuitemradio when the menu sets a value. */
.mk-menu-wrap { position: relative; display: inline-flex; }
.mk-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 40; min-width: 240px;
           background: var(--mk-surface); border: 1px solid var(--mk-hairline-strong);
           border-radius: var(--mk-radius-md); box-shadow: var(--mk-shadow-pop);
           padding: 6px; }
.mk-menu[hidden] { display: none; }
/* A menu whose control sits near the right edge anchors right, not left — the
   reassign picker ran 115px off a 1440 screen (found by popover-overflow, 07-12). */
.mk-menu.mk-menu-right { left: auto; right: 0; }
/* ...and the same law on the other axis (07-18): a menu whose control sits low opens UPWARD
   rather than off the bottom edge. The time picker on an open service card hung 215px past a
   662px viewport, so the agent had to scroll to read her own options. Applied by mkPlaceMenu()
   on open — never a resting class, because the right side depends on where the control IS. */
.mk-menu.mk-menu-up { top: auto; bottom: calc(100% + 6px); }
/* A long menu is CAPPED to the room it has and scrolls inside itself rather than running off
   the screen (07-18: half-hour slots took the time picker to 29 rows — no viewport holds that).
   mkPlaceMenu sets the max-height. Scroll chaining remains natural: a compact menu never traps
   the page wheel, and a capped list hands scrolling back to the page at its boundary. */
.mk-menu { overflow-y: auto; overscroll-behavior-y: auto; }
.mk-menu-item { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px;
                padding: 0 10px; border: 0; background: none;
                /* 6px, not radius-sm: each item's hover/checked fill is its own rounded
                   plate (Ihor, 07-17: two adjacent checked items merged into one bar) */
                border-radius: var(--mk-radius-sm);
                cursor: pointer; font-family: var(--mk-font-app); font-size: var(--mk-text-sm);
                color: var(--mk-text-muted); text-align: left;
                /* an item may be an <a> when it opens a page (Guest view, 07-29) —
                   it still reads as a menu row, never as body-copy link */
                text-decoration: none; }
/* 2px air between the plates so neighbouring fills never touch (same ruling) */
.mk-menu-item + .mk-menu-item { margin-top: 2px; }
.mk-menu-item:hover, .mk-menu-item.is-hover { background: var(--mk-surface-sunken); }
/* A SECTION inside a menu — a caps label over the run of items it governs. Born 07-28 on
   the builder's time picker, when scheduling an unscheduled service stopped being «which
   hour» and became «which day, which hour»: one flat list of 84 slots cannot say which day
   a row belongs to. The label is not an option — it takes no hover, no hit area, and the
   keyboard walks straight past it. */
.mk-menu-group { margin: 10px 0 4px; padding: 0 10px;
                 font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs);
                 letter-spacing: .1em; text-transform: uppercase;
                 color: var(--mk-text-muted); pointer-events: none; }
.mk-menu-group:first-child { margin-top: 2px; }
/* Keyboard focus and committed choice are different facts. Focus keeps the standard
   inset camel outline; only aria-checked earns the persistent fill and drawn check. */
.mk-menu-item:focus-visible { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
.mk-menu-item.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: -2px; }
/* A destructive choice reads as one BEFORE it is pressed (Polaris destructive item) —
   and colour is never the only signal: the trash mark rides the label (N2, 07-14,
   born when the segment header's bare trash became an overflow menu). */
.mk-menu-item.is-destructive { color: var(--mk-error-text); }
.mk-menu-item.is-destructive:hover,
.mk-menu-item.is-destructive:focus-visible { background: var(--mk-error-wash); }
.mk-menu-item > svg,
.mk-menu-item .ico { flex: none; width: 16px; height: 16px; fill: none; stroke: currentColor;
                     stroke-width: var(--mk-icon-stroke); stroke-linecap: butt; stroke-linejoin: miter; }
.mk-menu-item[aria-checked="true"] { font-weight: 600;
                                     background: var(--mk-accent-wash); }
.mk-menu-item[aria-checked="true"]:focus-visible,
.mk-menu-item[aria-checked="true"]:hover { background: color-mix(in srgb, var(--mk-accent) 16%, transparent); }
/* Ihor, 07-16 live (round 9): the «✓» was a system-font glyph, not our icon language.
   The current mark is DRAWN — flat stroke, butt ends, accent ink, the registry's grammar. */
.mk-menu-item[aria-checked="true"]::after { content: ""; flex: none; width: 11px; height: 6px;
                                            margin-left: auto;
                                            /* --mk-icon-stroke is a unitless stroke-width; a border needs a length */
                                            border-left: calc(var(--mk-icon-stroke) * 1px) solid var(--mk-accent);
                                            border-bottom: calc(var(--mk-icon-stroke) * 1px) solid var(--mk-accent);
                                            transform: rotate(-45deg) translateY(-2px); }
/* M6 (07-12): with a stacked name+note the ✓ floated mid-block («в воздухе»).
   It pins to the NAME line — where the dot already sits. */
.mk-menu-item:has(.mki-col)[aria-checked="true"]::after { align-self: center; margin-top: 0; }
.mk-menu-item .mki-note { color: var(--mk-text-muted); font-weight: 350; font-size: var(--mk-text-sm); }
.mk-menu-item .mki-title { color: var(--mk-text); font-weight: 600; }
/* A choice with a consequence note stacks: the name never wraps mid-word beside
   its note; the note takes its own line under the name. */
.mk-menu-item .mki-col { display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
                         padding: 8px 0; min-width: 0; }
.mk-menu-item:has(.mki-col) { align-items: center; }
.mk-menu-item:has(.mki-col) > .mk-dot,
.mk-menu-item:has(.mki-col) > .ava { margin-top: 14px; } /* centred on the NAME line, not the block */
/* The confirm step a state-changing menu swaps to: the sentence, then the button
   that names the object and the destination (pattern 15). */
.mk-menu .mk-menu-confirm { padding: 10px; display: flex; flex-direction: column; gap: 10px;
                            font-size: var(--mk-text-sm); }
.mk-menu .mk-menu-confirm .acts { display: flex; gap: 10px; flex-wrap: wrap; }

/* ── Disclosure — a row that OPENS, and looks like it (07-11: "я даже не подозревал,
   что это раскрывашка"). The filled triangle is the affordance; it rotates on expand. A plain
   button styled like a button reads as an action, not as a place to look inside. */
.mk-disclosure { display:flex; align-items:center; gap:8px; width:100%; min-height:var(--mk-tap-min);
                 padding:10px 0; background:transparent; border:0; border-top:1px solid var(--mk-hairline);
                 font:inherit; font-weight:500; font-size:var(--mk-text-sm); color:var(--mk-text-muted);
                 cursor:pointer; text-align:left; }
.mk-disclosure:hover,
.mk-disclosure.is-hover { color:var(--mk-accent); }
.mk-disclosure:focus-visible,
.mk-disclosure.is-focus { outline:2px solid var(--mk-camel-mid); outline-offset:2px; }
/* The filled mark stays readable in both framed and plain disclosures. */
.mk-disclosure .caret { flex:none; width:16px; height:16px; fill:currentColor; stroke:none;
                        transition:transform .15s ease; }
.mk-disclosure[aria-expanded="true"] .caret { transform:rotate(180deg); }
.mk-disclosure .mk-disclosure-hint { margin-left:auto; color:var(--mk-text-muted); font-weight:350; }

/* A disclosure that already lives inside a framed card header. The parent owns its padding,
   border and hover wash; the component still owns semantics, focus and caret behaviour. */
.mk-disclosure.mk-disclosure-plain { min-height: 0; padding: 0; border: 0; color: inherit; }
.mk-disclosure.mk-disclosure-plain:hover { color: inherit; }

/* A human-confirmed / needs-check control: a compact state face that is explicitly
   pressable. Read-only lifecycle badges remain .mk-badge. */
.mk-state-toggle { display: inline-flex; align-items: center; gap: 6px; position: relative;
                   min-height: 26px; padding: 2px 9px; border: 1px solid transparent;
                   border-radius: 999px; background: transparent; color: var(--mk-text-muted);
                   font: inherit; font-size: var(--mk-text-sm); cursor: pointer; }
.mk-state-toggle::after { content: ""; position: absolute; inset: -9px 0; }
.mk-state-toggle.is-warning { color: var(--mk-warning-text); background: var(--mk-warning-tint);
                              border-color: color-mix(in srgb, var(--mk-warning) 40%, transparent); }
.mk-state-toggle.is-success { color: var(--mk-success-text); background: var(--mk-success-tint);
                              border-color: color-mix(in srgb, var(--mk-success) 40%, transparent); }
.mk-state-toggle:hover,
.mk-state-toggle.is-hover { filter: brightness(.96); }
.mk-state-toggle:focus-visible,
.mk-state-toggle.is-focus { outline: 2px solid var(--mk-camel-mid); outline-offset: 2px; }

/* ── Person list — a related-record list whose facts belong to a person, not to a
   parallel declarations column. Born on the builder's G4 round after the split roster
   proved that writing a name inside a fact is a caption, not a relationship.
   The ROW owns the relation: identity leads, owned facts follow. On the phone the two
   stack in the same cell; shared context cards may render a quiet placeholder when no fact exists. */
.mk-person-list { border-top:1px solid var(--mk-hairline); }
.mk-person-row { display:grid; grid-template-columns:minmax(150px,.72fr) minmax(0,1.28fr);
                 gap:var(--mk-space-3); padding:10px 0;
                 border-bottom:1px solid var(--mk-hairline); }
.mk-person-head { min-width:0; }
.mk-person-name { display:block; font-size:var(--mk-text-md); line-height:22px; font-weight:600; }
.mk-person-meta { display:block; margin-top:2px; color:var(--mk-text-muted);
                  font-size:var(--mk-text-sm); line-height:18px; }
.mk-person-meta.is-lead { font-family:var(--mk-font-app-caps); letter-spacing:.1em;
                          text-transform:uppercase; color:var(--mk-text-label); font-weight:500; }
.mk-person-body { min-width:0; }
.mk-person-body:empty { display:none; }
.mk-person-empty-note { display:block; color:var(--mk-text-quiet);
  font-size:var(--mk-text-sm); line-height:20px; }
/* Dossier modifier — whole guest records remain vertically ordered while each record keeps
   its own facts physically attached. A parent context may bound and scroll the complete section
   for large travel parties, so its heading follows the records instead of becoming sticky. */
.mk-person-roster { border:1px solid var(--mk-hairline-strong); border-radius:var(--mk-radius-sm);
                    background:var(--mk-surface); overflow:hidden; }
.mk-person-list-dossiers { display:grid; gap:0; border-top:0; }
.mk-person-list-dossiers .mk-person-row {
  display:block; padding:8px 10px; border:0; border-bottom:1px solid var(--mk-hairline);
  border-radius:0; background:var(--mk-surface);
}
.mk-person-list-dossiers .mk-person-row:last-child { border-bottom:0; }
.mk-person-list-dossiers .mk-person-head { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
.mk-person-list-dossiers .mk-person-meta { display:inline; margin-top:0; }
.mk-person-list-dossiers .mk-person-body:not(:empty) { margin-top:2px; }
/* Trailing-role modifier — relationship/status metadata takes the quiet right slot while the
   person's name owns the line. Useful in narrow related-record widgets where an inline label
   after the name reads like part of the name and caps consume scarce width. */
.mk-person-list-roles-right .mk-person-head { width:100%; flex-wrap:nowrap; }
.mk-person-list-roles-right .mk-person-name { flex:1; min-width:0; overflow:hidden;
                                                white-space:nowrap; text-overflow:ellipsis; }
.mk-person-list-roles-right .mk-person-meta,
.mk-person-list-roles-right .mk-person-meta.is-lead {
  flex:none; display:block; max-width:44%; margin:0 0 0 auto; overflow:hidden;
  color:var(--mk-text-muted); font-family:var(--mk-font-app); font-size:var(--mk-text-xs);
  font-weight:350; letter-spacing:0; line-height:18px; text-transform:none;
  white-space:nowrap; text-overflow:ellipsis;
}
.mk-person-list-roles-right .mk-person-meta.is-lead {
  color:var(--mk-text-muted); font-weight:600;
}
.mk-person-list-scroll { max-height:154px; overflow-y:auto; overflow-x:hidden;
                         overscroll-behavior:contain; }
.mk-person-list-scroll:focus-visible { outline:2px solid var(--mk-camel-mid); outline-offset:2px; }
.mk-person-scroll-hint { margin:0; padding:6px 10px; border-top:1px solid var(--mk-hairline);
                         background:var(--mk-surface); color:var(--mk-text-muted);
                         font-size:var(--mk-text-xs); line-height:18px; }

/* Enquiry context — one received request and the people who belong to it. The card is a
   single disclosure because both halves leave the compose canvas together. It is shared by
   the enquiry record and Builder; page code supplies only the record. The card takes the
   height its content needs — every guest and every note visible, no inner scroll (07-27;
   the fixed 280px viewport with scrolling halves died here). */
.mk-enquiry-context { min-width:0; --mk-fact-key:auto; }
.mk-enquiry-context-card { width:100%; min-width:0; padding:0; overflow:hidden; }
.mk-enquiry-context-card.is-collapsed { height:var(--mk-widget-head-h); }
/* 07-20: the head became a ROW — the disclosure keeps the whole title band as its target,
   and a page may hang one action off the right edge (the record hangs `Open in CRM` here,
   where the object it opens is named). The hairline moved from the button to the row, so
   the action sits on the same band instead of below it. */
/* the band's height is measured INCLUDING its hairline, so it lands on the same 64 as
   every `.mk-widget-head-framed` beside it */
.mk-enquiry-context-head { display:flex; align-items:stretch; flex:none;
  box-sizing:border-box; min-height:var(--mk-widget-head-h);
  border-bottom:1px solid var(--mk-hairline); }
.mk-enquiry-context-heading { margin:0; flex:1; min-width:0; display:flex; }
.mk-enquiry-context-toggle { position:relative; flex:1; min-width:0; min-height:0;
  display:flex; align-items:center; gap:10px; padding:0 14px;
  transition:background-color .15s ease; }
/* the record page's widget rests OPEN and STATIC (Ihor, 07-27): a head band with no
   caret and no fold — the builder keeps the collapsible face */
.mk-enquiry-context-toggle.is-static { cursor:default; background:none; border:0; }
.mk-enquiry-context-act { flex:none; display:flex; align-items:center; padding:0 14px 0 4px; }
/* The hover tint paints the WHOLE band, action slot included — the toggle alone left a
   plain rectangle around the CRM action, which read as a hole in the band (Ihor, 07-27). */
.mk-enquiry-context-head:has(.mk-enquiry-context-fold:hover) {
  background:var(--mk-wash-faint); }
/* 16px, not 14 — the same left inset the segment heads keep, so «Enquiry» starts on the
   very x as «Payment» and «Itinerary» stacked beneath it (07-31) */
.mk-enquiry-context-toggle { padding-left:16px; gap:14px; }
.mk-enquiry-context-fold { flex:none; color:var(--mk-text-muted); }
.mk-enquiry-context-fold .chev { transition:transform .15s ease; }
.mk-enquiry-context-fold[aria-expanded="true"] .chev { transform:rotate(180deg); }
/* Guests own the wider column: their rows carry name + preference + adult/child,
   while the brief is a date line and short notes (Ihor, 07-27 — the even split died). */
.mk-enquiry-context-body.mk-brief-layout { flex:1; min-height:0; gap:0;
  grid-template-columns:minmax(0,1fr) minmax(0,2.25fr); }
.mk-enquiry-context-body[hidden] { display:none; }
.mk-enquiry-brief, .mk-enquiry-guests { min-width:0; min-height:0; overflow:hidden; }
.mk-enquiry-brief { padding:12px 20px 16px; }
.mk-enquiry-record-link { display:inline-flex; align-items:center; gap:6px; margin:10px 0 0;
  color:var(--mk-text-secondary); font-size:var(--mk-text-sm); font-weight:500;
  line-height:18px; text-decoration:none; }
.mk-enquiry-record-link svg { width:14px; height:14px; flex:none; fill:none;
  stroke:currentColor; stroke-width:var(--mk-icon-stroke);
  stroke-linecap:butt; stroke-linejoin:miter; }
.mk-enquiry-record-link:hover { color:var(--mk-text-label); }
.mk-enquiry-record-link:hover span { text-decoration:underline; text-underline-offset:3px; }
.mk-enquiry-record-link:focus-visible { outline:2px solid var(--mk-camel-mid);
  outline-offset:2px; }
.mk-enquiry-section-title { margin:0 0 6px; font-family:var(--mk-font-app-caps);
  font-size:var(--mk-text-sm); letter-spacing:.1em; text-transform:uppercase;
  color:var(--mk-text-label); font-weight:500; }
.mk-enquiry-brief > .mk-enquiry-section-title,
.mk-enquiry-notes-section > .mk-enquiry-section-title { margin-bottom:14px; }
.mk-enquiry-request { min-width:0; }
.mk-enquiry-request-value { margin:0; font-size:var(--mk-text-md); line-height:24px;
  font-weight:600; }
/* the requested DATES wear the itinerary grammar at a size between the itinerary
   card (14) and the Itinerary widget (20) — Ihor, 07-27 review */
.mk-enquiry-request-value .mk-enquiry-dates { font-size:var(--mk-text-md); font-weight:600;
  color:var(--mk-text-secondary); }
.mk-enquiry-party-summary { margin:4px 0 0; color:var(--mk-text-secondary);
  font-size:var(--mk-text-md); font-weight:350; line-height:22px; }
/* the enquiry's special flags (anniversary, honeymoon, alias…) ride the Requested
   stay block as tags; a flag with a story carries it as a gloss */
.mk-enquiry-special { margin:10px 0 0; display:flex; gap:6px; flex-wrap:wrap; }
button.mk-tag { cursor:default; }
button.mk-tag.mk-gloss { cursor:help; }
.mk-enquiry-notes-section { margin-top:14px; }
/* Notes are a plain bulleted list — each note is a remark, not a record, so the card
   dressing came off (Ihor, 07-27). */
.mk-enquiry-notes { margin:0; padding:0 0 0 18px; display:grid; gap:4px; list-style:disc; }
.mk-enquiry-note { margin:0; color:var(--mk-text-muted); font-size:var(--mk-text-sm);
  line-height:20px; }
.mk-enquiry-note::marker { color:var(--mk-text-muted); }
.mk-enquiry-crm-note { margin:8px 0 0; color:var(--mk-text-muted); font-size:var(--mk-text-sm);
  line-height:20px; }
.mk-enquiry-guests { position:relative; padding:0; container:enquiry-guests / inline-size; }
.mk-enquiry-guests-divider { position:absolute; z-index:1; left:0; top:12px; bottom:12px;
  width:1px; background:var(--mk-hairline); pointer-events:none; }
.mk-enquiry-guests-frame { position:relative; min-height:0; gap:4px; padding:12px 0 12px 20px; }
.mk-enquiry-guests-heading { min-height:24px; margin:0; padding:0 20px 0 0; align-items:baseline; }
.mk-enquiry-guests-heading .mk-enquiry-section-title { margin:0; }
.mk-enquiry-guests .mk-person-roster { flex:none; min-height:auto; gap:0; border:0;
  border-radius:0; overflow:visible; padding:0; }
/* One guest, one row: identity leads, the guest's facts follow, and the party maths —
   Adult / Child — closes the row on the right edge. Subgrid shares the tracks across
   rows, so every preference starts on the same vertical line whatever the name's length
   (Ihor, 07-27). The role rides the name's line as a quiet word — «Primary guest»,
   «Daughter, 3» — one guest, one line (roles under the name lived one round). */
/* The identity track is a FIXED 220px, not max-content: Chrome sized the shared subgrid
   track without its rows' contents, so the track came out narrower than the names and the
   names swallowed the whole gutter (caught by measurement, 07-27). A fixed track cannot
   lie — the gap after it is real at every row. */
/* 28px, not 20 (Ihor, 07-29: «колонка с заметками слишком близко прижата к колонке с
   именами»). The gutter is the only thing between two runs of text — at 20 the note
   began where the name ended. */
/* 240px, not 220 (07-29): names were already clipping at «Salim Al Mask…» before the star
   arrived. A fixed track cannot lie — the gap after it is real at every row — so the track
   grows rather than the name shrinking. */
.mk-enquiry-guests .mk-person-list-dossiers { min-height:0;
  grid-template-columns:240px minmax(0,1fr) max-content; column-gap:28px; }
/* the row's own gap must repeat the list's: a subgrid's own gap OVERRIDES the
   inherited gutter, and the base .mk-person-row carries gap:12px — which silently ate
   two rounds of widening (measured, 07-27) */
.mk-enquiry-guests .mk-person-list-dossiers .mk-person-row {
  display:grid; grid-column:1 / -1; grid-template-columns:subgrid; gap:0 28px;
  align-items:baseline; margin:0 20px 0 0; padding:8px 0; background:transparent;
}
.mk-enquiry-guests .mk-person-list-dossiers .mk-person-head { flex-wrap:nowrap; }
.mk-enquiry-guests .mk-person-name { min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; font-size:var(--mk-text-sm); line-height:18px; font-weight:600; }
/* The trip answers to ONE person, and the star says so before any word does — filled,
   in the brand's gold, on the name's own line (Ihor, 07-29). It leads the relation it
   marks, so the eye lands on the star and the word explains it. */
.mk-person-lead-star { flex:none; width:14px; height:14px; align-self:center;
  color:var(--mk-accent); }
/* the library's hand is a hairline outline; a marker is the one thing that is solid */
.mk-icon-solid { fill:currentColor; stroke:none; }
/* the relation rides the name's line. It has no lead variant here any more: the role that
   used to be set in semibold is the star's job now, and the primary guest's row carries no
   relation at all unless the record gave it one beyond the words the star replaced. */
.mk-enquiry-guests .mk-person-meta {
  flex:none; color:var(--mk-text-muted); white-space:nowrap;
  font-family:var(--mk-font-app); font-size:var(--mk-text-sm);
  font-weight:350; letter-spacing:0; text-transform:none; line-height:16px; }
.mk-person-kind { color:var(--mk-text-muted); font-size:var(--mk-text-sm);
  line-height:18px; text-align:right; }
.mk-enquiry-guests .mk-person-body { display:flex; flex-wrap:wrap;
  align-items:baseline; gap:0 16px; }
.mk-enquiry-guests .mk-person-list-dossiers .mk-person-body:not(:empty) { margin-top:0; }
.mk-enquiry-guests .mk-person-body .mk-fact { padding:0; }
/* the plate died (Ihor, 07-27 review): red ink on the words and the icon carries the
   warning — no wash, no left rule */
.mk-enquiry-guests .mk-person-body .mk-fact.alert { margin:0; padding:2px 0;
  border:0; border-radius:0; background:none; }
.mk-enquiry-guests .mk-person-body .mk-fact.alert .mk-fact-copy {
  min-width:0; flex:1; text-align:left;
}
.mk-enquiry-guests .mk-person-body .mk-fact.alert .mk-fact-copy .k,
.mk-enquiry-guests .mk-person-body .mk-fact.alert .mk-fact-copy .v {
  display:inline; width:auto;
}
.mk-enquiry-guests .mk-person-body .mk-fact.alert .mk-fact-copy .k {
  margin-right:6px;
}
.mk-enquiry-guests .mk-person-body .mk-fact:not(.alert) { color:var(--mk-text-muted); }
/* On a small laptop the section runs narrow and the notes starved while the identity
   track held spare air (his 07-31 catch at 13″). Below the section's own ~560px the
   relation leaves the name's line and sits under it, and the identity track hands the
   freed width to the notes. 170px still seats every fixture name whole — «Thomas van
   der Berg» with the star is ~160px. The one-guest-one-line rule (07-27) holds above;
   this is its narrow-width bend, asked for, not drift. */
@container enquiry-guests (width < 560px) {
  .mk-enquiry-guests .mk-person-list-dossiers { grid-template-columns:170px minmax(0,1fr) max-content; }
  .mk-enquiry-guests .mk-person-list-dossiers .mk-person-head { flex-wrap:wrap; }
  .mk-enquiry-guests .mk-person-meta { flex-basis:100%; margin-top:2px; }
}

/* Price summary — a consolidated internal ledger, not a second itinerary. Category totals
   stay visible in conventional disclosures; their detail scrolls, while the payable total
   remains physically outside the scroll viewport at the bottom of the widget. */
.mk-price-summary { min-width:0; }
.mk-price-summary-body { display:flex; flex:1 1 auto; flex-direction:column;
  min-height:0; overflow:hidden; }
.mk-price-summary-groups { position:relative; flex:1 1 auto; min-height:0;
  overflow-x:hidden; overflow-y:auto; overscroll-behavior:contain; }
/* the divider between category disclosures stops at the rows' own text inset —
   an edge-to-edge line read as the widget's frame, not the rows' seam (Ihor, 07-27) */
.mk-price-summary-group + .mk-price-summary-group { position:relative; }
.mk-price-summary-group + .mk-price-summary-group::before { content:'';
  position:absolute; top:0; left:14px; right:14px; height:1px;
  background:var(--mk-hairline); }
.mk-price-summary-toggle.mk-disclosure { display:grid;
  grid-template-columns:12px minmax(0,1fr) auto; align-items:center; gap:8px;
  min-height:44px; padding:0 14px; border-top:0; }
/* 07-31, his ask: a step up, kept square — 12 read undersized against a 14px row */
.mk-price-summary-toggle .caret { width:16px; height:16px; transform:none; }
.mk-price-summary-toggle[aria-expanded="true"] .caret { transform:rotate(180deg); }
.mk-price-summary-toggle .mk-price-summary-label { min-width:0; font-weight:600;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* every amount carries the money weight, 650 — it was 600 here and 650 on a line price,
   which is the same figure doing the same job at two weights (Ihor spotted it, 29 Jul) */
.mk-price-summary-amount { flex:none; color:var(--mk-text-muted); font-size:var(--mk-text-sm);
  font-weight:650; font-variant-numeric:lining-nums tabular-nums; white-space:nowrap; }
.mk-price-summary-toggle:hover .mk-price-summary-amount { color:inherit; }
/* ── THE PRICE — one TYPE PAIR, never two styles that happen to sit together ──────
   Ihor, 07-31: every place that showed money had invented its own pairing, and the
   currency drifted as far as 7px below its figure. It is a pair: the currency takes
   its size FROM the figure beside it, 2px smaller and never more — with a floor at
   13px, because under the 14px figure the derived 12px stopped being readable (his
   07-31 catch). The formula is what enforces both: set the figure's size on .mk-price
   and the currency follows, so the two cannot be pulled apart by a later edit.
   Use: <span class="mk-price"><span class="mk-price-cur">OMR</span>14,200</span> */
.mk-price { font-family:var(--mk-font-app); font-weight:650;
  font-variant-numeric:lining-nums tabular-nums; white-space:nowrap; }
.mk-price-cur, .mk-price-summary-currency {
  font-size:max(13px, calc(1em - 2px)); font-weight:350; color:var(--mk-text-muted); margin-right:4px; }
.mk-price-summary-currency { margin-right:4px; }
.mk-price-summary-detail { padding:0 14px 8px; }
.mk-price-summary-item { display:grid; grid-template-columns:minmax(0,1fr) auto;
  align-items:start; gap:12px; padding:8px 0; border-top:1px solid var(--mk-hairline); }
.mk-price-summary-copy { min-width:0; }
.mk-price-summary-name { display:block; min-width:0; overflow:hidden;
  color:var(--mk-text-muted); font-size:var(--mk-text-sm); font-weight:500;
  line-height:18px; text-overflow:ellipsis; white-space:nowrap; }
.mk-price-summary-meta { display:block; margin-top:2px; overflow:hidden;
  color:var(--mk-text-muted); font-size:var(--mk-text-xs); line-height:17px;
  text-overflow:ellipsis; white-space:nowrap; }
/* 07-31, his eye: inside an OPEN group the price sat 3px under the name beside it. The
   row aligns to START, so the two inks are placed by their line boxes — and the name's
   was 18px against the amount's inherited 21. A 1px padding had been nudging at it;
   matching the leading is the actual fix, and it holds if either size changes. */
.mk-price-summary-item > .mk-price-summary-amount { line-height:18px; }
.mk-price-summary-empty { margin:0; padding:14px; color:var(--mk-text-quiet);
  font-size:var(--mk-text-sm); line-height:20px; }
/* 07-31, his eye: on a shared BASELINE the 20px total read as sitting above the 14px
   word beside it — correct typographically, wrong optically, because all of the larger
   figure's mass stands over the line while the small label's sits on it. Measured at 2px
   of optical offset. Centred instead, so the two runs balance. */
.mk-price-summary-total { display:flex; flex:none; align-items:center;
  justify-content:space-between; gap:12px; min-height:56px; padding:10px 14px;
  border-top:1px solid var(--mk-hairline-strong); background:var(--mk-surface); }
.mk-price-summary-total-label { color:var(--mk-text-muted); font-size:var(--mk-text-sm);
  font-weight:600; }
.mk-price-summary-total .mk-price-summary-amount { font-size:var(--mk-text-lg); }
/* the total's currency needs no size of its own: the pair derives it from the figure */
@media (max-width:1023px) {
  .mk-price-summary-body { flex:none; height:min(360px,55dvh); }
}

/* Brief layout — a compact record summary with two peer columns and an optional full-width
   continuation (usually a route). The phone collapses it without page-specific grid logic. */
.mk-brief-layout { display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
                   gap:var(--mk-space-4); }
.mk-brief-span { grid-column:1 / -1; }
@media (max-width:767px) {
  .mk-person-row { grid-template-columns:minmax(0,1fr); gap:var(--mk-space-2); }
  .mk-person-list-scroll { max-height:200px; }
  .mk-brief-layout { grid-template-columns:minmax(0,1fr); gap:var(--mk-space-4); }
  .mk-brief-span { grid-column:1; }
  .mk-enquiry-guests { border-top:1px solid var(--mk-hairline); }
  .mk-enquiry-guests-frame { padding-left:0; }
  .mk-enquiry-guests-divider { display:none; }
  .mk-enquiry-guests-heading { padding-left:18px; }
  .mk-enquiry-guests .mk-person-list-dossiers { grid-template-columns:minmax(0,1fr); }
  .mk-enquiry-guests .mk-person-list-dossiers .mk-person-row { display:block;
    margin-left:18px; }
}

/* ── Fact row — a scannable fact line (record, handover, Murshid, payment).
   AN ICON REPLACES A LABEL; IT NEVER DECORATES ONE. (C21, Ihor 07-11 — this comment
   used to say the opposite: "the caps label always names the fact". That doctrine was
   overturned in review; the pages had not drifted — the component was wrong.)
   Two legal shapes, chosen by the subtraction test:
     [.ico][.v]  — the icon carries the KIND of fact ("who", "when", "money"), so the
                   word naming the kind is dead weight: `[people] 2 adults · Hartmann`.
     [.k][.v]    — no icon can carry the kind (SLA, Source, Contract): plain label/value.
   [.ico][.k][.v] is decoration and does not ship — with ONE exception: `.alert`, where
   the icon is the severity channel (colour is never the only signal) and the label still
   names the kind (⚠ ALLERGY Shellfish — the icon says "danger", the word says of what).
   Icons obey the text rules: 3:1 contrast minimum, and a title/aria name on the row
   carries what the icon means for anyone it doesn't reach. */
.mk-fact { display:flex; gap:10px; align-items:baseline; padding:4px 0;
           font-size:var(--mk-text-sm); line-height:20px; }
/* The icon aligns to the FIRST LINE of the value, not to the centre of the row. It used to be
   `align-self:center`, which is invisible on a one-line fact and drifts badly the moment the value
   wraps: on a three-line row the icon floats down beside the middle of the paragraph, unmoored from
   the label it belongs to (Ihor spotted it on the record's "Confirming" row, 07-11). */
/* PR-9 round 3 (Ihor live, 07-13 — the 4th «тоньше»): every icon SHAPE renders its stroke
   at a FLAT --mk-icon-stroke px, at every size — vector-effect pins the width so a 26px
   tab glyph is exactly as thin as a 13px fact icon, the way themalkai.com draws its own
   chrome. Micro carets (L4) keep flat px values of their own below. */
svg :is(path, circle, rect, line, polyline, ellipse) { vector-effect: non-scaling-stroke; }

/* PR-9 round 2 (concepts-icon-language.md): the brand's own hand — hairline
   --mk-icon-stroke, butt caps, miter joins, in the icon ink (darker than muted:
   thin + grey lost legibility together). */
/* 18px (Ihor, 07-13 live: thinner ≠ smaller — the hairline wants MORE canvas, the way the
   brand site draws stroke-1 glyphs on 20–23px boxes; the Guest card was his named example) */
.mk-fact .ico { flex:none; width:18px; height:18px; align-self:flex-start; margin-top:1px;
                color:var(--mk-icon-ink);
                fill:none; stroke:currentColor; stroke-width:var(--mk-icon-stroke);
                stroke-linecap:butt; stroke-linejoin:miter; }
/* The key column is a fixed width so the values line up down the card — but 80px only fits a
   short label, and a longer one ("CANCELLATION", "CONFIRMING") ran straight into its own value
   (found driving the experience record, 07-11). The width is now a variable: a page whose facts
   have long names widens the column once, and its values stay aligned. */
/* Ihor's 07-27 standing rule: the caps face belongs to SECTION TITLES only. An item's
   label (Source, Rate basis, Consent…) is ordinary text — quiet, sentence case. */
.mk-fact .k { flex:none; width:var(--mk-fact-key, 80px); font-family:var(--mk-font-app);
              font-size:var(--mk-text-sm); letter-spacing:0; text-transform:none;
              color:var(--mk-text-muted); }
.mk-fact .v { min-width:0; }
/* An allergy is not one fact among facts (C22, Ihor 07-11: right to be red — give it
   the weight it deserves). The alert row carries a tinted band, not just red ink:
   it must be findable when she is NOT looking for it. */
.mk-fact.alert { color:var(--mk-error-text); font-weight:600;
                 background:var(--mk-error-tint); border-radius:var(--mk-radius-sm);
                 padding:6px 10px; margin:4px 0; }
.mk-fact.alert .k, .mk-fact.alert .ico { color:var(--mk-error-text); }
/* The THIRD caste (round G, 07-19). A guest fact can fail in three different ways and the
   component used to know only two of them:
     · a CONSTRAINT makes a choice wrong          → .alert  (red, reserved for harm)
     · a SPEECH RESTRICTION makes a SENTENCE wrong → .restricted
     · CONTEXT makes a choice better               → the plain muted row
   The surprise flag is the case that earned it: the anniversary must never reach the guest in
   writing, which is not a danger and must not borrow the allergy's red — Polaris's own rule is
   that critical tone dies if it is spent on non-critical things.
   Cold reader, 07-19, and it overturned the first cut: a caution BAND next to the alert band
   read as one severity ramp — «the visual system says three severities while the content is one
   danger, one instruction, one preference», and in dark theme the yellow out-shouted the red, so
   the ramp inverted. Severity and category had been folded onto the same axis. Fixed by changing
   the DEVICE, not the hue: exactly one band exists in this component and it belongs to danger.
   A speech restriction is a marked LINE — a rule down its edge and its own ink — so it is still
   findable when she is not looking for it, without claiming to be a lesser alarm. */
.mk-fact.restricted { color:var(--mk-warning-text); border-left:2px solid var(--mk-warning-text);
                      padding:2px 0 2px 10px; margin:4px 0; }
.mk-fact.restricted .k, .mk-fact.restricted .ico { color:var(--mk-warning-text); }

/* On the phone a LABELLED fact stacks: label above, value below (Ihor, 07-12, on the
   allergy band — «title слева, текст справа — на мобилке такой layout не работает»,
   and it was a recurring pattern, so it dies at the component, not on a page). The
   fixed key column steals a third of a 390px row and squeezes the value into a
   ragged column; stacked, the value gets the full width. Icon-only facts (no .k)
   keep their row — an icon does not cost the line its width. */
@media (max-width: 767px) {
  /* PR-8: descriptions were a shade small in the hand — facts read at 15 on the phone */
  .mk-fact { font-size: 15px; line-height: 21px; }
  .mk-fact:has(> .k) { flex-wrap: wrap; row-gap: 2px; }
  .mk-fact:has(> .k) .k { width: 100%; }
  .mk-fact:has(> .k) .v { flex-basis: 100%; }
  /* the alert keeps its icon ON the label line: icon + ALLERGY above, the fact below */
  .mk-fact.alert:has(> .k) .k { width: auto; flex: 1; }
}

/* ── Threshold figure — progress toward a boundary, stated with its arithmetic
   (Surface E). The bar shows distance to the LINE, not to 100%; the text
   carries the same fact, so colour is never the only signal. */
/* The deposit bar is the canonical threshold (Ihor, 08-11: the DS page drew a
   fatter accent-filled bar the product never wears). The grammar is the builder's
   own payment foot, accepted by his eye on 07-30: a hairline track, the success
   fill, a 9px dot standing on the reached edge, the reached figure riding the dot
   as a tinted label in the status family's shape, and the target total in the
   money weight at the row's end. Green is the label's GROUND, never its ink —
   the fill and the dot already carry the success colour. */
.mk-threshold { display: flex; align-items: center; gap: 14px; min-width: 0; }
.mk-threshold .track { position: relative; flex: 1 1 auto; height: 3px; border-radius: 2px;
                       background: var(--mk-hairline); }
.mk-threshold .fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: 2px;
                      background: var(--mk-success-text); }
.mk-threshold .mark { position: absolute; top: 50%; width: 9px; height: 9px;
                      transform: translate(-50%, -50%); border-radius: 50%;
                      background: var(--mk-success-text); border: 2px solid var(--mk-surface);
                      box-sizing: content-box; }
/* rides the mark as a label, never a tooltip — it does not hide, so the hover
   dialect would promise an interaction that does not exist (07-30) */
.mk-threshold .lab { position: absolute; bottom: calc(100% + 3px); transform: translateX(-50%);
                     /* same light-theme dilution, same dark-theme fix as .mk-status-label */
                     background: color-mix(in srgb, var(--mk-success-tint) 55%, var(--mk-surface));
                     color: var(--mk-text-on-tint);
                     font-size: var(--mk-text-md); font-weight: 650;
                     font-variant-numeric: lining-nums tabular-nums; }
.mk-threshold .lab .cur,
.mk-threshold .total .cur { font-size: max(13px, calc(1em - 2px)); font-weight: 350; }
.mk-threshold .lab .cur { color: inherit; }
.mk-threshold .total .cur { color: var(--mk-text-muted); }
[data-theme="dark"] .mk-threshold .lab { background: var(--mk-success-tint); }
.mk-threshold .total { flex: none; font-family: var(--mk-font-app); font-weight: 650;
                       font-size: var(--mk-text-md); color: var(--mk-text);
                       font-variant-numeric: lining-nums tabular-nums; white-space: nowrap; }

/* ── The phone shell. Four primary destinations and Menu stay one tap away in a
   persistent bottom tab bar. Menu opens a full-width navigation layer above the
   bar; it is not a modal Bottom sheet, so the same Menu target stays available
   for closing. shell.js projects both forms from the desktop navigation source. */
.mk-tabbar { display: none; }   /* --mk-tabbar-h (tokens.css) carries the height contract */
.mk-mobile-nav { display: none; }
.mk-sheet, .mk-sheet-scrim { display: none; }   /* phone chrome — see the media block */
/* The brand mark rides the wordmark on desktop — small, fixed, never stretched
   (brand rule). On the phone the topbar leaves entirely; the mark lives in the
   mobile navigation layer. */
.mk-topbar-mark { display: block; height: 28px; width: auto; flex: none; }
.mk-topbar-wordmark { display: inline-flex; align-items: center; gap: 10px; }
[data-theme="dark"] .mk-topbar-mark { filter: invert(1); }

@media (max-width: 767px) {
  /* The desktop chrome yields. The tab bar answers primary navigation; identity,
     the remaining destinations and theme live in the mobile navigation layer. */
  .mk-topbar { display: none; }
  .mk-nav { display: none; }
  .mk-shell { display: block; min-height: 0; }
  /* content must end above the bar — shell.js stamps the class when it mounts */
  body.mk-has-tabbar { padding-bottom: var(--mk-tabbar-h); }

  /* The bar owns the phone's bottom edge in both closed and open Menu states. */
  .mk-tabbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 72;
               display: flex; background: var(--mk-surface);
               border-top: 1px solid var(--mk-hairline);
               padding-bottom: env(safe-area-inset-bottom, 0px); }
  .mk-tab { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column;
            align-items: center; gap: 3px; padding: 9px 0 7px; min-height: 68px;
            border: 0; background: none; cursor: pointer;
            font-family: var(--mk-font-app); font-size: 13px; font-weight: 500;
            letter-spacing: .01em; color: var(--mk-text-muted); text-decoration: none; }
  .mk-tab svg { width: 24px; height: 24px; flex: none; fill: none; stroke: currentColor;
                stroke-width: var(--mk-icon-stroke);
                stroke-linecap: butt; stroke-linejoin: miter; }
  .mk-tab[aria-current] { color: var(--mk-accent); font-weight: 500; }
  .mk-tab[aria-current] svg { fill: currentColor; }
  .mk-tab[aria-current] svg [fill="none"] { fill: none; }
  .mk-tab[aria-disabled="true"] { color: var(--mk-text-quiet); cursor: not-allowed; }
  /* The Menu tab's OPEN state uses the same accent voice as the current tab, while
     aria-expanded and the close glyph keep "open" distinct from "current". */
  .mk-tab .ico-close { display: none; }
  .mk-tab[aria-expanded="true"] { color: var(--mk-accent); font-weight: 500; }
  .mk-tab[aria-expanded="true"] .ico-menu { display: none; }
  .mk-tab[aria-expanded="true"] .ico-close { display: block; }

  /* Menu's expanded state is a navigation layer, not a dialog. It leaves a small
     reveal at the top, meets the bar, and arrives from the right. The bar keeps
     the larger z-index, so Menu closes in exactly the place where it opened. */
  .mk-mobile-nav { display: block; position: fixed; z-index: 71;
                   inset: 12px 0 var(--mk-tabbar-h) 0;
                   overflow-y: auto; overscroll-behavior: contain;
                   background: var(--mk-surface); border-top: 1px solid var(--mk-hairline);
                   box-shadow: 0 -4px 18px rgb(0 0 0 / .08);
                   animation: mk-mobile-nav-in 180ms ease-out both; }
  .mk-mobile-nav[hidden] { display: none; }
  .mk-mobile-nav-inner { display: flex; flex-direction: column; min-height: 100%; }
  .mk-mobile-nav-head { display: flex; align-items: center; justify-content: center; gap: 10px; flex: none;
                        min-height: 64px; padding: 10px 18px;
                        border-bottom: 1px solid var(--mk-hairline); }
  .mk-mobile-nav-wordmark { display: block; width: 132px; height: auto; }
  [data-theme="dark"] .mk-mobile-nav-wordmark { filter: invert(1); }
  .mk-mobile-nav-body { flex: 1 1 auto; padding: 20px 16px; }
  .mk-mobile-nav-tail { flex: none; padding: 12px 16px 16px;
                        border-top: 1px solid var(--mk-hairline); }
  .mk-mobile-nav-theme { display: flex; align-items: center; gap: 10px;
                         padding: 10px 0 0; color: var(--mk-text-muted);
                         font-size: var(--mk-text-sm); }
  .mk-mobile-nav-theme .mk-theme-toggle { margin-left: auto; }
  .mk-mobile-nav .mk-nav-link { min-height: 52px; padding: 10px 4px;
                                border-radius: 0; border-bottom: 1px solid var(--mk-hairline);
                                font-size: var(--mk-text-md); }
  .mk-mobile-nav .mk-nav-link:hover:not([aria-current]) { background: transparent; }
  .mk-mobile-nav .mk-nav-heading { display: none; }
  .mk-mobile-nav .mk-nav-list li:last-child .mk-nav-link { border-bottom: 0; }
  .mk-mobile-nav .mk-nav-tag { margin-left: auto; }
  .mk-mobile-nav .mk-nav-group + .mk-nav-group { margin-top: 24px; }
  @keyframes mk-mobile-nav-in {
    from { transform: translateX(100%); }
    to { transform: translateX(0); }
  }

  /* ── Bottom sheet — a separate modal component for compact detail, selection,
     and tasks. It owns a scrim, Escape, close control and focus containment. */
  .mk-sheet-scrim { display: block; position: fixed; inset: 0; z-index: 73;
                    background: var(--mk-scrim); border: 0; padding: 0; cursor: default; }
  .mk-sheet-scrim[hidden] { display: none; }
  /* A modal bottom sheet owns the complete lower edge. It covers the tab bar
     until dismissal and carries the device safe area itself. */
  .mk-sheet { display: flex; flex-direction: column; position: fixed;
              left: 0; right: 0; bottom: 0; z-index: 74;
              max-height: 94dvh;
              background: var(--mk-surface); border-top: 1px solid var(--mk-hairline);
              border-radius: var(--mk-radius-md) var(--mk-radius-md) 0 0;
              box-shadow: 0 -12px 40px rgb(0 0 0 / .25);
              padding-bottom: env(safe-area-inset-bottom, 0px); }
  .mk-sheet[hidden] { display: none; }
  /* the dialog container takes programmatic focus on open — the ring belongs
     to the controls inside it, not to the surface */
  .mk-sheet:focus { outline: none; }
  .mk-sheet-handle { display: block; flex: none; width: 40px; height: 4px;
                     margin: 10px auto 2px; border-radius: 999px;
                     background: var(--mk-hairline-strong); }
  .mk-sheet-head { display: flex; align-items: center; gap: 10px; flex: none;
                   min-height: 56px; padding: 6px 10px 6px 16px;
                   border-bottom: 1px solid var(--mk-hairline); }
  .mk-sheet-title { font-family: var(--mk-font-app); font-weight: 600;
                    font-size: var(--mk-text-md); margin: 0; }
  .mk-sheet-head .mk-icon-btn { margin-left: auto; } /* the × — what a tap opened, a tap closes (M2) */
  .mk-sheet-body { overflow-y: auto; padding: 8px 16px 16px; }
  /* Tall Bottom sheet remains available for focused modal work that genuinely
     needs the sheet contract; primary navigation does not use it. */
  .mk-sheet-tall { height: calc(100dvh - 48px);
                   max-height: none; }
  .mk-sheet .mk-nav-link { min-height: 52px; font-size: var(--mk-text-md);
                           border-radius: 0; border-bottom: 1px solid var(--mk-hairline);
                           padding: 10px 4px; }
  /* Touch navigation has no preview state. Suppress the sticky synthetic hover
     some mobile browsers leave behind after a tap; current remains explicit. */
  .mk-sheet .mk-nav-link:hover:not([aria-current]) { background: transparent; }
  .mk-sheet .mk-nav-list li:last-child .mk-nav-link { border-bottom: 0; }
  .mk-sheet .mk-nav-go { flex: none; width: 16px; height: 16px; margin-left: auto;
                         color: var(--mk-text-muted); fill: none; stroke: currentColor;
                         stroke-width: var(--mk-icon-stroke);
                         stroke-linecap: butt; stroke-linejoin: miter; }
  .mk-sheet .mk-nav-tag { margin-left: auto; }
  .mk-sheet .mk-nav-tag ~ .mk-nav-go { margin-left: 12px; }
  .mk-sheet .mk-nav-group + .mk-nav-group { margin-top: 24px; }
  /* Optional Bottom sheet identity and theme rows for modal account surfaces. */
  .mk-sheet-id { display: flex; align-items: baseline; gap: 8px; padding: 12px 12px 14px;
                 margin-bottom: 10px; border-bottom: 1px solid var(--mk-hairline);
                 font-size: var(--mk-text-sm); }
  .mk-sheet-id .role { font-family: var(--mk-font-app-caps); font-size: var(--mk-text-xs);
                       letter-spacing: .1em; text-transform: uppercase; color: var(--mk-text-label); }
  .mk-sheet-id .name { font-weight: 500; }
  .mk-sheet-theme { display: flex; align-items: center; gap: 10px; margin-top: 10px;
                    border-top: 1px solid var(--mk-hairline); padding: 12px 12px 4px;
                    font-size: var(--mk-text-sm); color: var(--mk-text-muted); }
  .mk-sheet-theme .mk-theme-toggle { margin-left: auto; }

  /* M6-6 (the top-anger finding of the it6 review): a control wearing a
     chevron-down promises an ATTACHED dropdown — the menu opens FROM the
     control, chevron flipping, at every width. The phone's menu-as-bottom-sheet
     morph died here («это паттерн… такого нет нигде» — the affordance and the
     answer must agree). M7 is solved where it belongs: the attached menu CLAMPS
     to the viewport instead of leaving the control. Bottom sheets remain the
     idiom for the Menu tab and the filter sheet (.mk-sheet) — surfaces with no
     attached trigger — never for a dropdown's answer. */
  .mk-menu { width: 100%; min-width: min(240px, calc(100vw - 24px));
             max-width: calc(100vw - 24px); max-height: 55dvh; overflow-y: auto; }

  /* M8: label-left / hint-right dies on the phone here too — the .mk-fact rule's
     sibling, missed on 07-12. The hint leaves the right edge and stacks under
     the label, indented past the caret so the two lines read as one control. */
  .mk-disclosure { flex-wrap: wrap; }
  .mk-disclosure .mk-disclosure-hint { margin-left: 0; flex-basis: 100%;
                                       padding-left: 18px; }
}

/* ── Text on a tint ────────────────────────────────────────────────────────
   A tinted panel darkens the ground it composites against, so the SAME
   secondary grey reads thinner on it than it does on a card — 5.72:1 becomes
   5.03-5.30 depending on the tint. Every value still passes AA, so nothing is
   broken; what is lost is weight, and weight is what the eye reads (Ihor,
   07-31, on the toast body over the amber).

   The rule is mechanical on purpose. A tinted surface RE-POINTS the neutral
   ink token instead of restating a colour, so every descendant — body copy, a
   currency, a note, anything added later — inherits the step without knowing
   it is on a tint. Prose in a doc gets lost; a variable at the boundary holds.

   Coloured ink (--mk-warning-text and its siblings) already carries its own
   on-tint ratio and needs none of this.

   New tinted panel? Give it .mk-tinted, or copy the one declaration. */
.mk-tinted,
/* the sunken band is not a status tint, but the ink cannot tell: it loses 11.4% where the
   card loses none, which is what a coloured tint costs. Same loss, same rule — rather than
   a fourth neutral ink token nobody would keep in sync (raised and page lose 2-4%, which is
   invisible, and they deliberately get nothing). */
.mk-surface-sunken-panel, .mk-table thead, .mk-band-sunken,
.mk-menu-item[aria-checked="true"],
.mk-opt[aria-selected="true"],
.mk-nav-link[aria-current],
.mk-row-tint-success, .mk-row-tint-warning, .mk-row-tint-error,
.mk-sla.warn, .mk-sla.breach,
.mk-state-toggle.is-warning, .mk-state-toggle.is-success { --mk-text-muted: var(--mk-text-on-tint); }
