/* ============================================================
   AI Cloud — Interactive Data Grid (mpdg)
   Generic, reusable list/board surface (sort, filter, group, columns,
   inline quick-edit, kanban). Driven entirely by data-mpdg-* hooks and
   per-instance config; no module-specific assumptions.
   ============================================================ */
/* Tasks overview — interactive SaaS table.
   Scoped under .mpdg so nothing leaks into the modal or other modules.
   Colours/tokens come from the ai-cloud foundation plugin. */

.mpdg{
  --mpdg-line:var(--mpdg-fill-07);
  --mpdg-line-strong:var(--mpdg-fill-10);
  --mpdg-surface:var(--mpdg-table-bg);
  --mpdg-row-hover:rgba(255,255,255,.028);
  --mpdg-grid: 44px minmax(260px,1.7fr) minmax(210px,.85fr) minmax(170px,.85fr) minmax(120px,.6fr) 46px;
  display:block;
  margin-bottom:22px;
}

/* ===== A grid whose active view owns its height =====
   data-grid.js puts .mp-page-fill on this element for a view that is as tall as the space it is
   given rather than as tall as its content. Foundation answers it on the pane — flex column, no
   scroll, an even frame — and turns THIS element into the flex item that takes what is left,
   including its margin. Everything below is only what the grid owes that contract from there down.

   THE LIST IS THE CARD AND THE ROWS ARE THE SCROLLER, and the useful part is what did NOT have to
   be built. .mpdg-list already carries overflow:hidden and a 14px radius, and .mpdg-head is already
   a SIBLING of .mpdg-rows inside it. So making the list a column and the rows the remainder puts
   the header outside the scrolling region by construction: it stays put with no position:sticky, no
   z-index and no opaque-background workaround, and the scroller is clipped by the card's own
   corners. .mpdg-foot is the last child of the grid root, so the pager pins itself for the same
   reason. Three things the reader keeps, none of them a mechanism.

   WHAT SCROLLS IS BOUNDED. Page sizes across every host are 10/25/50/100 with no "all", so the
   scroller holds at most a hundred rows. No virtualisation here, deliberately.

   min-height:0 twice, because a flex item's default min-height:auto refuses to shrink below its
   content — either one left out hands the overflow straight back to the pane.

   KEYED ON `.content >`, WHICH IS THE CONTRACT AND NOT SHELL GEOMETRY. This file still does not
   know the rail width, the bar height or the gutter — that was the coupling v1.12.451 took out of
   the timeline. What it names here is the same selector foundation defines the contract with, and
   it has to, because the class alone means the engine ASKED. Two hosts nest their grid one level
   deeper than :has(> .mp-page-fill) can reach, so on those the request is refused. Keyed on the
   class, they would have taken the flex column and overflow:auto anyway, with no bounded box to be
   a remainder of — and the height pin, which stands down whenever the rows host is a scroller,
   would have stood down on a host whose height still comes from its content. Measured before it
   was fixed: grid_render_quality_test caught the collapse at 280px. */
.content > .mpdg.mp-page-fill[data-mpdg-active-view="list"] .mpdg-list{
  flex:1 1 auto;
  min-height:0;
  display:flex;
  flex-direction:column;
}
/* EVERY CHILD OF THE CARD IS A BAND AND THE ROWS HOST IS THE REMAINDER, which was true of the two
   children mpdg itself ships and false of any a consumer adds. .mpdg-head was pinned by name and
   nothing else was, so a band between the head and the rows kept flex-shrink:1 and was squeezed by
   however much the rows overflowed by.

   IT DID NOT OVERFLOW, IT CLIPPED, which is the part worth writing down. A flex item's automatic
   minimum size is content-based only while its overflow is VISIBLE. The tasks open-sessions band
   carries overflow:hidden to clip its own rounded corners, so its automatic minimum was zero and it
   shrank silently to whatever was left, with no scrollbar and nothing to say content was missing.
   Measured on v1.12.479 at 1440x900 with 25 rows: a 202px two-row band rendered at 101px, half of
   itself gone; at ten rows, 362px gone. The one lens where it looked right was the one where the
   rows did not overflow, so there was no negative free space to distribute — which is why this
   read as a per-lens bug rather than as the frame contract being one child short.

   flex-shrink AND NOT THE SHORTHAND, and .mpdg-rows restated after it rather than given an id or a
   second class: both selectors carry five, so source order is what leaves the scroller as the one
   child that still yields. The shorthand would also have flattened flex-grow on the empty states,
   which is what lifts the empty message off the bottom of the card. */
.content > .mpdg.mp-page-fill[data-mpdg-active-view="list"] .mpdg-list > *{flex-shrink:0;}
.content > .mpdg.mp-page-fill[data-mpdg-active-view="list"] .mpdg-rows{
  flex:1 1 auto;
  min-height:0;
  overflow:auto;
}

/* ===== A BAND THAT CAN OUTGROW THE FRAME BOUNDS ITSELF =====
   The grid publishes the vocabulary instead of naming a consumer: [data-mpdg-band] is the same
   shape the card has one level up — fixed children, one remainder, and the remainder is what
   scrolls. So a band is never clipped, and never takes the whole frame either.

   PINNING ALONE WOULD HAVE TRADED ONE DEFECT FOR ANOTHER. The tasks band caps at five rows per
   group across two groups, which is 580px of content; pinned and uncapped that leaves 10px of grid
   at 1440x900 and nothing at all at 1440x700. A list with no rows in it is not an improvement on a
   clipped band.

   50%, MEASURED RATHER THAN CHOSEN. The common case must not pay for the worst one: a two-row band
   is 204px against a 223px allowance at the shortest height measured, 1440x700, so the cap engages
   only from five rows up. There the rows host keeps 168px at 700 and 268px at 900. 45% was tried
   first and put a scrollbar on the two-row band at 700, which is the case in front of every reader
   who has two open sessions.

   A PERCENTAGE AND NOT A PIXEL COUNT, because the constraint is relative to the frame: a fixed cap
   generous enough at 1080 leaves nothing at 700, and one safe at 700 truncates a band that had the
   room. It resolves against the card, whose height is definite under this contract.

   THE BAND'S OWN CHILDREN GET THE SAME TREATMENT AND FOR THE SAME REASON. .mp-tlo-head has
   overflow:visible today, so it happens to be unsqueezable; that is an accident of a property it
   set for another purpose, and the accident is exactly what produced the defect above one level
   higher. Stated, so a band whose head one day clips its own corners does not inherit the bug.

   :not([hidden]) IS LOAD-BEARING, AND v1.12.480 SHIPPED WITHOUT IT. `display` is the property
   that decides whether a thing is on the screen at all, so a shared contract setting it on a
   consumer's element is overruling that consumer about its own visibility. This selector carries
   five and the tasks band hides itself with .mp-tlo[hidden]{display:none}, which carries two — so
   display:flex won, and every reader with no open sessions got the band anyway, 63px of it with a
   badge reading 0 and 73px taken off the rows host. A panel that exists to say "you have work
   waiting" appearing when there is none is worse than the clipping it replaced: the first was a
   layout fault, this one made the platform state something untrue.

   THE CONTRACT HONOURS THE `hidden` ATTRIBUTE AND NOTHING ELSE, which is a real limit and is
   written here rather than left to be rediscovered. It is the attribute the band toggles
   (leftovers-band.js sets band.hidden) and the one mpdg's own empty and no-results blocks use, so
   it is the platform's hide signal rather than one consumer's habit. A band that hides itself by
   class instead must beat five selectors to do it — and should carry the `hidden` attribute
   instead. Only this rule needs the guard: it is the only one in the contract that sets display.

   WHAT LET IT SHIP: the suite's fixture emitted no band at all for a zero count, so the state
   "present in the DOM and hidden" — the state every reader with a clean slate is in — was never
   rendered. grid_frame_test now asserts it. */
.content > .mpdg.mp-page-fill[data-mpdg-active-view="list"] [data-mpdg-band]:not([hidden]){
  display:flex;
  flex-direction:column;
  max-height:50%;
}
.content > .mpdg.mp-page-fill[data-mpdg-active-view="list"] [data-mpdg-band] > *{flex-shrink:0;}
.content > .mpdg.mp-page-fill[data-mpdg-active-view="list"] [data-mpdg-band-body]{
  flex:1 1 auto;
  min-height:0;
  overflow:auto;
}

/* ===== The board, same shape, and the same three things that came free =====
   .mpdg-board-col-head is already a SIBLING of .mpdg-board-body, exactly as .mpdg-head is of
   .mpdg-rows, so bounding the column and making the body the remainder puts every column heading
   outside its own scrolling region. No sticky here either.

   CAPPED, NOT STRETCHED. max-height rather than height, and the board keeps align-items:flex-start,
   so a column still hugs its cards and only a column TALLER than the pane is bounded. That is a
   choice and it has a cost: the drop target for an empty column stays the ~115px of its heading
   instead of the full frame. The one line that reverses it is align-items:stretch on .mpdg-board.
   grid_frame_test asserts the ragged bottom, so reversing it is an edit rather than a side effect.

   AND NO DRAG AUTO-SCROLL IS NEEDED, which is worth writing down because the plan for this work
   assumed one. The drop target is the COLUMN, not a slot inside it: the pointerup handler resolves
   elementFromPoint(...).closest('.mpdg-board-col') and boardMoveCard() sets the field. There is
   nothing within a column to reach, so bounding the columns makes the drop EASIER — the whole
   column is on screen — rather than putting anything out of reach. A column off to the RIGHT is
   reached by the board's own sideways scroll, which is unchanged here and does not auto-scroll
   under a drag today either. */
.content > .mpdg.mp-page-fill[data-mpdg-active-view="board"] .mpdg-board{
  flex:1 1 auto;
  min-height:0;
  /* The pane's bottom padding is the frame edge now; a second gutter here would double it. */
  padding-bottom:0;

  /* EVERY COLUMN THE HEIGHT OF THE FRAME. The base rule is align-items:flex-start, which is right
     for a board on a scrolling page and wrong inside one that cannot scroll: a column with two
     cards was 287px and a column with none was 115px, so the buckets you most often drag INTO
     were the smallest things to aim at. The drop target is the whole column, so its height is its
     hit area.

     This also makes max-height on the column redundant — stretch takes the cross size from the
     container rather than from the content, so a column taller than the frame is bounded by the
     same rule that lifts a short one. Measured both ways: identical geometry, 718px column and a
     75px inner scroll either way. One declaration, not two saying the same thing from opposite
     ends. */
  align-items:stretch;
}
.content > .mpdg.mp-page-fill[data-mpdg-active-view="board"] .mpdg-board-col{
  min-height:0;
}
.content > .mpdg.mp-page-fill[data-mpdg-active-view="board"] .mpdg-board-col-head{flex:0 0 auto;}
.content > .mpdg.mp-page-fill[data-mpdg-active-view="board"] .mpdg-board-body{
  flex:1 1 auto;
  min-height:0;
  overflow-y:auto;
}

/* ===== A bucket with nothing in it is a rail =====
   SIX COLUMNS DO NOT FIT ON A LAPTOP, and that is the larger of the two board problems. At 300px
   fixed — flex:0 0 300px, min-width:300px, no shrink at any breakpoint — the tasks board is 1870px
   wide against a 1088px pane at a 1440 window. Measured: 3 of 6 columns fully visible, 782px off
   to the right. And there is no auto-scroll during a drag, so two columns 1870px apart can never
   be on screen together: dragging a card from To do to Done was not slow, it was IMPOSSIBLE.

   Collapsed, a zero-count column keeps its dot, its name and its count and loses only its body.
   Measured at the same 1440: 5 of 6 fully visible, 2px off. The whole workflow fits, every bucket
   is still a drop target, and there is nothing left to auto-scroll TO — which is why no auto-scroll
   was built.

   NO CLASS AND NO JS. renderBoard() already inserts .mpdg-board-empty exactly when a column has no
   cards, so :has() reads the state the renderer already publishes rather than a second copy of it.
   Drop a card on a rail and the marker goes with the re-render, so the column opens by itself; the
   drop works because the handler resolves .closest('.mpdg-board-col') and a rail is still one. */
.content > .mpdg.mp-page-fill[data-mpdg-active-view="board"] .mpdg-board-col:has(.mpdg-board-empty){
  flex:0 0 40px;
  min-width:40px;
  background:var(--mpdg-deep-30);
}
.content > .mpdg.mp-page-fill[data-mpdg-active-view="board"] .mpdg-board-col:has(.mpdg-board-empty) .mpdg-board-body{
  display:none;
}
.content > .mpdg.mp-page-fill[data-mpdg-active-view="board"] .mpdg-board-col:has(.mpdg-board-empty) .mpdg-board-col-head{
  flex-direction:column;
  align-items:center;
  justify-content:flex-start;
  gap:12px;
  height:100%;
  padding:14px 0;
  border-bottom:0;
}
.content > .mpdg.mp-page-fill[data-mpdg-active-view="board"] .mpdg-board-col:has(.mpdg-board-empty) .mpdg-board-col-title{
  writing-mode:vertical-rl;
  transform:rotate(180deg);
  white-space:nowrap;
  color:var(--mpdg-ink-muted);
}
/* NO COUNT ON A RAIL, and the reason is that it cannot say anything. renderBoard() prints
   inThis.length into the badge and inserts .mpdg-board-empty when !inThis.length — the same
   number, from the same line. So the badge on a collapsed column reads 0 by construction, always,
   and measured: ["0","0","0","0","0","0"] across a board where every bucket was empty.

   IT ALSO ANSWERED THE WRONG QUESTION. The badges staggered by 38px down the row, because the head
   packs from the top and a vertical title is as long as its words — "In progress" pushes its badge
   further than "To do" does. Aligning them would have been keeping six identical zeroes tidy.
   A collapsed column IS the statement that it is empty; the number is that statement again, in a
   form that has to be lined up. */
.content > .mpdg.mp-page-fill[data-mpdg-active-view="board"] .mpdg-board-col:has(.mpdg-board-empty) .mpdg-board-col-count{
  display:none;
}

/* ===== Scheme tokens =====================================================
   One token per colour this plugin paints with. Dark values are the exact
   literals this file always used, so the dark scheme is computed-identical
   to before tokenization. Light pairs are seeded from the material-light
   palette. html[data-mp-scheme] (core >= 1.12.24) is the only switch. */
:root{
    --mpdg-avatar-ring:var(--mp-avatar-ring, rgba(2,6,23,.55)); /* SSOT: foundation token (folded v1.12.79); scheme switching lives on the token */
    --mpdg-stack-ring:rgba(2,6,23,.78);
    --mpdg-stack-ring-soft:rgba(2,6,23,.72);
    --mpdg-chip-line:rgba(148,163,184,.20);
    --mpdg-slate-16:rgba(148,163,184,.16);
    --mpdg-slate-22:rgba(148,163,184,.22);
    --mpdg-slate-24:rgba(148,163,184,.24);
    --mpdg-slate-06:rgba(148,163,184,.06);
    --mpdg-chip-ring:rgba(15,23,42,.55);
    --mpdg-lead-ring:rgba(0,234,94,.62);
    --mpdg-ink-strong:#f8fafc;
    --mpdg-ink-bright:#f1f5f9;
    --mpdg-ink:#e2e8f0;
    --mpdg-ink-soft:#cbd5e1;
    --mpdg-ink-muted:#94a3b8;
    --mpdg-ink-count:#9fb0c9;
    --mpdg-ink-dim:#64748b;
    --mpdg-ink-faint:#475569;
    --mpdg-ink-icon:#5b6b85;
    --mpdg-ink-nodate:#5b6678;
    --mpdg-ink-btn:#e5edf8;
    --mpdg-ink-check:#e5eefb;
    --mpdg-ink-tab:#e7eefc;
    --mpdg-ink-item:#dbeafe;
    --mpdg-ink-invert:#fff;
    --mpdg-ink-danger-soft:#fca5a5;
    --mpdg-ink-on-hl:#d1fae5;
    --mpdg-ink-danger:#ffd5de;
    --mpdg-ink-hl:#eafff2;
    --mpdg-hl-08:rgba(0,234,94,.08);
    --mpdg-hl-10:rgba(0,234,94,.10);
    --mpdg-hl-12:rgba(0,234,94,.12);
    --mpdg-hl-16:rgba(0,234,94,.16);
    --mpdg-hl-20:rgba(0,234,94,.20);
    --mpdg-hl-22:rgba(0,234,94,.22);
    --mpdg-hl-30:rgba(0,234,94,.30);
    --mpdg-hl-32:rgba(0,234,94,.32);
    --mpdg-hl-48:rgba(0,234,94,.48);
    --mpdg-hl-full:rgba(0,234,94,1);
    --mpdg-hl-deep:#00b84a;
    --mpdg-accent:#38bdf8;
    --mpdg-accent-soft:#7dd3fc;
    --mpdg-accent-10:rgba(56,189,248,.10);
    --mpdg-accent-14:rgba(14,165,233,.14);
    --mpdg-danger:#fb7185;
    --mpdg-danger-11:rgba(251,113,133,.11);
    --mpdg-danger-34:rgba(251,113,133,.34);
    --mpdg-danger-hov:rgba(244,63,94,.14);
    --mpdg-danger-16:rgba(244,63,94,.16);
    --mpdg-fill-02:rgba(255,255,255,.02);
    --mpdg-fill-022:rgba(255,255,255,.022);
    --mpdg-fill-025:rgba(255,255,255,.025);
    --mpdg-fill-032:rgba(255,255,255,.032);
    --mpdg-fill-04:rgba(255,255,255,.04);
    --mpdg-fill-045:rgba(255,255,255,.045);
    --mpdg-fill-05:rgba(255,255,255,.05);
    --mpdg-fill-06:rgba(255,255,255,.06);
    --mpdg-fill-07:rgba(255,255,255,.07);
    --mpdg-line-08:rgba(255,255,255,.08);
    --mpdg-line-09:rgba(255,255,255,.09);
    --mpdg-fill-10:rgba(255,255,255,.10);
    --mpdg-fill-12:rgba(255,255,255,.12);
    --mpdg-line-14:rgba(255,255,255,.14);
    --mpdg-slate-10:rgba(148,163,184,.10);
    --mpdg-slate-12:rgba(148,163,184,.12);
    --mpdg-slate-18:rgba(148,163,184,.18);
    --mpdg-slate-40:rgba(148,163,184,.4);
    --mpdg-slate-75:rgba(148,163,184,.75);
    --mpdg-deep-25:rgba(15,23,42,.25);
    --mpdg-deep2-25:rgba(2,6,23,.25);
    --mpdg-table-bg:rgba(15,23,42,.34);
    --mpdg-deep-50:rgba(15,23,42,.5);
    --mpdg-deep-55:rgba(15,23,42,.55);
    --mpdg-panel-98:rgba(15,23,42,.98);
    --mpdg-panel-solid:rgba(15,23,42,1);
    --mpdg-deep-30:rgba(2,6,23,.30);
    --mpdg-deep-32:rgba(2,6,23,.32);
    --mpdg-panel-96:rgba(2,6,23,.96);
    --mpdg-chart-42:rgba(8,12,24,.42);
    --mpdg-chart-50:rgba(8,12,24,.5);
    --mpdg-chart-60:rgba(8,12,24,.6);
    --mpdg-badge-ring:rgba(8,12,24,.92);
    --mpdg-chart-bg:rgba(13,18,31,.35);
    --mpdg-lens-ink:#0b1220;
    --mpdg-sh-18:rgba(0,0,0,.18);
    --mpdg-sh-22:rgba(0,0,0,.22);
    --mpdg-sh-40:rgba(0,0,0,.4);
    --mpdg-sh-42:rgba(0,0,0,.42);
    --mpdg-sh-62:rgba(0,0,0,.62);
    --mpdg-ghost-18:rgba(202,219,247,.18);
    --mpdg-ghost-14:rgba(202,219,247,.14);
    --mpdg-shadow-pop:rgba(2,6,23,.55);
}
html[data-mp-scheme="light"]{
    /* --mpdg-avatar-ring light override removed v1.12.79: the foundation token is scheme-aware */
    --mpdg-stack-ring:rgba(255,255,255,.92);
    --mpdg-stack-ring-soft:rgba(255,255,255,.92);
    --mpdg-chip-line:rgba(15,23,42,.14);
    --mpdg-slate-16:rgba(15,23,42,.08);
    --mpdg-slate-22:rgba(15,23,42,.11);
    --mpdg-slate-24:rgba(15,23,42,.12);
    --mpdg-slate-06:rgba(15,23,42,.04);
    --mpdg-chip-ring:rgba(255,255,255,.9);
    --mpdg-lead-ring:rgba(0,150,64,.75);
    --mpdg-ink-strong:#0f172a;
    --mpdg-ink-bright:#1e293b;
    --mpdg-ink:#1e293b;
    --mpdg-ink-soft:#334155;
    --mpdg-ink-muted:#64748b;
    --mpdg-ink-count:#64748b;
    --mpdg-ink-dim:#94a3b8;
    --mpdg-ink-faint:#cbd5e1;
    --mpdg-ink-icon:#64748b;
    --mpdg-ink-nodate:#94a3b8;
    --mpdg-ink-btn:#334155;
    --mpdg-ink-check:#1e293b;
    --mpdg-ink-tab:#334155;
    --mpdg-ink-item:#24324a;
    --mpdg-ink-invert:#0f172a;
    --mpdg-ink-danger-soft:#be123c;
    --mpdg-ink-on-hl:#065f46;
    --mpdg-ink-danger:#be123c;
    --mpdg-ink-hl:#047857;
    --mpdg-hl-08:rgba(0,184,79,.10);
    --mpdg-hl-10:rgba(0,184,79,.12);
    --mpdg-hl-12:rgba(0,184,79,.14);
    --mpdg-hl-16:rgba(0,184,79,.18);
    --mpdg-hl-20:rgba(0,184,79,.22);
    --mpdg-hl-22:rgba(0,184,79,.26);
    --mpdg-hl-30:rgba(0,184,79,.34);
    --mpdg-hl-32:rgba(0,184,79,.36);
    --mpdg-hl-48:rgba(0,150,64,.55);
    --mpdg-hl-full:#00b84f;
    --mpdg-hl-deep:#009a3f;
    --mpdg-accent:#0284c7;
    --mpdg-accent-soft:#0369a1;
    --mpdg-accent-10:rgba(2,132,199,.10);
    --mpdg-accent-14:rgba(2,132,199,.12);
    --mpdg-danger:#e11d48;
    --mpdg-danger-11:rgba(225,29,72,.08);
    --mpdg-danger-34:rgba(225,29,72,.30);
    --mpdg-danger-hov:rgba(225,29,72,.10);
    --mpdg-danger-16:rgba(225,29,72,.12);
    --mpdg-fill-02:rgba(15,23,42,.02);
    --mpdg-fill-022:rgba(15,23,42,.025);
    --mpdg-fill-025:rgba(15,23,42,.03);
    --mpdg-fill-032:rgba(15,23,42,.035);
    --mpdg-fill-04:rgba(15,23,42,.05);
    --mpdg-fill-045:rgba(15,23,42,.05);
    --mpdg-fill-05:rgba(15,23,42,.05);
    --mpdg-fill-06:rgba(15,23,42,.06);
    --mpdg-fill-07:rgba(15,23,42,.06);
    --mpdg-line-08:rgba(15,23,42,.10);
    --mpdg-line-09:rgba(15,23,42,.11);
    --mpdg-fill-10:rgba(15,23,42,.08);
    --mpdg-fill-12:rgba(15,23,42,.09);
    --mpdg-line-14:rgba(15,23,42,.16);
    --mpdg-slate-10:rgba(15,23,42,.05);
    --mpdg-slate-12:rgba(15,23,42,.06);
    --mpdg-slate-18:rgba(15,23,42,.14);
    --mpdg-slate-40:rgba(15,23,42,.35);
    --mpdg-slate-75:#94a3b8;
    --mpdg-deep-25:rgba(15,23,42,.04);
    --mpdg-deep2-25:rgba(15,23,42,.05);
    --mpdg-table-bg:rgba(255,255,255,.85);
    --mpdg-deep-50:#eef2f9;
    --mpdg-deep-55:#f1f5fa;
    --mpdg-panel-98:rgba(255,255,255,.98);
    --mpdg-panel-solid:#ffffff;
    --mpdg-deep-30:rgba(15,23,42,.045);
    --mpdg-deep-32:rgba(15,23,42,.05);
    --mpdg-panel-96:rgba(255,255,255,.97);
    --mpdg-chart-42:rgba(15,23,42,.04);
    --mpdg-chart-50:#f1f5f9;
    --mpdg-chart-60:rgba(15,23,42,.25);
    --mpdg-badge-ring:rgba(255,255,255,.95);
    --mpdg-chart-bg:rgba(15,23,42,.03);
    --mpdg-lens-ink:#ffffff;
    --mpdg-sh-18:rgba(15,23,42,.08);
    --mpdg-sh-22:rgba(15,23,42,.10);
    --mpdg-sh-40:rgba(15,23,42,.16);
    --mpdg-sh-42:rgba(15,23,42,.16);
    --mpdg-sh-62:rgba(15,23,42,.22);
    --mpdg-ghost-18:rgba(15,23,42,.12);
    --mpdg-ghost-14:rgba(15,23,42,.10);
    --mpdg-shadow-pop:rgba(15,23,42,.18);
}
html[data-mp-scheme="light"] .mpdg{
    --mpdg-line:rgba(15,23,42,.08);
    --mpdg-line-strong:rgba(15,23,42,.12);
    --mpdg-surface:rgba(255,255,255,.85);
    --mpdg-row-hover:rgba(15,23,42,.03);
}


/* ===== Toolbar ===== */
.mpdg-toolbar{
  display:flex;
  align-items:center;
  gap:8px;
  flex-wrap:wrap;
  margin-bottom:var(--mp-head-gap,16px);
}
/* A page tab rail that is a MEMBER of this toolbar does not also own the gap below it.
   .mp-seg-shell--rail (actions-badges) carries a margin-bottom so a rail heading a page can
   space itself from whatever follows, without pinning that gap to one following card class.
   Inside a toolbar the same margin lands in the flex LINE: it raises the toolbar rather than
   sitting under it, so the tray keeps its position and the surface below moves down by the
   rail's margin ON TOP OF the 16px this toolbar already sets. Two owners of one gap, and
   invisible to a reader of either stylesheet, because neither rule is wrong on its own.
   The toolbar wins here because it is the box spacing itself from the grid; the rail owns
   the gap only where it stands alone. Stated once, so a module that nests a rail inherits
   it rather than having to remember a flag.

   THIS RULE DID NOT BECOME UNNECESSARY when both sides moved to --mp-head-gap. One token
   makes the two contexts agree on the NUMBER; it does nothing about a rail contributing a
   second copy of it inside this toolbar, which would read 32px. */
.mpdg-toolbar .mp-seg-shell--rail{margin-bottom:0;}
.mpdg-toolbar-title{margin:0;}
.mpdg-toolbar-spacer{flex:1 1 auto;min-width:8px;}

.mpdg-search{
  position:relative;
  display:flex;
  align-items:center;
  min-width:200px;
}
.mpdg-search-input{
  width:100%;
  min-height:22px;
  padding:0 10px 0 28px;
  border-radius:var(--radius,5px);
  border:1px solid var(--mpdg-slate-18);
  background:var(--mpdg-deep-55);
  color:var(--text);
  font-family:inherit;
  font-size:var(--mp-fs-xs,11px);
  font-weight:var(--mp-fw-semibold,600);
  transition:border-color .16s ease, box-shadow .16s ease, background .16s ease;
}
.mpdg-search-input::placeholder{color:var(--mpdg-slate-75);}
.mpdg-search-input:focus{
  outline:none;
  border-color:var(--mpdg-hl-48);
  box-shadow:0 0 0 3px var(--mpdg-hl-10);
  border:3px solid var(--mpdg-slate-18);
  background:var(--mpdg-panel-solid);
}
.mpdg-search-ico{
  position:absolute;left:9px;top:50%;width:12px;height:12px;
  transform:translateY(-50%);
  background:currentColor;color:var(--mpdg-ink-muted);
  -webkit-mask:var(--mpdg-svg-search) center/contain no-repeat;
          mask:var(--mpdg-svg-search) center/contain no-repeat;
  pointer-events:none;
}

/* --- Collapsible search (engine adds .mpdg-search--pop) ---------------------------------------
   The engine relocates .mpdg-search to the toolbar end and turns it into a circular toggle like
   .mpdg-sort-dir. The input pops open leftward as an absolute overlay that slides OVER the
   neighbouring controls without reflowing them; the icon stays in the fixed 22px anchor circle at
   the right. .has-query flags an active term while collapsed. */
.mpdg-search--pop{width:22px;min-width:22px;flex:0 0 22px;height:22px;overflow:visible;}
.mpdg-search--pop .mpdg-search-input{
  position:absolute;right:0;top:50%;transform:translateY(-50%);
  width:22px;min-width:22px;height:22px;padding:0;
  border-radius:999px;color:transparent;cursor:pointer;
  transition:width .22s ease, padding .18s ease, color .12s ease, box-shadow .18s ease, border-color .16s ease;
}
.mpdg-search--pop:not(.is-open) .mpdg-search-input::placeholder{color:transparent;}
.mpdg-search--pop .mpdg-search-ico{left:50%;transform:translate(-50%,-50%);z-index:21;}
.mpdg-search--pop.is-open .mpdg-search-input{
  width:min(320px,60vw);padding:0 30px 0 12px;color:var(--text);cursor:text;
  z-index:20;box-shadow:0 12px 34px var(--mpdg-shadow-pop);
}
.mpdg-search--pop.has-query:not(.is-open) .mpdg-search-input{border-color:var(--mpdg-hl-full);}
.mpdg-search--pop.has-query:not(.is-open) .mpdg-search-ico{color:var(--mpdg-accent);}


/* --- The toolbar control rail ------------------------------------------------
   Filter / Group / Sort / Columns are four INDEPENDENT controls that belong together, so they
   sit in one .mp-seg-shell--flush tray (actions-badges) rather than as four loose flex items.
   Everything about the segments themselves comes from the Buttons registry - tertiary + seg +
   xs - and this file deliberately adds no geometry back. The block that used to live here set
   min-height:22px and font-size:var(--mp-fs-2xs), which is declaration for declaration what
   .btn--xs already said in a file this one cannot see; the registry had documented sizes as
   composable with seg, and nothing called it.

   ONLY TWO THINGS ARE LEFT, and both are about this cluster rather than about buttons. */
.mpdg-toolbar .mpdg-tool-rail{
  /* The tray hugs its members; the toolbar spacer does the pushing. */
  flex:0 0 auto;
}
/* Sort field + direction are ONE control that happens to have two hit targets, so they take a
   lighter internal rule than the tray's own member seams. Without the distinction the direction
   arrow reads as a fifth control in the rail rather than as part of Sort. */
/* A rail member that hides must actually go, and take its hairline with it. NOT a copy of the
   .btn[hidden] base rule, and measured so at v1.12.542 (tests/behavior/grid_hidden_members_test.js):
   the flush tray gives every MEMBER a display of its own, and the members are mostly not buttons —
   the filter, group and columns menus and the sort group are divs, which stay on screen at full
   width under [hidden] without this line. Stated for the whole rail, so the next optional member
   does not have to rediscover it. The two genuine .btn copies (.mpdg-sort-dir, the saved-views
   footer button) went in that release. */
.mpdg-tool-rail > [hidden]{display:none;}

/* A MENU PANEL IS NOT A SEGMENT. The flush tray squares its members (actions-badges) with a
   descendant selector, because a consumer may nest its trigger at any depth. A panel hangs inside
   a member too, so its rows were squared with it — every dropdown item, and the "Save as preset"
   button saved-views injects into the filter panel. The popup is not part of the flush row and
   keeps the button shape.

   The value repeats .btn's own base declaration (actions-badges) rather than reading a token of
   its own, which is a second copy of one number and is recorded as the cost of restoring rather
   than never flattening. Restoring is what the cascade allows here: the tray's rule cannot be
   narrowed without giving it a depth it is not allowed to assume. */
.mp-seg-shell--flush .mpdg-menu-panel .btn{
  border-radius:var(--radius,5px);
}

.mpdg-tool-rail .mpdg-sort-group .mpdg-sort-dir{
  box-shadow:inset 1px 0 0 var(--mp-fill-08);
}

/* Sort control: field menu + direction toggle */
.mpdg-sort-group{display:inline-flex;align-items:center;gap:6px;}
.mpdg-tool-rail .mpdg-sort-group{gap:0;}
.mpdg-sort-arrow{
  display:inline-block;width:12px;height:12px;background:currentColor;
  -webkit-mask:var(--mpdg-svg-arrowdown) center/contain no-repeat;mask:var(--mpdg-svg-arrowdown) center/contain no-repeat;
  transition:transform .16s ease;
}
.mpdg-sort-dir.is-asc .mpdg-sort-arrow{transform:rotate(180deg);}

/* Controls with no function in board view are hidden there */
.mpdg[data-mpdg-active-view="board"] [data-mpdg-hide-in-board]{display:none;}
.mpdg:not([data-mpdg-active-view="board"]) [data-mpdg-show-in-board]{display:none;}
/* The caret on a bespoke status-badge trigger. These six are not mp_button() calls — they are
   .mp-status-badge buttons that open an inline picker — so the Buttons registry cannot reach
   them and they read the glyph from mp_icon('chevron-down') themselves. What is left here is
   the BOX, not the glyph: the borders that used to draw a chevron are gone, because a shape
   of the icon vocabulary drawn in a consumer's stylesheet is one the icon registry cannot
   keep consistent. Every labelled mp_button trigger gets .btn-caret from the registry. */
.mpdg-caret{
  width:10px;height:10px;flex:0 0 10px;margin-left:2px;
  opacity:.7;transition:transform .16s ease;
}
[aria-expanded="true"] > .mpdg-caret{transform:rotate(180deg);}


/* Active group/sort value as a subtle inline chip. Empty span = no chip, so an
   inactive control reads just "Group" / "Sort" with no redundant "None". */
.mpdg-tool-val:empty{ display:none; }
.mpdg-tool-val:not(:empty){
  display:inline-flex;
  align-items:center;
  /* OPTICAL LIFT, NOT A CENTRING FIX. The chip's box is centred exactly - measured, chip mid,
     label mid and button mid all agree to the pixel - so this is about where the GLYPHS read
     inside a pill only ~2px taller than its own font. Asymmetric padding raises the text against
     the pill without moving the pill, and line-height:1 removes the half-leading that put it
     there. Kept as one declaration so re-tuning is one number.

     NOT VERIFIED IN THE SANDBOX, and that is worth saying rather than implying. The active face
     is Work Sans, loaded by modules/typography from Google Fonts, which the build environment
     cannot reach; a render here falls back to a different typeface whose vertical metrics are not
     the ones this nudge is for. Measuring the fallback and calling it centred is exactly the
     mistake this note exists to stop the next reader repeating. */
  padding:0 8px 2px;
  line-height:1;
  border-radius:999px;
  background:var(--grad-brand);
  color:#06111d;
  font-size:.86em;
  font-weight:var(--mp-fw-bold,700);
  letter-spacing:normal;
  text-transform:none;
}

/* Light scheme: the same hairline white emboss the brand-gradient primary button carries
   (modules/buttons/assets/buttons.css, "1.7.6"). The chip became dark-glyph-on-brand at
   v1.12.496 and so inherits the same problem that rule exists to solve - a dark label on a
   bright gradient reads flat without the letterpress. Same values, same scope, so the two
   surfaces cannot drift apart; the dark scheme stays byte-identical. */
html[data-mp-scheme="light"] .mpdg-tool-val:not(:empty){
  text-shadow:0 1px 0 rgba(255,255,255,.5);
}

.mpdg-count-badge{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:16px;height:16px;padding:0 5px;margin-left:2px;
  border-radius:999px;background:var(--grad-brand);color:#04110a;
  font-size:var(--mp-fs-2xs,10px);font-weight:var(--mp-fw-black,900);line-height:var(--mp-lh-none,1);
}
.mpdg-count-badge[hidden]{display:none;}

.mpdg-add-btn{
  background:linear-gradient(135deg,var(--green),var(--mpdg-hl-deep));
  color:#04110a;font-weight:var(--mp-fw-heavy,800);border:none;
  box-shadow:0 8px 22px var(--mpdg-hl-20);
  gap:6px;
}
.mpdg-add-btn:hover{transform:translateY(-1px);color:#04110a;}
.mpdg-add-plus{font-size:var(--mp-fs-lg,16px);font-weight:var(--mp-fw-black,900);line-height:var(--mp-lh-none,1);}

/* ===== Dropdown menus (toolbar + row kebab) ===== */
.mpdg-menu{position:relative;}
.mpdg-menu-panel{
  position:absolute;z-index:60;top:calc(100% + 8px);left:0;
  display:grid;gap:4px;min-width:210px;
  padding:8px;
  border:1px solid var(--mpdg-slate-18);
  border-radius:var(--radius,5px);
  background:linear-gradient(180deg,var(--mpdg-panel-98),var(--mpdg-panel-96));
  box-shadow:0 18px 42px var(--mpdg-sh-42),inset 0 1px 0 var(--mpdg-fill-04);
  backdrop-filter:blur(14px);
}
.mpdg-menu-panel[hidden]{display:none;}
.mpdg-menu[data-mpdg-menu="group"] .mpdg-menu-panel,
.mpdg-menu[data-mpdg-menu="columns"] .mpdg-menu-panel,
.mpdg-menu[data-mpdg-menu="sort"] .mpdg-menu-panel,
.mpdg-menu[data-mpdg-menu="filter"] .mpdg-menu-panel{right:0;left:auto;}
.mpdg-filter-panel{min-width:300px;max-height:70vh;overflow:auto;}

.mpdg-menu-item{
  display:flex;align-items:center;gap:9px;
  width:100%;min-height:34px;padding:7px 9px;
  border:0;border-radius:5px;background:transparent;
  color:var(--mpdg-ink-item);font-family:inherit;font-size:var(--mp-fs-base,13px);font-weight:var(--mp-fw-bold,700);
  text-align:left;cursor:pointer;transition:background .12s ease,color .12s ease;
}
.mpdg-menu-item:hover{background:var(--mpdg-fill-07);color:var(--mpdg-ink-invert);}
.mpdg-menu-item.is-active{background:var(--mpdg-hl-12);color:var(--mpdg-ink-on-hl);}
.mpdg-menu-item.is-active::after{content:'';margin-left:auto;width:15px;height:15px;flex:0 0 auto;background-color:var(--green);-webkit-mask:var(--mpdg-svg-check) center/contain no-repeat;mask:var(--mpdg-svg-check) center/contain no-repeat;}
.mpdg-menu-danger{color:var(--mpdg-ink-danger);}
.mpdg-menu-danger:hover{background:var(--mpdg-danger-hov);color:var(--mpdg-ink-invert);}

.mpdg-filter-group{
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    padding: 4px 2px 8px;
    border-bottom: 1px solid var(--mpdg-line);

}
.mpdg-filter-group:not(:has(~ .mpdg-filter-group)){border-bottom:0;}
.mpdg-filter-title{
  flex-basis:100%;margin:2px 4px 6px;color:var(--mpdg-ink-muted);font-size:var(--mp-fs-xs,11px);font-weight:var(--mp-fw-heavy,800);
  text-transform:uppercase;letter-spacing:var(--mp-ls-wider,0.08em);
}
/* A group title that carries a control (the Any/All include-mode toggle) becomes a row.
   Scoped with :has() so every other group title keeps its plain block rendering exactly
   as before - this rule cannot reach them. */
.mpdg-filter-title:has(.mpdg-mode-seg){
  display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:22px;
}
/* The tray inherits the title's uppercase/tracking otherwise; the labels are words, not headings. */
.mpdg-filter-title .mpdg-mode-seg{text-transform:none;letter-spacing:normal;flex:0 0 auto;}
.mpdg-filter-title .mpdg-mode-seg[hidden]{display:none;}
/* Size is the registry btn--xs modifier on the button itself (mp_button 'size'=>'xs';
   seg+size composition lives in buttons.css). Only the label emphasis/tracking stays
   mode-specific here, since btn--xs sets neither weight nor letter-spacing. */
.btn.mpdg-mode-btn{
  font-weight:var(--mp-fw-heavy,800);letter-spacing:var(--mp-ls-wide,0.04em);
}
.mpdg-check{
  display:flex;flex-direction:row;align-items:center;justify-content:flex-start;
  gap:9px;text-align:left;
  padding:6px 6px;border-radius:5px;cursor:pointer;
  font-size:var(--mp-fs-base,13px);font-weight:var(--mp-fw-semibold,600);color:var(--mpdg-ink-item);line-height:var(--mp-lh-snug,1.25);
}
.mpdg-check:hover{background:var(--mpdg-fill-05);}
.mpdg-check > input[type="checkbox"]{
  /* Resize the theme checkbox via its own token instead of fighting !important */
  --mp-choice-size:16px;
  flex:0 0 16px;margin:0;cursor:pointer;
}
.mpdg-menu-foot{padding-top:8px;display:flex;justify-content:flex-end;}
.mpdg-badge-inline{width:auto;min-height:22px;font-size:var(--mp-fs-xs,11px);}
.mpdg-pri-name{color:var(--mpdg-ink-soft);font-weight:var(--mp-fw-semibold,600);}

/* ===== Filter panel: inline chip layout =====
   Options render as inline toggle chips (status pills, priority badges, person avatars,
   text tags) that wrap, instead of a vertical checkbox list. The checkbox is kept for the
   engine's :checked read but visually hidden; the active chip gets a brand ring. Scoped to
   .mpdg-filter-group so the Columns menu's plain .mpdg-check list is untouched. */
.mpdg-filter-group .mpdg-check{
  position:relative;
  display:inline-flex;gap:6px;
  padding:2px;border-radius:999px;
  transition:box-shadow .14s ease,background .14s ease;
}
.mpdg-filter-group .mpdg-check:hover{background:var(--mpdg-slate-12);}
.mpdg-filter-group .mpdg-check:has(> input:checked){box-shadow:0 0 0 2px var(--green,var(--highlight));background:var(--mpdg-hl-08);}
.mpdg-filter-group .mpdg-check > input[type="checkbox"]{--mp-choice-size:1px;position:absolute;opacity:0;margin:0;pointer-events:none;}
.mpdg-filter-group .mpdg-check > span:not([class]){
  padding:3px 11px;border-radius:999px;
  background:var(--mpdg-slate-10);border:1px solid var(--mpdg-slate-18);
  font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-semibold,600);color:var(--mpdg-ink-soft);
}
.mpdg-filter-group .mpdg-check:has(> input:checked) > span:not([class]){border-color:transparent;color:var(--mpdg-ink-check);}
/* Reflected from an active scope lens, not from a facet. A host with a personal scope
   (e.g. the Tasks "Assigned" / "Created" tabs) marks the chip for the person that scope is
   about. The person IS being filtered - but by scope, so the value is NOT in state.filters
   and the FILTER badge does not count it. That is exactly why it must not wear the green
   include ring: a chip that looks checked while the badge ignores it is a lie. Sky ring +
   corner dot instead, and the host releases the lens when it is clicked. */
.mpdg-filter-group .mpdg-check.is-lens{box-shadow:0 0 0 2px var(--blue,var(--mpdg-accent));background:var(--mpdg-accent-10);}
.mpdg-filter-group .mpdg-check.is-lens::after{
  content:"";position:absolute;top:-1px;right:-1px;
  width:8px;height:8px;border-radius:999px;
  background:var(--blue,var(--mpdg-accent));box-shadow:0 0 0 2px var(--mpdg-lens-ink);
}
/* An include/exclude facet state always outranks the reflection: the user set that by hand. */
.mpdg-filter-group .mpdg-check.is-lens:has(> input:checked),
.mpdg-filter-group .mpdg-check.is-lens.is-exclude{box-shadow:0 0 0 2px var(--green,var(--highlight));}
.mpdg-filter-group .mpdg-check.is-lens.is-exclude{box-shadow:0 0 0 2px var(--mpdg-slate-40);}
.mpdg-filter-group .mpdg-check.is-lens:has(> input:checked)::after,
.mpdg-filter-group .mpdg-check.is-lens.is-exclude::after{display:none;}

/* Excluded (deactivated / hide) chip: monotone + muted, no colour. A subtle grey ring keeps
   it readable as a set state; the content is desaturated, dimmed and struck (avatars get a slash). */
.mpdg-filter-group .mpdg-check.is-exclude{box-shadow:0 0 0 2px var(--mpdg-slate-40);background:var(--mpdg-slate-10);}
.mpdg-filter-group .mpdg-check.is-exclude .mp-status-badge,
.mpdg-filter-group .mpdg-check.is-exclude .mp-priority-badge,
.mpdg-filter-group .mpdg-check.is-exclude .mpdg-pri-name,
.mpdg-filter-group .mpdg-check.is-exclude .mpdg-avatar,
.mpdg-filter-group .mpdg-check.is-exclude > span:not([class]){filter:grayscale(1);opacity:.5;}
.mpdg-filter-group .mpdg-check.is-exclude .mp-status-badge,
.mpdg-filter-group .mpdg-check.is-exclude .mp-priority-badge,
.mpdg-filter-group .mpdg-check.is-exclude .mpdg-pri-name,
.mpdg-filter-group .mpdg-check.is-exclude > span:not([class]){text-decoration:line-through;}
.mpdg-filter-group .mpdg-check.is-exclude .mpdg-avatar{position:relative;overflow:visible;}
.mpdg-filter-group .mpdg-check.is-exclude .mpdg-avatar::after{content:"";position:absolute;left:-2px;right:-2px;top:calc(50% - 1px);height:2px;background:var(--mpdg-ink-soft);transform:rotate(-45deg);border-radius:2px;}
/* Clear-all floats in the panel top-right corner (absolute, so it takes no row of its own);
   still last in DOM, so keyboard tabbing runs through the filters before reaching the reset.
   The .btn--xs sizing is applied by the engine (data-grid.js). */
.mpdg-filter-panel .mpdg-menu-foot{position:absolute;top:8px;right:10px;padding:0;border:0;z-index:1;}

/* Canonical extension footer for query-persistence services. The grid owns the layout; the
   service owns its action and behaviour, and creates buttons through the Buttons registry. */
.mpdg-filter-persist{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  margin-top:8px;padding:12px 2px 2px;border-top:1px solid var(--mpdg-line);
}
.mpdg-filter-persist-label{color:var(--mpdg-ink-muted);font-size:var(--mp-fs-xs,11px);line-height:var(--mp-lh-snug,1.25);}
.mpdg-filter-persist-actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex:0 0 auto;}
.mpdg-filter-persist-btn{white-space:nowrap;}
.mpdg-filter-persist[hidden]{display:none;}

@media (max-width:520px){
  .mpdg-filter-persist{align-items:flex-start;flex-direction:column;}
  .mpdg-filter-persist-actions{width:100%;flex-wrap:wrap;}
}
/* Filter priority chips show only the P1-P4 badge; the word stays in rows and the select. */
.mpdg-filter-group .mpdg-pri-name{display:none;}

/* Priority badge appearance now lives in the actions-badges component
   (.mp-priority-badge[data-priority]); the grid only adds the trigger variant. */

/* ===== List ===== */
.mpdg-list{
  border:1px solid var(--mpdg-line-strong);
  border-radius:14px;
  background:var(--mpdg-surface);
  overflow:hidden;
}
.mpdg[data-mpdg-active-view="board"] .mpdg-list{display:none;}

/* EXCEPT WHEN IT IS CARRYING THE ONLY THING LEFT TO SAY.

   apply() sets [data-mpdg-noresults].hidden from visible.length on EVERY view, board included — so
   in board view the message was correctly unhidden and then painted nowhere, because it lives
   inside .mpdg-list and the rule above hides the whole card. A reader filtering a board down to
   nothing got six empty rails and no sentence. Measured: hidden=false, painted height 0.

   The list becomes a bare container for that one child rather than a card: no border, no
   background, no head, no rows. It sits above the board in source order, which is where the
   sentence belongs — the buckets stay visible underneath, because "no tasks match" and "these are
   your columns" are both true and the second is not an apology for the first. */
.mpdg[data-mpdg-active-view="board"] .mpdg-list:has(> [data-mpdg-noresults]:not([hidden])){
  display:block;
  border:0;
  background:none;
  overflow:visible;
}
.mpdg[data-mpdg-active-view="board"] .mpdg-list .mpdg-head,
.mpdg[data-mpdg-active-view="board"] .mpdg-list .mpdg-rows{
  display:none;
}

.mpdg-head{
  display:grid;grid-template-columns:var(--mpdg-grid);
  align-items:stretch;gap:14px;
  padding:0 14px;min-height:44px;
  border-bottom:1px solid var(--mpdg-line-strong);
  background:var(--mpdg-deep-30);
}
.mpdg-h{display:flex;align-items:center;color:var(--mpdg-ink-muted);font-size:var(--mp-fs-xs,11px);font-weight:var(--mp-fw-heavy,800);text-transform:uppercase;letter-spacing:var(--mp-ls-wider,0.08em);}
.mpdg-sort{
  display:inline-flex;align-items:center;gap:6px;
  border:0;background:none;cursor:pointer;
  color:inherit;font:inherit;text-transform:inherit;letter-spacing:inherit;
  padding:0;
}
.mpdg-sort:hover{color:var(--mpdg-ink);}
.mpdg-sort-ind{width:10px;height:10px;opacity:0;transition:opacity .12s ease;}
.mpdg-sort.is-asc .mpdg-sort-ind,.mpdg-sort.is-desc .mpdg-sort-ind{
  opacity:.9;background:currentColor;
  -webkit-mask:var(--mpdg-svg-caret) center/contain no-repeat;mask:var(--mpdg-svg-caret) center/contain no-repeat;
}
.mpdg-sort.is-asc .mpdg-sort-ind{transform:rotate(180deg);}

/* ===== Rail =====
   A wrapper around a row's editable cells (status, dates, estimate or budget, followers). On the
   grid it has no box, so the cells stay grid items in their tracks and nothing about the desktop
   layout knows it is there. Its whole purpose is the flex-card breakpoint below, where it is the
   card's second line. */
.mpdg-rail{display:contents;}

/* ===== Row ===== */
.mpdg-row{
  display:grid;grid-template-columns:var(--mpdg-grid);
  align-items:stretch;gap:14px;
  padding:12px 14px;
  border-bottom:1px solid var(--mpdg-line);
  transition:background .12s ease;
}

/* NO content-visibility ON A DRAG, AND THE ABSENCE IS THE POINT.

   v1.12.469 put `content-visibility:auto` on list rows under .mpdg.is-resizing, to relieve
   raster while the window was being dragged. It was removed at v1.12.471 because it did not
   do that. Measured in Chromium with the rule toggled and a rule-off control in every
   condition: 74 of 100 rows skipped when the viewport is STILL, and 0 of 100 during a drag,
   slow or fast. Every viewport change invalidates relevance for every candidate, so a
   continuous drag never reaches a state in which anything can be marked skipped - and this
   rule was scoped to exactly that state. It skipped nothing, for as long as it shipped.

   It looked like it worked at 10 and 25 rows because those pages were never over budget.

   THE ACTUAL CAUSE was `backdrop-filter:blur(8px)` on the base .btn class in
   actions-badges: six buttons per row, 600 on a page of 100, each one a backdrop root with
   its own layer and a full readback, re-sampled on every :hover because .btn:hover moves
   the element. A DevTools trace from the installation, before and after: GPU memory median
   1 106 MB -> 354 MB, checkerboarded_needs_raster 130 -> 0, high-latency frames 528 -> 3,
   dropped frames 26.6% -> 6.1% - on a recording with 7.6x more resizing in it.

   IF THIS IS EVER REACHED FOR AGAIN: it is a real instrument for the STILL state, where it
   demonstrably skips. Applying it permanently is the textbook use and is blocked by one
   thing - it implies contain:layout style paint, which makes every row a containing block
   for position:fixed, and floatPanel() lifts the row kebab there without reparenting it.
   Since v1.12.470 that kebab is the ONLY panel still floated out of a task row; the status
   and priority popovers moved to the grid-level dictionary. Move the kebab the same way and
   the blocker is gone. Do not re-add it scoped to a drag: that is this entry. */
.mpdg-rows .mpdg-row:last-child{border-bottom:0;}
.mpdg-row:hover{background:var(--mpdg-row-hover);}
.mpdg-row[hidden]{display:none;}
.mpdg-row.is-open{background:var(--mpdg-fill-022);}

/* ===== Settling =====
   A row that no longer belongs where it is recedes for a moment: its cells blur and dim, and a
   badge sits in the middle saying who did this, beside a throbber. The row is still there and
   still true — it has simply stopped being the thing you are meant to read for a second.

   The blur is on the CELLS, not on the row, because a filter on the row would blur the badge with
   everything else. Only transform and opacity are animated, both on the compositor: the blur is a
   static value the browser rasterises once per state, not a per-frame recomputation, so 3.5s of
   this costs no layout and no relayout of the 339-row list around it.

   The ring reuses .brand-mark, the platform's own mark, given a spinner treatment scoped to this
   overlay. NOTE: chat/assets/chat.css gives the same mark the same treatment for its loading
   state. Two spinners, one mark — this belongs in foundation as one component with tokens, and is
   recorded here rather than quietly copied a third time. */
.mpdg-row.is-settling{position:relative;}
/* The rail is excluded and reached through: with no box on the grid a filter on it would blur
   nothing, and with a box on the card it would blur its children twice. */
.mpdg-row.is-settling > *:not(.mpdg-settle):not(.mpdg-rail),
.mpdg-row.is-settling > .mpdg-rail > *{
  filter:blur(2.5px);
  opacity:.38;
  transition:opacity .18s ease, filter .18s ease;
  pointer-events:none;
}
.mpdg-settle{
  position:absolute;inset:0;z-index:2;
  display:flex;align-items:center;justify-content:center;
  pointer-events:none;
}
.mpdg-settle-badge{
  display:inline-flex;align-items:center;gap:8px;
  max-width:calc(100% - 24px);
  padding:5px 12px 5px 6px;
  border-radius:999px;
  background:var(--mpdg-table-bg);
  border:1px solid var(--mpdg-line-09);
  box-shadow:0 8px 24px var(--mpdg-sh-18);
  font-size:var(--mp-fs-sm,12px);line-height:1.5;
  color:var(--mpdg-ink-strong);
  animation:mpdg-settle-in .18s ease both;
}
@keyframes mpdg-settle-in{from{opacity:0;transform:translateY(3px);}to{opacity:1;transform:none;}}

.mpdg-settle-who{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

/* Only the throbber: a circle around it, with the padding equal on every side. The pill shape is
   for a badge with something to say; without a name it read as unfinished, which it was. */
.mpdg-settle-badge.is-bare{padding:6px;border-radius:50%;}

/* The throbber, same mark and same motion language as the chat loader. */
.mpdg-settle .brand-mark{
  --mp-brand-ring-size:20px;
  --mp-brand-ring-stroke:3px;
  position:relative;
  width:var(--mp-brand-ring-size);height:var(--mp-brand-ring-size);
  flex:0 0 var(--mp-brand-ring-size);
  border-radius:50%;
  animation:mpdg-settle-spin 1.1s linear infinite;
}
.mpdg-settle .brand-mark::before{
  content:"";position:absolute;inset:0;border-radius:50%;pointer-events:none;
  background:conic-gradient(var(--mp-hl-05,rgba(0,234,94,.05)) 0deg,var(--mp-hl-18,rgba(0,234,94,.18)) 80deg,var(--highlight,#00ea5e) 138deg,var(--mp-blue-92,rgba(56,189,248,.92)) 178deg,var(--mp-hl-12,rgba(0,234,94,.12)) 235deg,var(--mp-hl-05,rgba(0,234,94,.05)) 360deg);
  mask:radial-gradient(farthest-side,transparent calc(100% - var(--mp-brand-ring-stroke) - 1px),#000 calc(100% - var(--mp-brand-ring-stroke)));
}
@keyframes mpdg-settle-spin{to{transform:rotate(360deg);}}

/* Hover stops the clock and says so: you are reading this row, so the list waits. */
.mpdg-settle.is-paused .brand-mark{animation-play-state:paused;}
.mpdg-row.is-settling:hover > *:not(.mpdg-settle):not(.mpdg-rail),
.mpdg-row.is-settling:hover > .mpdg-rail > *{filter:blur(1px);opacity:.62;}

/* No treatment, same delay. The delay is functional — it is what stops a row vanishing before it
   has been read — and the blur, the badge and the throbber are decoration. The engine holds the
   row on the same timer and draws none of this. */
@media (prefers-reduced-motion:reduce){
  .mpdg-settle{display:none;}
  .mpdg-row.is-settling > *:not(.mpdg-settle):not(.mpdg-rail),
  .mpdg-row.is-settling > .mpdg-rail > *{filter:none;opacity:1;transition:none;}
  .mpdg-settle-badge,.mpdg-settle .brand-mark{animation:none;}
}


/* Hairline dividers between the data columns (full row height, inset by padding) */
.mpdg-h.mpdg-col-status,.mpdg-h.mpdg-col-due,.mpdg-h.mpdg-col-estimate,.mpdg-h.mpdg-divide,
.mpdg-cell-status,.mpdg-cell-due,.mpdg-cell-estimate,.mpdg-cell.mpdg-divide{
  border-left:1px solid var(--mpdg-line);
  padding-left:18px;
}

.mpdg-cell{min-width:0;}
.mpdg-cell-task,.mpdg-cell-primary{display:flex;align-items:center;gap:11px;}
.mpdg-task-body,.mpdg-primary-body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;justify-content:center;}
.mpdg-task-counts{flex:0 0 auto;display:flex;flex-direction:column;align-items:flex-start;gap:5px;margin-left:auto;padding-left:6px;}
.mpdg-cell-grip{display:flex;align-items:center;justify-content:center;}
.mpdg-expand{
  display:inline-flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:6px;
  border:1px solid transparent;background:transparent;color:var(--mpdg-ink-muted);cursor:pointer;
  transition:background .12s ease,color .12s ease;
}
.mpdg-expand:hover{background:var(--mpdg-fill-06);color:var(--mpdg-ink);}

/* Task cell */
.mpdg-task-main,.mpdg-primary-main{display:flex;align-items:center;gap:6px;}
.mpdg-title{
  border:0;background:none;padding:0;cursor:pointer;
  color:var(--mpdg-ink-strong);font-family:inherit;font-size:var(--mp-fs-md,14px);font-weight:var(--mp-fw-bold,700);
  letter-spacing:var(--mp-ls-tight,-0.01em);text-align:left;line-height:var(--mp-lh-snug,1.25);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;
}
.mpdg-title:hover{color:var(--mpdg-ink-invert);text-decoration:underline;text-underline-offset:2px;}
.mpdg-title-edit{
  display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;border-radius:5px;flex:0 0 22px;
  border:0;background:none;color:var(--mpdg-ink-icon);cursor:pointer;opacity:0;
  transition:opacity .12s ease,color .12s ease,background .12s ease;
}
.mpdg-row:hover .mpdg-title-edit{opacity:1;}
.mpdg-title-edit:hover{background:var(--mpdg-hl-12);color:var(--green-light);}
/* Copy-link control — sibling of the edit pencil; copies a deep link to the row's
   task (resolved + copied by the shared engine via [data-mpdg-copy-link]). */
.mpdg-title-link{
  display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;border-radius:5px;flex:0 0 22px;
  border:0;background:none;color:var(--mpdg-ink-icon);cursor:pointer;opacity:0;
  transition:opacity .12s ease,color .12s ease,background .12s ease;
}
.mpdg-row:hover .mpdg-title-link{opacity:1;}
.mpdg-title-link:hover{background:var(--mpdg-accent-14);color:var(--mpdg-accent);}
.mpdg-title-link:focus-visible{opacity:1;outline:2px solid var(--mpdg-accent);outline-offset:1px;}
/* Transient confirmation after a copy. The list container has overflow:hidden, which
   clips floating bubbles, so we flash the control itself: chain glyph -> green check
   on success, red tint on failure. Cleared by the engine after ~1.4s. */
.mpdg-title-link.is-copied,.mpdg-title-link.is-copyfail{opacity:1;}
.mpdg-title-link.is-copied{color:var(--green-light);background:var(--mpdg-hl-16);}
.mpdg-title-link.is-copied .mpdg-ico{-webkit-mask-image:var(--mpdg-svg-check);mask-image:var(--mpdg-svg-check);}
.mpdg-title-link.is-copyfail{color:var(--mpdg-ink-danger-soft);background:var(--mpdg-danger-16);}

.mpdg-task-sub,.mpdg-primary-sub{
  display:flex;align-items:center;gap:6px;margin-top:3px;
  font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-semibold,600);color:var(--mpdg-ink-muted);
  overflow:hidden;white-space:nowrap;text-overflow:ellipsis;
}
.mpdg-company{color:var(--mpdg-accent-soft);}
.mpdg-company,.mpdg-project{display:inline-flex;align-items:center;gap:5px;min-width:0;overflow:hidden;text-overflow:ellipsis;}
/* Sub-line entity badges (company / related-entity chips) shared by every grid.
   Lifted here from the Tasks (#tasks-overview) and Projects (.mpdg-projects) modules,
   which each carried a byte-identical copy. Single source of truth. Placed AFTER
   .mpdg-company/.mpdg-project so its gap:4px wins the equal-specificity tie the way the
   old ID-scoped copies did (they beat these class rules on specificity). */
.mpdg-badge{display:inline-flex;align-items:center;gap:4px;max-width:100%;padding:1px 7px;border-radius:999px;border:1px solid transparent;background:var(--mpdg-slate-10);color:var(--mpdg-ink-soft);font-size:var(--mp-fs-2xs,10px);font-weight:var(--mp-fw-bold,700);line-height:var(--mp-lh-relaxed,1.5);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.mpdg-badge.is-empty{opacity:.55;border-style:dashed;}
/* Tone modifier, not an entity class: .mpdg-company / .mpdg-project say WHAT a chip is,
   .is-empty / .is-blocked say what STATE it is in — same axis as .is-empty, so same
   naming. Amber rather than red: waiting on unfinished work is a normal state of a
   plan, not an error, and red is already spent on a contradicted dependency. */
.mpdg-badge.is-blocked{
  border-color:color-mix(in srgb,var(--amber) 42%,transparent);
  background:color-mix(in srgb,var(--amber) 14%,transparent);
  color:var(--amber);
}
.mpdg-badge.is-blocked svg,.mpdg-badge.is-blocked .mp-icon{width:11px;height:11px;}
/* Blocked AND contradicted. Amber says "waiting on work", which is an ordinary state of
   a plan; red says "the plan disagrees with itself" — this task is scheduled to start
   before the work it waits on can end. The glyph swaps with the colour in the row markup:
   a state carried by hue alone is a state a colour-blind reader never receives. */
.mpdg-badge.is-blocked.is-violated{
  border-color:color-mix(in srgb,var(--red) 46%,transparent);
  background:color-mix(in srgb,var(--red) 15%,transparent);
  color:var(--red);
}
/* Reuse the modal's company / project / contact glyphs, scaled to the sub-line */
.mpdg-ent-ico{flex:0 0 auto;transform:scale(.8);transform-origin:center;margin:0 -2px;opacity:.95;}
.mpdg-dot{color:var(--mpdg-ink-faint);}

.mpdg-avatar{
  display:inline-flex;align-items:center;justify-content:center;
  width:24px;height:24px;flex:0 0 24px;border-radius:50%;
  /* THE CANONICAL AVATAR VARIABLE FIRST. The grid-local one behind it is a legacy parallel on a
     ratchet, so a new call site folds the surface forward instead of adding another copy — which is
     what the register caught when the board's assignee heading was first written against the old
     one. The prose deliberately does not spell that variable: the probe counts occurrences and does
     not strip comments, so explaining it here would have been a twenty-second one. Existing markup
     keeps working through the fallback, so nothing has to move at once. */
  background:var(--mp-av,var(--mpdg-av,var(--mpdg-ink-dim)));
  color:#04110a;font-size:var(--mp-fs-2xs,10px);font-weight:var(--mp-fw-black,900);letter-spacing:var(--mp-ls-normal,0);
  box-shadow:0 0 0 2px var(--mpdg-avatar-ring);
}
.mpdg-followers .mpdg-avatar{margin-left:-7px;}
.mpdg-followers .mpdg-avatar:first-child{margin-left:0;}
.mpdg-task-avatar,.mpdg-primary-lead{flex:0 0 30px;width:30px;height:30px;font-size:var(--mp-fs-xs,11px);}

.mpdg-meta-counts{display:inline-flex;align-items:center;gap:10px;flex:0 0 auto;padding-left:2px;}
.mpdg-chip-count{
  display:inline-flex;align-items:center;gap:4px;
  font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-bold,700);color:var(--mpdg-ink-count);
}
.mpdg-chip-count.is-empty{opacity:.4;}
/* Theme file-pill rendered inside the task cell (and after live updates) — keep it compact */
.mpdg [data-task-files-cell]{display:inline-flex;align-items:center;}
.mpdg .task-file-pill{
  gap:4px;padding:0;background:none;color:var(--mpdg-ink-count);font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-bold,700);
}
.mpdg .task-file-pill-icon{color:var(--mpdg-ink-count);}
.mpdg [data-task-files-cell] .muted{font-size:var(--mp-fs-sm,12px);}
.mpdg-ico{display:inline-block;width:14px;height:14px;background:currentColor;
  -webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-size:contain;mask-size:contain;}
.mpdg-ico-chat{-webkit-mask-image:var(--mpdg-svg-chat);mask-image:var(--mpdg-svg-chat);}
.mpdg-ico-cal{-webkit-mask-image:var(--mpdg-svg-cal);mask-image:var(--mpdg-svg-cal);width:13px;height:13px;color:var(--mpdg-ink-muted);}
.mpdg-ico-clock{-webkit-mask-image:var(--mpdg-svg-clock);mask-image:var(--mpdg-svg-clock);width:13px;height:13px;color:var(--mpdg-ink-muted);}
/* A standing commitment where a deliverable shows a calendar: the row states a cadence, not a
   date, so the glyph has to say the same thing the text does. */
.mpdg-ico-repeat{-webkit-mask-image:var(--mpdg-svg-repeat);mask-image:var(--mpdg-svg-repeat);width:13px;height:13px;color:var(--mpdg-ink-muted);}
.mpdg-ico-pencil{-webkit-mask-image:var(--mpdg-svg-pencil);mask-image:var(--mpdg-svg-pencil);}
.mpdg-ico-link{-webkit-mask-image:var(--mpdg-svg-link);mask-image:var(--mpdg-svg-link);}
.mpdg-ico-group{-webkit-mask-image:var(--mpdg-svg-group);mask-image:var(--mpdg-svg-group);}
.mpdg-ico-title{-webkit-mask-image:var(--mpdg-svg-title);mask-image:var(--mpdg-svg-title);}
/* ===== Field glyphs =====
   ONE TABLE, field key -> glyph, and every place a field is named paints from it: the column
   headers ([data-mpdg-sort] on the header button, [data-mpdg-field] on a header without one) and
   the Sort and Group menus ([data-mpdg-sortfield], [data-mpdg-group]). Until v1.12.543 the menus
   were decorated by data-grid.js from its own FIELD_ICONS object after boot, and the headers
   carried nothing; a header decorated the same way would have shifted its label a frame after
   first paint. Here the glyph is on screen with the stylesheet, and there is no second map to
   drift — the engine's is gone.

   HOW IT READS: a key rule sets --mpdg-field-ico on the element that names the field; the paint
   rule draws it in that element's ::before, which inherits it from there. EVERY element that
   names a field first gets the dot at zero specificity (:where), so an unmapped key paints the
   dot — the engine's old fallback — and never borrows a glyph from a keyed ancestor. Registering
   the property non-inheriting would have been the obvious way to say that, and it is wrong: a
   ::before inherits from the element it hangs on, so the glyph would never reach the paint.
   [data-mpdg-icon] overrides the key where a key is ambiguous (Companies sorts its company by
   `name`), and comes after the table so it wins at equal specificity.
   Adding a field is one entry here and nothing else. */
:where([data-mpdg-sort],[data-mpdg-sortfield],[data-mpdg-group],[data-mpdg-field]){--mpdg-field-ico:var(--mpdg-svg-dot);}
.mpdg :is([data-mpdg-sortfield],[data-mpdg-group])::before,
.mpdg .mpdg-h [data-mpdg-sort]::before,
.mpdg .mpdg-h[data-mpdg-field]::before{
  content:'';flex:0 0 auto;width:16px;height:16px;background:currentColor;opacity:.9;
  -webkit-mask:var(--mpdg-field-ico) center/contain no-repeat;
  mask:var(--mpdg-field-ico) center/contain no-repeat;
}
/* In a header the glyph sits at the cap height of an 11px uppercase label, a step quieter. */
.mpdg .mpdg-h [data-mpdg-sort]::before,
.mpdg .mpdg-h[data-mpdg-field]::before{width:13px;height:13px;opacity:.75;}
.mpdg .mpdg-h[data-mpdg-field]{gap:6px;}
[data-mpdg-sort=""],[data-mpdg-sortfield=""],[data-mpdg-group=""],[data-mpdg-field=""],[data-mpdg-sort="none"],[data-mpdg-sortfield="none"],[data-mpdg-group="none"],[data-mpdg-field="none"]{--mpdg-field-ico:var(--mpdg-svg-none);}
[data-mpdg-sort="title"],[data-mpdg-sortfield="title"],[data-mpdg-group="title"],[data-mpdg-field="title"],[data-mpdg-sort="task"],[data-mpdg-sortfield="task"],[data-mpdg-group="task"],[data-mpdg-field="task"]{--mpdg-field-ico:var(--mpdg-svg-task);}
[data-mpdg-sort="name"],[data-mpdg-sortfield="name"],[data-mpdg-group="name"],[data-mpdg-field="name"],[data-mpdg-sort="subject"],[data-mpdg-sortfield="subject"],[data-mpdg-group="subject"],[data-mpdg-field="subject"]{--mpdg-field-ico:var(--mpdg-svg-title);}
[data-mpdg-sort="status"],[data-mpdg-sortfield="status"],[data-mpdg-group="status"],[data-mpdg-field="status"],[data-mpdg-sort="active"],[data-mpdg-sortfield="active"],[data-mpdg-group="active"],[data-mpdg-field="active"],[data-mpdg-sort="enabled"],[data-mpdg-sortfield="enabled"],[data-mpdg-group="enabled"],[data-mpdg-field="enabled"],[data-mpdg-sort="state"],[data-mpdg-sortfield="state"],[data-mpdg-group="state"],[data-mpdg-field="state"]{--mpdg-field-ico:var(--mpdg-svg-statusdot);}
[data-mpdg-sort="email"],[data-mpdg-sortfield="email"],[data-mpdg-group="email"],[data-mpdg-field="email"],[data-mpdg-sort="mail"],[data-mpdg-sortfield="mail"],[data-mpdg-group="mail"],[data-mpdg-field="mail"],[data-mpdg-sort="inbox"],[data-mpdg-sortfield="inbox"],[data-mpdg-group="inbox"],[data-mpdg-field="inbox"],[data-mpdg-sort="provider"],[data-mpdg-sortfield="provider"],[data-mpdg-group="provider"],[data-mpdg-field="provider"]{--mpdg-field-ico:var(--mpdg-svg-mail);}
[data-mpdg-sort="priority"],[data-mpdg-sortfield="priority"],[data-mpdg-group="priority"],[data-mpdg-field="priority"]{--mpdg-field-ico:var(--mpdg-svg-flag);}
[data-mpdg-sort="type"],[data-mpdg-sortfield="type"],[data-mpdg-group="type"],[data-mpdg-field="type"],[data-mpdg-sort="category"],[data-mpdg-sortfield="category"],[data-mpdg-group="category"],[data-mpdg-field="category"],[data-mpdg-sort="label"],[data-mpdg-sortfield="label"],[data-mpdg-group="label"],[data-mpdg-field="label"],[data-mpdg-sort="runtime"],[data-mpdg-sortfield="runtime"],[data-mpdg-group="runtime"],[data-mpdg-field="runtime"],[data-mpdg-sort="architecture"],[data-mpdg-sortfield="architecture"],[data-mpdg-group="architecture"],[data-mpdg-field="architecture"]{--mpdg-field-ico:var(--mpdg-svg-tag);}
[data-mpdg-sort="deadline"],[data-mpdg-sortfield="deadline"],[data-mpdg-group="deadline"],[data-mpdg-field="deadline"],[data-mpdg-sort="due"],[data-mpdg-sortfield="due"],[data-mpdg-group="due"],[data-mpdg-field="due"],[data-mpdg-sort="date"],[data-mpdg-sortfield="date"],[data-mpdg-group="date"],[data-mpdg-field="date"],[data-mpdg-sort="submitted"],[data-mpdg-sortfield="submitted"],[data-mpdg-group="submitted"],[data-mpdg-field="submitted"],[data-mpdg-sort="created"],[data-mpdg-sortfield="created"],[data-mpdg-group="created"],[data-mpdg-field="created"],[data-mpdg-sort="updated"],[data-mpdg-sortfield="updated"],[data-mpdg-group="updated"],[data-mpdg-field="updated"]{--mpdg-field-ico:var(--mpdg-svg-cal);}
[data-mpdg-sort="start"],[data-mpdg-sortfield="start"],[data-mpdg-group="start"],[data-mpdg-field="start"],[data-mpdg-sort="start_date"],[data-mpdg-sortfield="start_date"],[data-mpdg-group="start_date"],[data-mpdg-field="start_date"],[data-mpdg-sort="begin"],[data-mpdg-sortfield="begin"],[data-mpdg-group="begin"],[data-mpdg-field="begin"]{--mpdg-field-ico:var(--mpdg-svg-start);}
[data-mpdg-sort="budget"],[data-mpdg-sortfield="budget"],[data-mpdg-group="budget"],[data-mpdg-field="budget"],[data-mpdg-sort="budget_hours"],[data-mpdg-sortfield="budget_hours"],[data-mpdg-group="budget_hours"],[data-mpdg-field="budget_hours"]{--mpdg-field-ico:var(--mpdg-svg-budget);}
[data-mpdg-sort="estimate"],[data-mpdg-sortfield="estimate"],[data-mpdg-group="estimate"],[data-mpdg-field="estimate"],[data-mpdg-sort="duration"],[data-mpdg-sortfield="duration"],[data-mpdg-group="duration"],[data-mpdg-field="duration"],[data-mpdg-sort="time"],[data-mpdg-sortfield="time"],[data-mpdg-group="time"],[data-mpdg-field="time"],[data-mpdg-sort="hours"],[data-mpdg-sortfield="hours"],[data-mpdg-group="hours"],[data-mpdg-field="hours"]{--mpdg-field-ico:var(--mpdg-svg-clock);}
[data-mpdg-sort="owner"],[data-mpdg-sortfield="owner"],[data-mpdg-group="owner"],[data-mpdg-field="owner"],[data-mpdg-sort="assignee"],[data-mpdg-sortfield="assignee"],[data-mpdg-group="assignee"],[data-mpdg-field="assignee"],[data-mpdg-sort="user"],[data-mpdg-sortfield="user"],[data-mpdg-group="user"],[data-mpdg-field="user"],[data-mpdg-sort="member"],[data-mpdg-sortfield="member"],[data-mpdg-group="member"],[data-mpdg-field="member"]{--mpdg-field-ico:var(--mpdg-svg-person);}
[data-mpdg-sort="company"],[data-mpdg-sortfield="company"],[data-mpdg-group="company"],[data-mpdg-field="company"],[data-mpdg-sort="account"],[data-mpdg-sortfield="account"],[data-mpdg-group="account"],[data-mpdg-field="account"]{--mpdg-field-ico:var(--mpdg-svg-building);}
[data-mpdg-sort="industry"],[data-mpdg-sortfield="industry"],[data-mpdg-group="industry"],[data-mpdg-field="industry"],[data-mpdg-sort="sector"],[data-mpdg-sortfield="sector"],[data-mpdg-group="sector"],[data-mpdg-field="sector"]{--mpdg-field-ico:var(--mpdg-svg-industry);}
[data-mpdg-sort="country"],[data-mpdg-sortfield="country"],[data-mpdg-group="country"],[data-mpdg-field="country"],[data-mpdg-sort="region"],[data-mpdg-sortfield="region"],[data-mpdg-group="region"],[data-mpdg-field="region"],[data-mpdg-sort="location"],[data-mpdg-sortfield="location"],[data-mpdg-group="location"],[data-mpdg-field="location"]{--mpdg-field-ico:var(--mpdg-svg-country);}
[data-mpdg-sort="folder"],[data-mpdg-sortfield="folder"],[data-mpdg-group="folder"],[data-mpdg-field="folder"],[data-mpdg-sort="project"],[data-mpdg-sortfield="project"],[data-mpdg-group="project"],[data-mpdg-field="project"],[data-mpdg-sort="package"],[data-mpdg-sortfield="package"],[data-mpdg-group="package"],[data-mpdg-field="package"]{--mpdg-field-ico:var(--mpdg-svg-folder);}
[data-mpdg-sort="contact"],[data-mpdg-sortfield="contact"],[data-mpdg-group="contact"],[data-mpdg-field="contact"],[data-mpdg-sort="contacts"],[data-mpdg-sortfield="contacts"],[data-mpdg-group="contacts"],[data-mpdg-field="contacts"]{--mpdg-field-ico:var(--mp-icon-user);}
[data-mpdg-sort="role"],[data-mpdg-sortfield="role"],[data-mpdg-group="role"],[data-mpdg-field="role"]{--mpdg-field-ico:var(--mpdg-svg-shield-check);}
[data-mpdg-sort="followers"],[data-mpdg-sortfield="followers"],[data-mpdg-group="followers"],[data-mpdg-field="followers"]{--mpdg-field-ico:var(--mpdg-svg-users);}
[data-mpdg-sort="module"],[data-mpdg-sortfield="module"],[data-mpdg-group="module"],[data-mpdg-field="module"],[data-mpdg-sort="controls"],[data-mpdg-sortfield="controls"],[data-mpdg-group="controls"],[data-mpdg-field="controls"]{--mpdg-field-ico:var(--mpdg-svg-grid);}
[data-mpdg-sort="editor"],[data-mpdg-sortfield="editor"],[data-mpdg-group="editor"],[data-mpdg-field="editor"]{--mpdg-field-ico:var(--mpdg-svg-pencil);}
[data-mpdg-icon="budget"]{--mpdg-field-ico:var(--mpdg-svg-budget);}
[data-mpdg-icon="building"]{--mpdg-field-ico:var(--mpdg-svg-building);}
[data-mpdg-icon="cal"]{--mpdg-field-ico:var(--mpdg-svg-cal);}
[data-mpdg-icon="clock"]{--mpdg-field-ico:var(--mpdg-svg-clock);}
[data-mpdg-icon="company"]{--mpdg-field-ico:var(--mpdg-svg-building);}
[data-mpdg-icon="contact"]{--mpdg-field-ico:var(--mp-icon-user);}
[data-mpdg-icon="contacts"]{--mpdg-field-ico:var(--mp-icon-user);}
[data-mpdg-icon="country"]{--mpdg-field-ico:var(--mpdg-svg-country);}
[data-mpdg-icon="flag"]{--mpdg-field-ico:var(--mpdg-svg-flag);}
[data-mpdg-icon="folder"]{--mpdg-field-ico:var(--mpdg-svg-folder);}
[data-mpdg-icon="grid"]{--mpdg-field-ico:var(--mpdg-svg-grid);}
[data-mpdg-icon="industry"]{--mpdg-field-ico:var(--mpdg-svg-industry);}
[data-mpdg-icon="mail"]{--mpdg-field-ico:var(--mpdg-svg-mail);}
[data-mpdg-icon="pencil"]{--mpdg-field-ico:var(--mpdg-svg-pencil);}
[data-mpdg-icon="person"]{--mpdg-field-ico:var(--mpdg-svg-person);}
[data-mpdg-icon="project"]{--mpdg-field-ico:var(--mpdg-svg-folder);}
[data-mpdg-icon="shield-check"]{--mpdg-field-ico:var(--mpdg-svg-shield-check);}
[data-mpdg-icon="start"]{--mpdg-field-ico:var(--mpdg-svg-start);}
[data-mpdg-icon="statusdot"]{--mpdg-field-ico:var(--mpdg-svg-statusdot);}
[data-mpdg-icon="tag"]{--mpdg-field-ico:var(--mpdg-svg-tag);}
[data-mpdg-icon="task"]{--mpdg-field-ico:var(--mpdg-svg-task);}
[data-mpdg-icon="title"]{--mpdg-field-ico:var(--mpdg-svg-title);}
[data-mpdg-icon="users"]{--mpdg-field-ico:var(--mpdg-svg-users);}

/* Status cell — one fixed-width control, vertically centred, not full-width. Shared by every
   grid with a status column (Tasks, Projects, Companies, Contacts, Mail, Modules). */
.mpdg-cell-status{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:8px;--mpdg-ctl-w:128px;}
/* Priority — its own narrow column, badge and header icon centred in it. It takes the hairline the
   other data columns draw but NOT their 18px inset, which would push a 64px column's content off
   centre; so it is stated here rather than listed with them and then overruled. */
.mpdg-h.mpdg-col-priority,.mpdg-cell-priority{border-left:1px solid var(--mpdg-line);justify-content:center;}
.mpdg-cell-priority{display:flex;align-items:center;}
.mpdg-sort-icon{gap:4px;}
/* The sort indicator sits beside the icon only while the column is sorted; idle it would hold
   10px of empty space and pull the icon off the badge's axis. */
.mpdg-sort-icon:not(.is-asc):not(.is-desc) .mpdg-sort-ind{display:none;}
.mpdg[data-mpdg-col-status="0"] .mpdg-col-status,
.mpdg[data-mpdg-col-status="0"] .mpdg-cell-status,
.mpdg[data-mpdg-col-priority="0"] .mpdg-col-priority,
.mpdg[data-mpdg-col-priority="0"] .mpdg-cell-priority{display:none;}
.mpdg[data-mpdg-col-due="0"] .mpdg-col-due,
.mpdg[data-mpdg-col-due="0"] .mpdg-cell-due{display:none;}
.mpdg[data-mpdg-col-estimate="0"] .mpdg-col-estimate,
.mpdg[data-mpdg-col-estimate="0"] .mpdg-cell-estimate{display:none;}

.mpdg-status-edit{position:relative;width:var(--mpdg-ctl-w);flex:0 0 var(--mpdg-ctl-w);max-width:100%;}
.mpdg-pri-edit{position:relative;width:auto;flex:0 0 auto;}
.mpdg-status-trigger{
  cursor:pointer;width:100%;gap:7px;justify-content:flex-start;
  border:1px solid color-mix(in srgb,var(--status-accent) 32%,transparent);
}
.mpdg-status-trigger [data-mpdg-edit-text]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.mpdg-pri-trigger{cursor:pointer;width:auto;justify-content:flex-start;gap:5px;}
.mpdg-status-trigger .mpdg-caret{opacity:.55;margin-left:auto;}
.mpdg-pri-trigger .mpdg-caret{opacity:.55;margin-left:1px;}
.mpdg-status-trigger:hover,.mpdg-pri-trigger:hover{filter:brightness(1.12);}
.mpdg-pop{
  position:absolute;z-index:55;top:calc(100% + 6px);left:0;
  display:grid;gap:3px;min-width:160px;padding:6px;
  border:1px solid var(--mpdg-slate-18);border-radius:6px;
  background:linear-gradient(180deg,var(--mpdg-panel-98),var(--mpdg-panel-96));
  box-shadow:0 16px 38px var(--mpdg-sh-42);
}
.mpdg-pop[hidden]{display:none;}
/* Floated row-level panels (escaped from the list's overflow clip) sit above all grid chrome. */
.mpdg-pop[data-mpdg-floated],
.mpdg-menu-panel[data-mpdg-floated]{z-index:1200;}

.mpdg-pop-item{
  display:flex;align-items:center;gap:8px;width:100%;min-height:30px;
  padding:5px 7px;border:0;border-radius:5px;background:transparent;cursor:pointer;
  font:inherit;text-align:left;transition:background .12s ease;
}
.mpdg-pop-item:hover{background:var(--mpdg-fill-07);}

/* Due & planned cell */
.mpdg-cell-due{display:flex;flex-direction:column;justify-content:center;gap:7px;}
.mpdg-due-line{
  display:inline-flex;align-items:center;gap:7px;
  min-height:26px;padding:0 9px;border-radius:6px;
  background:var(--mpdg-deep-32);border:1px solid var(--mpdg-line);
  font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-bold,700);color:var(--mpdg-ink-soft);width:max-content;max-width:100%;
}
.mpdg-due-line.is-empty{color:var(--mpdg-ink-dim);background:transparent;border-color:transparent;padding-left:0;}
.mpdg-due-line.is-empty .mpdg-ico{opacity:.5;}
.mpdg-due-line.is-overdue{color:var(--mpdg-danger);background:var(--mpdg-danger-11);border-color:var(--mpdg-danger-34);}
.mpdg-due-line.is-overdue .mpdg-ico-cal{color:var(--mpdg-danger);}
.mpdg-arrow{color:var(--mpdg-ink-dim);}

/* Estimate cell */
.mpdg-cell-estimate{display:flex;align-items:center;gap:12px;}
.mpdg-est-value{font-size:var(--mp-fs-base,13px);font-weight:var(--mp-fw-bold,700);color:var(--mpdg-ink);white-space:nowrap;min-width:60px;}
/* The rate, where a deliverable draws its progress ring. Quiet: it is the commitment restated for
   scanning, not a second figure competing with the logged total beside it. */
.mpdg-est-ongoing{font-size:var(--mp-fs-xs,11px);font-weight:var(--mp-fw-bold,700);color:var(--mpdg-ink-muted);white-space:nowrap;}
.mpdg-due-ongoing .mpdg-ico-repeat{color:var(--mp-violet-70,rgba(139,92,246,.7));}
.mpdg-ring{
  --mpdg-ring-color:var(--green);
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  width:38px;height:38px;flex:0 0 38px;
}
.mpdg-ring-svg{width:38px;height:38px;transform:rotate(-90deg);overflow:visible;}
.mpdg-ring-track{fill:none;stroke:var(--mpdg-slate-18);stroke-width:3.4;}
.mpdg-ring-fill{
  fill:none;stroke:var(--mpdg-ring-color);stroke-width:3.4;
  stroke-linecap:round;            /* rounded end, matching the transfer ring */
  stroke-dasharray:100;            /* r=15.9155 -> circumference 100 */
  stroke-dashoffset:100;           /* overridden inline / by JS: 100 - pct */
  transition:stroke-dashoffset .3s ease;
}
.mpdg-ring-num{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:var(--mp-fs-2xs,10px);font-weight:var(--mp-fw-heavy,800);color:var(--mpdg-ink-soft);}
/* Estimate progress ring fills with the brand gradient instead of the engine's
   solid --green. The paint server (#mpdg-est-ring-grad) is injected once by
   data-grid.js (SVG strokes need a real gradient element, not a CSS gradient);
   the var(--green) fallback keeps a solid-green ring if that def is absent
   (e.g. JS off), so the server-rendered arc is never invisible. Scoped to the
   estimate cell so other mpdg rings (file-transfers, etc.) keep solid --green. */
.mpdg-cell-estimate .mpdg-ring{--mpdg-ring-color:url(#mpdg-est-ring-grad) var(--green);}

/* Row end / kebab */
.mpdg-cell-end{display:flex;align-items:center;justify-content:flex-end;}
.mpdg-kebab{
  display:inline-flex;align-items:center;justify-content:center;
  width:28px;height:28px;border-radius:6px;
  border:1px solid transparent;background:transparent;color:var(--mpdg-ink-muted);
  font-size:var(--mp-fs-lg,16px);line-height:var(--mp-lh-none,1);cursor:pointer;transition:background .12s ease,color .12s ease;
}
/* HOVER ONLY. The open state used to share this declaration — one selector list, one rule — so
   a reader could not tell an open menu from a pointer resting on the button, and the seven row
   kebabs never passed `trigger` because this rule painted something and nothing looked broken.
   That is the third hand-rolled copy of an open-trigger paint; btn--trigger's own source
   records folding the first two. Open now belongs to the registry. */
.mpdg-kebab:hover{background:var(--mpdg-fill-07);color:var(--mpdg-ink);}
.mpdg-row-menu-panel{right:0;left:auto;min-width:auto;width:max-content;gap:6px;}
.mpdg-row-menu-panel .action-btn{justify-content:flex-start;gap:9px;min-height:36px;}
.mpdg-delete-form{display:contents;}

/* Expanded detail */
.mpdg-detail{grid-column:1 / -1;}
.mpdg-detail[hidden]{display:none;}
.mpdg-detail-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:18px;
  margin-top:12px;padding:16px;
  border:1px solid var(--mpdg-line);border-radius:8px;
  background:var(--mpdg-deep-30);
}

/* Lead-block expander. The default auto-fit gives every block an equal share, which reads
   wrong the moment one of them is prose and the rest are meta: the description wraps to six
   lines inside a third of the row while a single tag chip sits alone in a column of its own.
   This modifier hands the lead block two thirds and stacks the meta blocks in the remaining
   third, so the width the prose gains is width nothing else was using. The rail is a flex
   column rather than grid rows, so it does not care how many blocks a consumer puts in it. */
.mpdg-detail-grid-split{grid-template-columns:minmax(0,2fr) minmax(220px,1fr);}
.mpdg-detail-rail{display:flex;flex-direction:column;gap:18px;min-width:0;}
/* Full-width block in any detail grid — sessions on a task, tasks on a project. */
.mpdg-detail-block-wide{grid-column:1 / -1;}
@media (max-width:900px){
  /* Same breakpoint as the row's flex-card mode: below it the rail is the whole width. */
  .mpdg-detail-grid-split{grid-template-columns:1fr;}
}
.mpdg-detail-label{
  margin:0 0 8px;color:var(--mpdg-ink-muted);font-size:var(--mp-fs-xs,11px);font-weight:var(--mp-fw-heavy,800);
  text-transform:uppercase;letter-spacing:var(--mp-ls-wide,0.04em);
}
.mpdg-detail-desc{margin:0;color:var(--mpdg-ink-item);font-size:var(--mp-fs-base,13px);line-height:var(--mp-lh-relaxed,1.5);}
.mpdg-detail-empty{margin:0;color:var(--mpdg-ink-dim);font-size:var(--mp-fs-base,13px);font-style:italic;}

/* Pending-migration preview inside the row expander. Answers "what is about to run?"
   before the Run migrations button is pressed. */
.mp-migration-preview{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px;}
.mp-migration-preview-item{display:flex;flex-direction:column;gap:4px;min-width:0;}
.mp-migration-preview-head{display:flex;flex-wrap:wrap;align-items:center;gap:6px;min-width:0;}
.mp-migration-preview-file{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:var(--mp-fs-xs,11px);color:var(--mpdg-ink-item);overflow-wrap:anywhere;}
.mp-migration-preview-summary{color:var(--mpdg-ink-item);font-size:var(--mp-fs-sm,12px);line-height:var(--mp-lh-relaxed,1.5);}
.mp-migration-preview-summary.is-empty{font-style:italic;}
.mp-migration-preview-note{margin:12px 0 0;padding-top:10px;border-top:1px solid var(--mpdg-line);color:var(--mpdg-ink-dim);font-size:var(--mp-fs-xs,11px);line-height:var(--mp-lh-relaxed,1.5);}
.mpdg-detail-tags{display:flex;flex-wrap:wrap;gap:6px;}
.mpdg-tag{
  --mpdg-tag:#36d399;
  display:inline-flex;align-items:center;min-height:22px;padding:0 10px;border-radius:999px;
  background:color-mix(in srgb,var(--mpdg-tag) 16%,transparent);
  border:1px solid color-mix(in srgb,var(--mpdg-tag) 36%,transparent);
  color:color-mix(in srgb,var(--mpdg-tag) 60%,var(--mpdg-ink-invert)fff 40%);
  font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-bold,700);
}
.mpdg-followers{display:flex;align-items:center;}
.mpdg-detail-activity{margin:0;color:var(--mpdg-ink-soft);font-size:var(--mp-fs-base,13px);line-height:var(--mp-lh-relaxed,1.5);}
.mpdg-activity-who{color:var(--mpdg-ink-strong);font-weight:var(--mp-fw-heavy,800);}
.mpdg-activity-when{color:var(--mpdg-ink-muted);}
.mpdg-activity-comments{margin-top:6px;color:var(--mpdg-ink-count);font-size:var(--mp-fs-sm,12px);}

/* Group headers */
.mpdg-group-head{
  display:flex;align-items:center;gap:10px;
  padding:13px 16px 9px;
  border-bottom:1px solid var(--mpdg-line-strong);
  background:var(--mpdg-deep-32);
}
.mpdg-rows .mpdg-group-head:first-child{border-top:0;}
.mpdg-group-title{font-size:var(--mp-fs-base,13px);font-weight:var(--mp-fw-heavy,800);color:var(--mpdg-ink);letter-spacing:var(--mp-ls-tight,-0.01em);}
.mpdg-group-count{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:22px;height:20px;padding:0 7px;border-radius:999px;
  background:var(--mpdg-fill-07);border:1px solid var(--mpdg-fill-10);
  color:var(--mpdg-ink-soft);font-size:var(--mp-fs-xs,11px);font-weight:var(--mp-fw-black,900);
}

/* Optional group-header tone. A row may declare data-mpdg-grouptone-<field>; the engine
   echoes it onto the header of the group it opens (see data-grid.js). Lets a derived
   bucket carry the same signal its rows already carry - an "Overdue" due-date header
   reads red exactly like the overdue pill in the row beneath it - without the header
   needing any per-module CSS. Tones: danger / active / muted. */
.mpdg-group-head[data-mpdg-group-tone="danger"]{background:var(--mpdg-danger-11);box-shadow:inset 3px 0 0 var(--mpdg-danger);}
.mpdg-group-head[data-mpdg-group-tone="danger"] .mpdg-group-title{color:var(--mpdg-danger);}
.mpdg-group-head[data-mpdg-group-tone="danger"] .mpdg-group-count{background:var(--mpdg-danger-16);border-color:var(--mpdg-danger-34);color:var(--mpdg-danger);}
.mpdg-group-head[data-mpdg-group-tone="active"]{background:var(--mpdg-hl-08);box-shadow:inset 3px 0 0 var(--mpdg-hl-full);}
.mpdg-group-head[data-mpdg-group-tone="active"] .mpdg-group-title{color:var(--mpdg-ink-hl);}
.mpdg-group-head[data-mpdg-group-tone="active"] .mpdg-group-count{background:var(--mpdg-hl-12);border-color:var(--mpdg-hl-30);color:var(--mpdg-ink-hl);}
.mpdg-group-head[data-mpdg-group-tone="muted"] .mpdg-group-title{color:var(--mpdg-ink-muted);}

/* Nested grouping levels. Depth 0 is the header the grid has always drawn; deeper levels
   step in and step down in weight so the hierarchy reads without a tree line. Indent is a
   single token so the rhythm stays consistent if the depth cap ever moves. */
.mpdg-group-head[data-mpdg-group-depth]{--mpdg-group-indent:0px;padding-left:calc(16px + var(--mpdg-group-indent));}
.mpdg-group-head[data-mpdg-group-depth="1"]{--mpdg-group-indent:18px;padding-top:9px;padding-bottom:7px;background:var(--mpdg-fill-025);}
.mpdg-group-head[data-mpdg-group-depth="2"]{--mpdg-group-indent:36px;padding-top:7px;padding-bottom:6px;background:transparent;}
.mpdg-group-head[data-mpdg-group-depth="1"] .mpdg-group-title{font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-bold,700);color:var(--mpdg-ink-soft);}
.mpdg-group-head[data-mpdg-group-depth="2"] .mpdg-group-title{font-size:var(--mp-fs-xs,11px);font-weight:var(--mp-fw-bold,700);color:var(--mpdg-ink-muted);text-transform:uppercase;letter-spacing:var(--mp-ls-wider,0.08em);}
.mpdg-group-head[data-mpdg-group-depth="1"] .mpdg-group-count,
.mpdg-group-head[data-mpdg-group-depth="2"] .mpdg-group-count{min-width:19px;height:17px;padding:0 6px;font-size:var(--mp-fs-2xs,10px);}
/* A tone belongs to the level that carries it, but an inner level must not shout over the
   outer one, so only depth 0 takes the accent bar. */
.mpdg-group-head[data-mpdg-group-depth="1"][data-mpdg-group-tone],
.mpdg-group-head[data-mpdg-group-depth="2"][data-mpdg-group-tone]{box-shadow:none;}

/* Multi-level group menu: a chosen level shows its ORDINAL in place of the check, because
   with more than one level the order is the meaningful part. At the depth cap the levels
   not already chosen read as unavailable. */
.mpdg-menu-item[data-mpdg-group-ord].is-active::after{
  content:attr(data-mpdg-group-ord);
  margin-left:auto;flex:0 0 auto;
  display:inline-flex;align-items:center;justify-content:center;
  min-width:17px;height:17px;padding:0 5px;border-radius:999px;
  background:var(--mpdg-hl-22);border:1px solid var(--mpdg-hl-32);
  color:var(--mpdg-ink-hl);font-size:var(--mp-fs-2xs,10px);font-weight:var(--mp-fw-black,900);
  -webkit-mask:none;mask:none;
}
.mpdg-menu-item.is-muted{opacity:.42;}
.mpdg-menu-item.is-muted:hover{background:transparent;color:inherit;cursor:default;}

/* Empty / no-results */
.mpdg-empty,.mpdg-noresults{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:5px;min-height:160px;text-align:center;color:var(--mpdg-ink-muted);padding:24px;
}
.mpdg-empty[hidden],.mpdg-noresults[hidden]{display:none;}
.mpdg-empty strong,.mpdg-noresults strong{color:var(--mpdg-ink-strong);font-size:var(--mp-fs-lg,16px);}

/* ===== Board ===== */
.mpdg-board{display:none;gap:14px;overflow-x:auto;padding-bottom:6px;align-items:flex-start;}
.mpdg[data-mpdg-active-view="board"] .mpdg-board{display:flex;}
.mpdg-board-col{
  flex:0 0 300px;min-width:300px;
  border:1px solid var(--mpdg-line-strong);border-radius:14px;
  background:var(--mpdg-surface);display:flex;flex-direction:column;max-height:none;
}
.mpdg-board-col.is-drop{border-color:color-mix(in srgb,var(--status-accent) 55%,transparent);
  box-shadow:0 0 0 2px color-mix(in srgb,var(--status-accent) 22%,transparent) inset;}
.mpdg-board-col-head{
  display:flex;align-items:center;gap:9px;padding:13px 14px;
  border-bottom:1px solid var(--mpdg-line-strong);
}
.mpdg-board-dot{width:9px;height:9px;border-radius:50%;background:var(--status-accent);
  box-shadow:0 0 12px color-mix(in srgb,var(--status-accent) 45%,transparent);}
.mpdg-board-col-title{font-size:var(--mp-fs-base,13px);font-weight:var(--mp-fw-heavy,800);color:var(--mpdg-ink);}
.mpdg-board-col-count{margin-left:auto;}
.mpdg-board-body{display:flex;flex-direction:column;gap:10px;padding:12px;min-height:60px;}
.mpdg-board-empty{color:var(--mpdg-ink-dim);font-size:var(--mp-fs-sm,12px);font-style:italic;text-align:center;padding:14px 0;}
.mpdg-card{
  border:1px solid var(--mpdg-line-strong);border-radius:5px;
  background:linear-gradient(180deg,var(--mpdg-fill-045),var(--mpdg-fill-02));
  padding:12px;cursor:grab;touch-action:none;transition:border-color .14s ease,box-shadow .14s ease,transform .08s ease;
}
.mpdg-card:hover{border-color:var(--mpdg-hl-22);box-shadow:0 10px 24px var(--mpdg-sh-22);}
.mpdg-card-top{display:flex;align-items:flex-start;gap:8px;}
.mpdg-card-title{
  flex:1 1 auto;border:0;background:none;padding:0;cursor:pointer;
  color:var(--mpdg-ink-strong);font:inherit;font-size:var(--mp-fs-base,13px);font-weight:var(--mp-fw-bold,700);text-align:left;line-height:var(--mp-lh-snug,1.25);
}
.mpdg-card-title:hover{text-decoration:underline;text-underline-offset:2px;}
.mpdg-card-sub{margin-top:5px;font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-semibold,600);color:var(--mpdg-ink-muted);}
.mpdg-card-sub .mpdg-company{color:var(--mpdg-accent-soft);}
.mpdg-card-foot{display:flex;align-items:center;gap:8px;margin-top:11px;}
.mpdg-card-foot .mpdg-meta-counts{margin-left:auto;}
.mpdg-card-due{display:inline-flex;align-items:center;gap:5px;font-size:var(--mp-fs-xs,11px);font-weight:var(--mp-fw-bold,700);color:var(--mpdg-ink-count);}
.mpdg-card-due.is-overdue{color:var(--mpdg-danger);min-height:22px;padding:0 7px;border-radius:6px;background:var(--mpdg-danger-11);border:1px solid var(--mpdg-danger-34);}
.mpdg-card-due.is-overdue .mpdg-ico-cal{color:var(--mpdg-danger);}

/* ===== Footer ===== */
.mpdg-foot{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  flex-wrap:wrap;margin-top:14px;padding:4px 2px;
  color:var(--mpdg-ink-muted);font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-bold,700);
}

/* ===== The foot is a DOCK, in every view =====
   It used to be hidden in board and chart, and the count inside it went on being computed for
   them anyway: apply() sets [data-mpdg-count] from visible.length BEFORE the board and chart
   branches return. So a reader on a board could not see how many tasks matched, while the number
   sat finished in a hidden element — the same shape as the no-results message before v1.12.456.

   RESERVED SPACE, NOT AN OVERLAY. Under .mp-page-fill the foot is the last child of the grid root
   and the frame gives it height, which is what it has done in list view since v1.12.453. Nothing
   floats and nothing scrolls underneath, so the board loses the dock's height rather than hiding
   cards behind it.

   WHAT IT CARRIES IS PER VIEW, because the question it answers is. A list paginates, so the pager
   and the rows-per-page control belong to it and stay exactly where they were. A board and a plan
   do not paginate — that is why the whole foot was hidden rather than half of it — so they carry
   the count alone for now, and the controls that have no home yet land here next. */
.mpdg[data-mpdg-active-view="board"] .mpdg-pager,
.mpdg[data-mpdg-active-view="board"] .mpdg-rows-per,
.mpdg[data-mpdg-active-view="chart"] .mpdg-pager,
.mpdg[data-mpdg-active-view="chart"] .mpdg-rows-per{
  display:none;
}

/* THE DOCK IS A SURFACE, not a stray line of text. v1.12.457 gave the foot its reserved space and
   left it looking like the bare list footer it had always been — in board view that was the word
   "26 tasks" in muted grey and nothing else, which is not a dock. It reads as one now: a panel with
   the same skin the menus use, so the bottom of the frame is a place rather than a margin. */
.mpdg.mp-page-fill .mpdg-foot{
  padding:7px 12px;

  /* ONE HEIGHT, EVERY VIEW. Its payload differs — a pager in the list, the grouping switch on the
     board, the count alone on a plan — and each is a different size, so the dock stood 46px tall
     in list and 58px on board and the frame jumped by twelve every time the reader switched. The
     board's cards grew and shrank for a reason that had nothing to do with them.

     Sized to the TALLEST payload rather than to an average: the seg buttons are the tallest thing
     that goes in here, and a floor that any of them can exceed is not a floor. align-items:center
     so a shorter payload sits in the middle of the same box instead of at the top of it. */
  min-height:58px;
  box-sizing:border-box;
  align-items:center;
}

/* THE SWITCH ON THE FRAME'S CENTRE LINE, not merely between two groups. Both flanks claim the same
   share — the count on the left, a pseudo-element standing in for the right, since board view has
   no pager or rows-per-page to balance against. Measured on the mockups: without the equal shares
   the middle drifts with the length of the count text; with them the drift is 0 at 1440, 1280 and
   1100. min-width:0 so a long count shrinks rather than shoving the middle aside. */
.mpdg[data-mpdg-active-view="board"] .mpdg-foot{
  justify-content:center;
}
.mpdg[data-mpdg-active-view="board"] .mpdg-foot-count{
  flex:1 1 0;
  min-width:0;
}
.mpdg[data-mpdg-active-view="board"] .mpdg-foot::after{
  content:'';
  flex:1 1 0;
}

/* The grouping switch. Its buttons are the seg control the lenses already use, so nothing new is
   introduced here — only the shell that holds them. */
.mpdg-dock-group{display:inline-flex;align-items:center;flex:0 0 auto;}
.mpdg:not([data-mpdg-active-view="board"]) .mpdg-dock-group{display:none;}
.mpdg-foot-count [data-mpdg-count]{color:var(--mpdg-ink);font-weight:var(--mp-fw-heavy,800);}
.mpdg-pager{display:flex;align-items:center;gap:6px;}
.mpdg-page-btn{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:30px;height:30px;padding:0 8px;border-radius:6px;
  border:1px solid var(--mpdg-slate-18);background:var(--mpdg-deep-50);
  color:var(--mpdg-ink-soft);font:inherit;font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-heavy,800);cursor:pointer;
  transition:background .12s ease,border-color .12s ease,color .12s ease;
}
.mpdg-page-btn:hover:not(:disabled){background:var(--mpdg-fill-07);color:var(--mpdg-ink-invert);}
.mpdg-page-btn:disabled{opacity:.4;cursor:default;}
.mpdg-page-btn.is-current{background:var(--grad-brand, linear-gradient(135deg,var(--highlight),var(--mpdg-accent)));color:#06111d;border-color:transparent;}
.mpdg-page-ellipsis{color:var(--mpdg-ink-dim);padding:0 2px;}
.mpdg-rows-per{display:inline-flex;align-items:center;gap:8px;white-space:nowrap;}
.mpdg-rows-per-label{font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-bold,700);color:var(--mpdg-ink-muted);white-space:nowrap;}
/* Opens UPWARD, because it lives in the dock at the foot of the frame, and RIGHT-ALIGNED to its
   trigger. The shared panel anchors left:0, which is right for a control on the left of a toolbar
   and wrong for the last control in a bar: the panel ran off the edge to the right of the button
   that opened it. Aligning the panel's right edge to the trigger's keeps it inside the frame
   whatever the widest option is. */
.mpdg-perpage-menu .mpdg-menu-panel{min-width:84px;top:auto;bottom:calc(100% + 8px);left:auto;right:0;}
.mpdg-rows-per label{display:inline-flex;flex-direction:row;align-items:center;gap:8px;font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-bold,700);color:var(--mpdg-ink-muted);white-space:nowrap;}
.mpdg-rows-per .mp-select{width:auto;min-width:78px;}
.mpdg-rows-per select{min-width:78px;}

/* ===== Responsive ===== */
@media (max-width:1100px){
  .mpdg{--mpdg-grid: 40px minmax(220px,1.6fr) minmax(192px,1fr) minmax(150px,.9fr) 110px 44px;}
}
@media (max-width:900px){
  .mpdg-head{display:none;}

  /* Mobile: collapse each grid row into a compact stacked card. The primary
     cell (avatar + title + company·project + counts) owns the full first line;
     the editable chips (status / priority / deadline / estimate) flow together
     on a wrapping line beneath it. The expander and kebab are lifted into the
     left / right padding gutters so neither claims a line of its own. Empty
     due / planned placeholders are dropped here to keep the chip line dense. */
  .mpdg-row{
    display:flex;flex-wrap:wrap;align-items:center;
    position:relative;
    column-gap:8px;row-gap:8px;
    padding:12px 40px 12px 38px;
  }

  .mpdg-cell-grip{position:absolute;left:6px;top:10px;width:26px;}
  .mpdg-cell-end{position:absolute;right:5px;top:7px;justify-content:flex-end;}

  /* THE PRIMARY CELL OWNS THE FIRST LINE, ALWAYS. A grid that draws a lead cell beside it
     (Tasks' assignees, Projects' owner) declares that cell's width in --mpdg-lead-w on the grid
     and puts the lead at order:-1; the basis below then leaves exactly that much room and not a
     pixel more, so the pair fills line one and every chip after it wraps. The earlier per-module
     answer, flex:1 1 0 on the primary cell, let the chips claim the line first and shrank the
     title to nothing (v1.12.527, tasks + projects on a phone). */
  .mpdg-cell-task,.mpdg-cell-primary{flex:1 1 calc(100% - var(--mpdg-lead-w,0px));min-width:0;}

  .mpdg-cell-status,.mpdg-cell-priority,.mpdg-cell-due,.mpdg-cell-estimate{
    flex:0 0 auto;border-left:0;padding-left:0;
  }
  .mpdg-cell-status{flex-wrap:nowrap;--mpdg-ctl-w:auto;}
  /* A shape without a rank renders the priority cell empty, so the desktop tracks stay aligned.
     On the card there are no tracks to keep, and an empty chip slot is only a stray gap. */
  .mpdg-cell-priority:empty{display:none;}
  .mpdg-cell-due{flex-direction:row;flex-wrap:nowrap;align-items:center;column-gap:8px;}

  /* THE RAIL IS THE CARD'S SECOND LINE. One flex row, fixed order, and it never wraps: when the
     chips outgrow the width the rail scrolls sideways, natively on touch, with no scrollbar drawn.
     The pinned cell is sticky at the right edge and paints an opaque fade to its left, so the
     estimate (or a project's budget) stays legible while the chips slide under it. Opaque on
     purpose: the list surface is translucent in both themes, so it is the page ground, not the
     surface, that covers the chips. The pin's order:1 puts it last whatever the DOM order, because
     the desktop tracks put the estimate BEFORE followers and the DOM follows the tracks. */
  .mpdg{--mpdg-rail-pin-bg:var(--bg);}
  .mpdg-rail{
    display:flex;align-items:center;column-gap:6px;
    flex:1 1 100%;min-width:0;
    overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;
    scrollbar-width:none;-webkit-overflow-scrolling:touch;
  }
  .mpdg-rail::-webkit-scrollbar{display:none;}
  .mpdg-rail > .mpdg-cell{flex:0 0 auto;}
  .mpdg-rail-pin{
    order:1;margin-left:auto;
    position:sticky;right:0;z-index:1;
    padding-left:10px;
    background:var(--mpdg-rail-pin-bg);
    box-shadow:-12px 0 12px var(--mpdg-rail-pin-bg);
  }
  .mpdg-status-edit,.mpdg-pri-edit{width:auto;}

  /* drop "No deadline" / "Not scheduled" placeholders on mobile */
  .mpdg-cell-due .mpdg-due-line.is-empty{display:none;}

  /* keep estimate time + ring compact on the chip line */
  .mpdg-cell-estimate{gap:8px;}
  .mpdg-est-value{min-width:0;}
  .mpdg-cell-estimate .mpdg-ring,
  .mpdg-cell-estimate .mpdg-ring-svg{width:30px;height:30px;}
  .mpdg-cell-estimate .mpdg-ring{flex-basis:30px;}
  .mpdg-cell-estimate .mpdg-ring-num{font-size:var(--mp-fs-2xs,10px);}
}
@media (prefers-reduced-motion: reduce){
  .mpdg *{transition:none !important;}
}

/* ===== Inline SVG icon tokens (masked, tint via currentColor) ===== */
.mpdg{
  --mpdg-svg-search:var(--mp-icon-search);
  --mpdg-svg-caret:var(--mp-icon-caret);
  --mpdg-svg-chat:var(--mp-icon-chat);
  --mpdg-svg-cal:var(--mp-icon-cal);
  --mpdg-svg-clock:var(--mp-icon-clock);
  --mpdg-svg-repeat:var(--mp-icon-refresh);
  --mpdg-svg-shield-check:var(--mp-icon-shield-check);
  --mpdg-svg-pencil:var(--mp-icon-pencil);
  --mpdg-svg-link:var(--mp-icon-link);
  --mpdg-svg-check:var(--mp-icon-check);
  --mpdg-svg-group:var(--mp-icon-group);
  --mpdg-svg-arrowdown:var(--mp-icon-arrowdown);
  --mpdg-svg-none:var(--mp-icon-none);
  --mpdg-svg-task:var(--mp-icon-task);
  --mpdg-svg-title:var(--mp-icon-title);
  --mpdg-svg-statusdot:var(--mp-icon-statusdot);
  --mpdg-svg-flag:var(--mp-icon-flag);
  --mpdg-svg-tag:var(--mp-icon-tag);
  --mpdg-svg-person:var(--mp-icon-person);
  --mpdg-svg-building:var(--mp-icon-building);
  --mpdg-svg-folder:var(--mp-icon-folder);
  --mpdg-svg-dot:var(--mp-icon-dot);
  --mpdg-svg-users:var(--mp-icon-users);
  --mpdg-svg-grid:var(--mp-icon-grid);
  --mpdg-svg-industry:var(--mp-icon-industry);
  --mpdg-svg-country:var(--mp-icon-country);
  --mpdg-svg-mail:var(--mp-icon-mail);
  --mpdg-svg-start:var(--mp-icon-start);
  --mpdg-svg-budget:var(--mp-icon-budget);
}

/* ===================== Chart (Gantt) view ===================== */
.mpdg-chart{
  --mpdg-chart-labelw:190px;
  display:none;
  border:1px solid var(--mpdg-line);
  border-radius:14px;
  overflow:hidden;
  background:var(--mpdg-chart-bg);
}
.mpdg[data-mpdg-active-view="chart"] .mpdg-chart{display:block;}
.mpdg[data-mpdg-active-view="chart"] .mpdg-list{display:none;}
.mpdg[data-mpdg-active-view="chart"] .mpdg-board{display:none;}
.mpdg[data-mpdg-active-view="chart"] [data-mpdg-hide-in-chart]{display:none;}

.mpdg-chart-head{
  display:grid;
  grid-template-columns:var(--mpdg-chart-labelw) 1fr;
  border-bottom:1px solid var(--mpdg-line);
  background:var(--mpdg-chart-50);
}
.mpdg-chart-grid{display:flex;min-width:0;}
.mpdg-chart-month{
  box-sizing:border-box;
  padding:9px 10px;
  font-size:var(--mp-fs-xs,11px);font-weight:var(--mp-fw-heavy,800);letter-spacing:var(--mp-ls-wide,0.04em);text-transform:uppercase;
  color:var(--mpdg-ink-muted);
  border-left:1px solid var(--mpdg-line);
  white-space:nowrap;overflow:hidden;
}
.mpdg-chart-month:first-child{border-left:0;}

.mpdg-chart-body{position:relative;}
.mpdg-chart-track{
  display:grid;
  grid-template-columns:var(--mpdg-chart-labelw) 1fr;
  align-items:center;
  min-height:40px;
  border-bottom:1px solid color-mix(in srgb,var(--mpdg-line) 50%,transparent);
}
.mpdg-chart-track:last-child{border-bottom:0;}
.mpdg-chart-track-label{
  padding:6px 14px;
  font-size:var(--mp-fs-base,13px);font-weight:var(--mp-fw-semibold,600);color:var(--mpdg-ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  border-right:1px solid var(--mpdg-line);
}
.mpdg-chart-lane{position:relative;height:100%;min-height:40px;}
.mpdg-chart-bar{
  position:absolute;top:50%;transform:translateY(-50%);
  height:18px;min-width:6px;border:0;padding:0;cursor:pointer;
  border-radius:5px;
  background:color-mix(in srgb,var(--mpdg-bar) 72%,transparent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--mpdg-bar) 92%,transparent);
  transition:background .12s ease,filter .12s ease;
}
.mpdg-chart-bar:hover{background:var(--mpdg-bar);filter:brightness(1.05);}
.mpdg-chart-bar:focus-visible{outline:2px solid var(--mpdg-bar);outline-offset:2px;}
.mpdg-chart-milestone{
  position:absolute;top:50%;
  width:13px;height:13px;
  transform:translate(-50%,-50%) rotate(45deg);
  border:0;padding:0;cursor:pointer;border-radius:3px;
  background:var(--mpdg-bar);
  box-shadow:0 0 0 1px var(--mpdg-chart-60);
}
.mpdg-chart-milestone:hover{filter:brightness(1.12);}
.mpdg-chart-milestone:focus-visible{outline:2px solid var(--mpdg-bar);outline-offset:3px;}
.mpdg-chart-nodate{
  position:absolute;top:50%;left:10px;transform:translateY(-50%);
  font-size:var(--mp-fs-xs,11px);font-style:italic;color:var(--mpdg-ink-nodate);
}

.mpdg-chart-overlay{
  position:absolute;left:var(--mpdg-chart-labelw);right:0;top:0;bottom:0;
  pointer-events:none;
}
.mpdg-chart-today{
  position:absolute;top:0;bottom:0;width:2px;
  background:color-mix(in srgb,var(--green,var(--highlight)) 75%,transparent);
}
.mpdg-chart-today::before{
  content:'Today';
  position:absolute;top:2px;left:5px;
  font-size:var(--mp-fs-2xs,10px);font-weight:var(--mp-fw-heavy,800);letter-spacing:var(--mp-ls-wide,0.04em);text-transform:uppercase;
  color:color-mix(in srgb,var(--green,var(--highlight)) 85%,var(--mpdg-ink-soft));
  white-space:nowrap;
}
/* ---- waterfall: project group header + task lanes ---- */
.mpdg-chart-grouptrack{
  background:var(--mpdg-chart-42);
  border-bottom:1px solid var(--mpdg-line);
}
.mpdg-chart-group-label{
  font-weight:var(--mp-fw-heavy,800);color:var(--mpdg-ink-bright);
}
.mpdg-chart-itemtrack{min-height:32px;}
.mpdg-chart-itemtrack .mpdg-chart-lane{min-height:32px;}
.mpdg-chart-item-label{
  position:relative;
  padding-left:30px;
  font-size:var(--mp-fs-sm,12px);font-weight:var(--mp-fw-semibold,600);color:var(--mpdg-ink-muted);
}
/* small elbow connector so tasks read as children of the project above */
.mpdg-chart-item-label::before{
  content:'';
  position:absolute;left:16px;top:-2px;bottom:50%;width:8px;
  border-left:1.5px solid var(--mpdg-line);
  border-bottom:1.5px solid var(--mpdg-line);
  border-bottom-left-radius:4px;
}
/* faint roll-up bar on the project header row */
.mpdg-chart-summary{
  height:9px;border-radius:4px;cursor:pointer;
  background:color-mix(in srgb,var(--mpdg-bar) 22%,transparent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--mpdg-bar) 55%,transparent);
}
.mpdg-chart-summary:hover{background:color-mix(in srgb,var(--mpdg-bar) 34%,transparent);}

.mpdg-chart-empty{padding:34px 20px;text-align:center;color:var(--mpdg-ink-dim);font-style:italic;}

@media (max-width:720px){
  .mpdg-chart{--mpdg-chart-labelw:120px;}
  .mpdg-chart-track-label{font-size:var(--mp-fs-sm,12px);padding:6px 8px;}
}

/* 1.0.51 — board card pointer-drag (replaces native HTML5 DnD): the picked-up card lifts
   free of the column (position:fixed in JS), floats under the cursor and leans toward the
   drag direction (rAF-driven transform), mirroring the calendar session blocks. The lift
   shadow/opacity fade in here; transform is JS-driven so the lean tracks the cursor 1:1
   (no transform transition). A placeholder holds the source gap during the drag. */
.mpdg-card.mpdg-card-floating{
  z-index:9999;
  box-shadow:0 20px 44px -12px var(--mpdg-sh-62), 0 6px 16px var(--mpdg-sh-40);
  opacity:.97;
  cursor:grabbing;
  transition:box-shadow .15s ease, opacity .15s ease;
}
.mpdg-card-placeholder{
  border:1px dashed var(--mpdg-line-strong, var(--mpdg-ghost-18));
  border-radius:5px;
  background:var(--mpdg-fill-025);
  pointer-events:none;
}
body.mpdg-board-dragging{ cursor:grabbing; }
body.mpdg-board-dragging, body.mpdg-board-dragging *{ -webkit-user-select:none; user-select:none; }
@media (prefers-reduced-motion:reduce){
  .mpdg-card.mpdg-card-floating{ transform:none!important; }
}


/* The grid's toolbar search is redundant once the app bar's control has adopted this
   grid (navigation-shell / window.MPSearch): one search box per page, in the chrome.
   The input stays in the DOM — it is still where the search term lives for persist(),
   saved views and serializeState(); it is just no longer the thing you type into. */
.mpdg-search.is-adopted{display:none;}
/* On a page whose app bar carries the platform search (the view DECLARED it, so the
   layout stamps body[data-mp-appbar-search]), the grid's own toolbar search is redundant
   and must be born hidden — not shown then hidden by .is-adopted after JS. Suppressing it
   at first paint kills the toolbar->appbar search swap flash. The input stays in the DOM as
   the grid's state home (serializeState reads it); it is simply never the visible box. */
body[data-mp-appbar-search] .mpdg-search{display:none;}


/* ===== Card-grid data set ====================================================
   A list-view treatment for visual registries and libraries. It remains a real
   mpdg list (search/filter/group/sort/state all stay canonical), but the rows are
   presented as compact cards. This is deliberately distinct from Board/Kanban:
   there are no board columns, drag buckets or board view controls. */
.mpdg-card-grid .mpdg-list{border:0;border-radius:0;background:transparent;overflow:visible;}
.mpdg-card-grid .mpdg-head{display:none;}
.mpdg-card-grid .mpdg-rows{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(110px,1fr));
  gap:12px;
}
.mpdg-card-grid .mpdg-row{
  appearance:none;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:10px;
  min-width:0;
  min-height:108px;
  margin:0;
  padding:14px 10px;
  border:1px solid var(--mpdg-line-strong);
  border-radius:8px;
  background:linear-gradient(180deg,var(--mpdg-fill-045),var(--mpdg-fill-025));
  color:var(--mpdg-ink-strong);
  font:inherit;
  text-align:center;
  cursor:pointer;
  box-shadow:none;
  transition:border-color .14s ease,background .14s ease,box-shadow .14s ease,transform .08s ease;
}
.mpdg-card-grid .mpdg-row:hover{
  border-color:var(--mpdg-hl-22);
  background:linear-gradient(180deg,var(--mpdg-fill-06),var(--mpdg-fill-032));
  box-shadow:0 10px 24px var(--mpdg-sh-22);
}
.mpdg-card-grid .mpdg-row:active{transform:translateY(1px);}
.mpdg-card-grid .mpdg-row:focus-visible{outline:2px solid var(--mpdg-accent);outline-offset:2px;}
.mpdg-card-grid .mpdg-row[hidden]{display:none;}
.mpdg-card-grid .mpdg-grid-card-visual{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:32px;
  height:32px;
  flex:0 0 32px;
  color:var(--mpdg-ink-strong);
}
.mpdg-card-grid .mpdg-grid-card-visual .mp-icon{width:32px;height:32px;}
.mpdg-card-grid .mpdg-grid-card-title{
  display:block;
  max-width:100%;
  color:inherit;
  font-size:var(--mp-fs-sm,12px);
  font-weight:var(--mp-fw-bold,700);
  line-height:var(--mp-lh-snug,1.25);
  overflow-wrap:anywhere;
}
.mpdg-card-grid .mpdg-group-head{
  grid-column:1 / -1;
  border:1px solid var(--mpdg-line-strong);
  border-radius:8px;
  padding:10px 12px;
}
.mpdg-card-grid .mpdg-foot{margin-top:12px;}
@media(max-width:640px){
  .mpdg-card-grid .mpdg-rows{grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:10px;}
  .mpdg-card-grid .mpdg-row{min-height:98px;padding:12px 8px;}
}

/* ===== Admin catalog grid ====================================================
   Shared management-catalog layout used by Appearance components and Modules.
   It is intentionally theme-owned: feature modules provide only mpdg markup and
   canonical Buttons / Settings Sections controls, never local table CSS. */
.mpdg-admin-catalog{
  --mpdg-grid:44px minmax(260px,1.7fr) minmax(120px,.55fr) minmax(110px,.5fr) minmax(110px,.5fr) minmax(150px,.72fr) minmax(300px,1.35fr);
}
.mpdg-admin-catalog .mpdg-cell:not(.mpdg-cell-grip):not(.mpdg-cell-primary){display:flex;align-items:center;min-width:0;}
.mpdg-admin-catalog .mpdg-cell-catalog-actions{align-items:center;}
.mpdg-admin-catalog .mpdg-cell-catalog-actions .action-group{gap:6px;}
.mpdg-admin-catalog .mpdg-primary-sub:empty{display:none;}
.mpdg-admin-catalog .mp-settings-ico{margin:0;}
.mpdg-admin-catalog .mpdg-detail-grid{grid-template-columns:minmax(0,1fr) minmax(220px,.45fr);}
.mpdg-admin-catalog .mpdg-detail-desc{white-space:pre-line;}
@media(max-width:900px){
  .mpdg-admin-catalog .mpdg-cell:not(.mpdg-cell-grip):not(.mpdg-cell-primary){flex:0 1 auto;border-left:0;padding-left:0;}
  .mpdg-admin-catalog .mpdg-cell-catalog-actions{flex:1 1 100%;}
  .mpdg-admin-catalog .mpdg-cell-catalog-actions .action-group{width:100%;}
  .mpdg-admin-catalog .mpdg-detail-grid{grid-template-columns:1fr;}
}

/* Activity entries in the row expander. One line per changed field: what it was, what it became,
   and when. The field name carries the weight because it is what you scan for; the values are the
   detail you read once you have found the line. */
.mpdg-activity-field{color:var(--mpdg-ink-strong);font-weight:var(--mp-fw-heavy,800);}
.mpdg-activity-from{opacity:.72;}
.mpdg-activity-arrow{opacity:.5;margin:0 .15rem;}
.mpdg-activity-to{color:var(--mpdg-ink-strong);}
.mpdg-detail-activity + .mpdg-detail-activity{margin-top:.15rem;}

/* ══ THE INBOX LENS: an intake row is not a task row ═════════════════════════
   A row awaiting triage has no company, no relation, no deadline, no schedule and no estimate —
   not because they are missing but because they are the DECISION the row is waiting for. Rendered
   in the ordinary six-column shape it reads "No company / No project or contact / No deadline /
   Not scheduled / 0m / 0%": six negations, which looks like a broken task rather than an item in a
   queue.

   SCOPED TO .is-inbox-scope, the marker the lens ALREADY sets on the grid root — not to
   data-mpdg-col-*. Those attributes are the reader's own column
   preferences and they are persisted per person in mp_grid_state (PRIMARY KEY user_id, grid_key).
   A lens that wrote them would silently overwrite what somebody chose and never give it back;
   this marker is set on entering the lens and dropped on leaving, so the preferences underneath
   are untouched throughout. */
/* !important, and it is the only honest option here rather than a shortcut. A CONSUMER declares
   these cells at id specificity — views/tasks.php carries `#tasks-overview .mpdg-cell-followers
   {display:flex}` — and no selector this shared component can write beats an id whose name it does
   not know. Without it the Followers cell stayed a grid item: four tracks, five items, and the
   action pair wrapped onto a second row at column one. The TRACKS need no rule here: the engine
   composes --mpdg-grid from the headers that are displayed, so a header this block withholds
   takes its track with it. */
.mpdg.is-inbox-scope .mpdg-col-status,
.mpdg.is-inbox-scope .mpdg-cell-status,
.mpdg.is-inbox-scope .mpdg-col-priority,
.mpdg.is-inbox-scope .mpdg-cell-priority,
.mpdg.is-inbox-scope .mpdg-col-due,
.mpdg.is-inbox-scope .mpdg-cell-due,
.mpdg.is-inbox-scope .mpdg-col-estimate,
.mpdg.is-inbox-scope .mpdg-cell-estimate,
.mpdg.is-inbox-scope .mpdg-col-followers,
.mpdg.is-inbox-scope .mpdg-cell-followers,
.mpdg.is-inbox-scope .mpdg-cell-grip .mpdg-expand,
.mpdg.is-inbox-scope .mpdg-cell-task .mpdg-company,
.mpdg.is-inbox-scope .mpdg-cell-task .mpdg-project{
  display:none !important;
}

/* The chevron's CELL keeps its width. Hiding the button and collapsing the cell would shift every
   row left on entering the lens and back on leaving, which reads as the list flinching rather than
   as a column being withheld. */

/* One action, and it only exists here. Outside the lens the kebab is right — a menu offers a
   choice — and inside it there is no choice to offer. */
/* THE BUTTON EXISTS ON EVERY ROW AND BELONGS TO ONE LENS. `.mpdg-triage-btn{display:none}` is a
   single class and so is `.btn{display:inline-flex}` in actions-badges — equal specificity, and
   that component loads later (order 14 against this one's 12), so the button won and appeared on
   every row of every lens. Qualified with .mpdg here so the rule outranks .btn by structure rather
   than by load order, which is not something this file gets to decide. */
.mpdg .mpdg-triage-btn{display:none;}
.mpdg.is-inbox-scope .mpdg-triage-btn{display:inline-flex;}
.mpdg.is-inbox-scope .mpdg-cell-end{gap:6px;}

/* Where it came from. Shown only in the lens, in the space the company and relation chips vacate. */
.mpdg-intake{display:none;}
.mpdg.is-inbox-scope .mpdg-intake{display:inline-flex;align-items:center;gap:5px;}
.mpdg-intake-avatar{width:18px;height:18px;flex:0 0 18px;}

/* ══ THE INBOX LENS, second pass: the row is only as wide as what it holds ═══
   Withholding the four columns left their TRACKS standing — --mpdg-grid is a fixed eight-track
   list, and display:none on a cell does not remove the column it sits in. So the row read as a
   title, a rule, a long empty stretch and then the action: the columns were gone and the space
   they occupied was not.

   REDECLARING THE TRACK LIST IS THE ESTABLISHED ANSWER, not a new mechanism: companies.css already
   ships one --mpdg-grid per hidden-column combination. Four tracks remain — grip, assignee, task,
   end — and the task track takes the slack, which is what lets the title stop truncating.

   THE LIST ITSELF LIVES WITH THE GRID IT OVERRIDES, in views/tasks.php beside the base
   declaration — not here. --mpdg-grid is declared per grid across this platform and never
   centrally, and the first cut of this ignored that: a rule here at `.mpdg.is-inbox-scope`
   (two classes) lost outright to `#tasks-overview` (an id), so the four cells were withheld and
   their tracks stayed exactly where they were. Caught by measuring the computed track count
   rather than by reading this file. */

/* THE TITLE IS THE ROW. Nothing competes for the track any more, so there is nothing to truncate
   for — and a triage decision made from "Movu events - item p..." is a decision made from a guess.
   Reverses the ellipsis the shared rule applies rather than widening it: wrapping is what a full
   title needs when it outgrows even this. */
.mpdg.is-inbox-scope .mpdg-title{
  overflow:visible;text-overflow:clip;white-space:normal;
}

/* THE RULES SEPARATED COLUMNS THAT ARE NO LONGER THERE. .mpdg-divide draws a left border for the
   Followers and end cells; with the four middle tracks withheld it drew a line between the title
   and the one button beside it, which is not a boundary anybody needs to see. */
.mpdg.is-inbox-scope .mpdg-cell.mpdg-divide,
.mpdg.is-inbox-scope .mpdg-h.mpdg-divide{border-left:0;}

/* THE SMALL AFFORDANCES ARE FOR A ROW YOU ARE WORKING WITH, and an intake row is one you have not
   decided about yet. The hover pencil and the copy-link both address a task with no relations to
   link to; the file and comment counts describe a conversation that has not started. The one thing
   to do is the Triage button, and every glyph beside it is a competing target. Nothing is removed
   from the row — all of it comes back with the columns the moment the lens is left. */
.mpdg.is-inbox-scope .mpdg-title-edit,
.mpdg.is-inbox-scope .mpdg-title-link,
.mpdg.is-inbox-scope .mpdg-task-counts{display:none !important;}

/* The action pair, hard against the end of the row. `auto` above sizes that track to the button and
   the kebab rather than to a share of what is left, so they sit where the eye finishes reading. */
.mpdg.is-inbox-scope .mpdg-cell-end{justify-content:flex-end;}
