/*
 * Assets Film & TV: the Module Dashboard. REFERENCE STYLESHEET (DOCKERTST_WALK_2026-09-18.md, T6).
 *
 * This file is the design. It is written to be copied into the application unchanged, and the page
 * `dashboard-mock.html` beside it is drawn by this file alone.
 *
 *   1. EVERYTHING IS UNDER `.ftd` AND EVERY CLASS STARTS `ftd-`. Nothing here can touch the rest of the
 *      site, and no Bootstrap or theme class is relied on (`.card`, `.row`, `.badge`, `.btn` are NOT used,
 *      on purpose: they are what bent the last mock out of shape).
 *   2. THE COLOURS ARE THE ENTITY'S. The four `--ftd-*` theme variables below are the fall-back only; the
 *      component sets them on the `.ftd` element from `window.COLOUR_PLATE` (primary_colour,
 *      accent_colour, soft_primary_colour, soft_accent_colour), which is where the site's own
 *      `.af-primary` / `.af-accent` classes get theirs. No colour of the theme is written anywhere else
 *      in this file.
 *   3. IT RESPONDS TO THE ROOM IT IS GIVEN, NOT TO THE SCREEN. `.ftd` and every card body are size containers, and the
 *      breakpoints are container queries: the page has a 250px sidebar that collapses to 70px, so a screen width says
 *      little about the width the dashboard really has. Nothing is ever clipped or overlapped: a band's buttons drop to
 *      a second line, panels go from three columns to two to one, the settings wrap. (A browser without container
 *      queries gets the wrapping, which needs none, and the single-column layout below 1100px of screen.)
 *   4. SIZES ARE FIXED WHERE THEY WERE AGREED: a band is 74px (58px small), a list row 30px, a button
 *      32px, a settings control 26px, the card body's padding 18px 20px 20px. Equal heights are the point.
 */

.ftd {
    /* theme: overwritten inline by the component from window.COLOUR_PLATE. These four are the fall-back for an entity
       with no colours of its own (app.blade.php:217 emits COLOUR_PLATE only when the entity has a primary colour): the
       site's own default dark sidebar and default primary, not any one customer's. */
    --ftd-primary: #2a3042;
    --ftd-accent: #556ee6;
    --ftd-primary-soft: rgba(42, 48, 66, .25);
    --ftd-accent-soft: rgba(85, 110, 230, .25);

    /* neutrals: the same for every entity */
    --ftd-ink: #2b3440;
    --ftd-text: #495057;
    --ftd-muted: #8a93a0;
    --ftd-line: #e6e9ee;
    --ftd-hairline: #f0f2f5;
    --ftd-surface: #ffffff;
    --ftd-inset: #f4f6f9;
    --ftd-control-line: #d3d9e2;
    --ftd-good-bg: #d8ecd9;
    --ftd-good-ink: #2f6b33;
    --ftd-bad-bg: #f8dcdc;
    --ftd-bad-ink: #9b2c2c;

    /* derived: a tint of the accent for hovers. The first line is the fall-back for a browser without color-mix. */
    --ftd-hover: #f0f5fc;
    --ftd-hover: color-mix(in srgb, var(--ftd-accent) 9%, #ffffff);

    font-size: 13px;
    line-height: 1.5;
    color: var(--ftd-text);
    container-type: inline-size;
    container-name: ftd;
}
.ftd, .ftd *, .ftd *::before, .ftd *::after { box-sizing: border-box; }
.ftd a, .ftd a:hover, .ftd a:focus { color: inherit; text-decoration: none; }
/* ================================================================ THE GUARD (the margin reset, and what else the site leaks in)
   HISTORY, kept because each step was right about the thing it was fixing.
   Written plainly as `.ftd h4, .ftd p, ...` the reset was (0,1,1), and it silently beat every single-class margin on
   those tags: a rule such as `.ftd-thing { margin: 0 0 12px }` was cancelled with nothing to show for it, 31 times
   over during the design walk (find_cancelled_margins.py, re-run 2026-09-20: `TOTAL cancelled margin rules: 31`;
   an earlier draft of this comment said 63, which the script has never printed). On 2026-09-20 it was rewritten
   with :where() at (0,0,0)
   so a single class wins, which is what a reader expects. That fixed the walk, and broke the site.
   THE FAULT, found the same day by T6a's two checkers (throughput/notes/T6a.md Q1) and measured here: this sheet is
   shipped into the application as public/css/film-tv-dashboard.css and loaded LAST of six link tags in
   layouts/app.blade.php, but LOAD ORDER DOES NOT DECIDE BETWEEN DIFFERENT SPECIFICITIES. The site's app.css carries
   Bootstrap's reboot as bare element selectors at (0,0,1) (h1..h6 margin-bottom .5rem, dl 1rem, dd .5rem, p 1rem,
   dt weight, label, ul, ol, hr, figure, img, svg, sub, sup, code, and the form controls' font: inherit), and (0,0,1)
   beats (0,0,0) whatever loads last. So inside .ftd on the REAL page the :where() reset did not apply at all: the
   mocks never showed it, because a mock loads no site stylesheet.
   THE MEND, 2026-09-20: the ancestor is written PLAINLY and the tags stay inside :where().
   `.ftd :where(h4)` is (0,1,0). Specificity compares a, then b, then c, so (0,1,0) beats the reboot's (0,0,1)
   outright -- and a single class is ALSO (0,1,0), so it still wins on source order, because every class rule in this
   sheet stands below this block. The reason for :where() therefore survives exactly: the guard loses to one class.
   Measured on all 22 signed-off pages at 1920, 1366 and 390 (check_under_site_css.py).
   ONLY THE PROPERTIES MEASURED TO LEAK ARE LISTED. A property a .ftd- class sets must never be added here without
   re-measuring: at the same (0,1,0) the class still wins on order, but the moment one is raised, it does not. */
.ftd :where(h1, h2, h3, h4, h5, h6, p, dl, dt, dd, ul, ol, li, figure, blockquote, pre, label, fieldset, legend) { margin: 0; }
.ftd :where(h1, h2, h3, h4, h5, h6) { font-size: inherit; font-weight: inherit; line-height: inherit; color: inherit; }
.ftd :where(dt, label) { font-weight: inherit; }
.ftd :where(label) { display: inline; }       /* the reboot makes every label inline-block */
.ftd :where(ul, ol) { padding-left: 0; }
/* NO list-style guard, on purpose (corrected 2026-09-21). `4-styles.css:41` strips the marker of every ul and li on
   the site, and the first pass answered with `.ftd :where(ul, ol, li) { list-style: revert }`. Measured 2026-09-21 in
   the engine under the site's five sheets, that rule put BULLETS back on every Bootstrap control drawn inside .ftd:
   a top-bar dropdown's items, a tab strip, a pagination all computed `disc` at display: list-item, because
   `.dropdown-menu`, `.nav` and `.pagination` strip the marker at (0,1,0), the guard was also (0,1,0) and the sheet
   loads later; and the redesigned top bar carries .ftd on every page of every customer. Measured the same day: not
   ONE list item in any of the 24 signed-off pages shows a marker, so the rule protected nothing the design draws.
   A later page that wants a marked list says so on its own class (`list-style: disc`), which beats the site's
   (0,0,1) rule without any help from here. Dropping only `li` from the guard is NOT enough: the site sets the li
   itself to none, so an inherited value never reaches it, and the ul kept `disc` on every site control. */
.ftd :where(legend) { width: auto; max-width: none; font-size: inherit; }
.ftd :where(hr) { height: auto; overflow: hidden; }
.ftd :where(img, svg) { vertical-align: baseline; }
/* the UA's own values, measured 2026-09-20 with the reboot switched off in the engine, NOT `inherit`:
   small is 83.33% of its parent and sup/sub carry vertical-align super/sub at the same 83.33%. The reboot
   moves each of them (small to 80%, sup/sub to 75% on the baseline with position: relative). */
.ftd :where(small) { font-size: 83.3333%; }
.ftd :where(sub, sup) { position: static; line-height: inherit; font-size: 83.3333%; }
.ftd :where(sup) { vertical-align: super; top: auto; }
.ftd :where(sub) { vertical-align: sub; bottom: auto; }
/* `code`, `kbd`, `samp` and `pre` are NOT guarded, and this is a DELIBERATE, NARROW exception.
   The reboot does reach them (font-family to SFMono, font-size to 87.5%, colour to #e83e8c), so under the
   site's CSS they would draw in pink monospace. But measured 2026-09-20, <code> appears in the signed-off
   pages FOUR times, all four inside table-mock's own explanatory prose ABOUT the component ("the new_line
   slot", "the custom_header prop"), never in the design itself; the other six mocks and r1 to r15 have none.
   No redesigned page draws one. Guarding them buys nothing on any real page and costs real damage on the
   mock: the guard at (0,1,0) beat table-mock's own shell rule `code { font-size: 12px }` (0,0,1) and grew
   all four, breaking a signed-off render by 5,188 pixels at 1920 and again at 1366.
   IF A LATER PAGE DRAWS A <code>, ADD IT TO THE GUARD THEN and re-measure that page. */
/* `b` and `strong` are DELIBERATELY NOT reset. Measured the same way: the reboot's `font-weight: bolder`
   and the UA's own `bold` BOTH compute to 700 from this sheet's weights, so it is not a leak -- and
   `font-weight: inherit` here broke 20 signed-off renders, because 268 <b> elements in the mocks carry no
   class and rely on that bold. Where the sheet does want another weight it says so on an ancestor class
   (`.ftd-count b`, `.ftd-post__line b` and eleven more, all (0,1,1), all beating this guard). */
/* the reboot gives every form control `font: inherit`, which pulls the leaked heading line-height down into buttons
   and inputs. The mocks draw them at the UA's own values, so the UA's own values are restated. NOT `inherit`:
   measured 2026-09-20, `inherit` here breaks 731 elements of the mocks, which rely on the UA default. */
.ftd :where(button, input, optgroup, select, textarea) { line-height: normal; font-size: 13.3333px; font-family: Arial; }
.ftd button, .ftd select { font-family: inherit; }   /* declared after the guard on purpose: this one IS the design */

/* THE ONE LEAK A LOW GUARD CANNOT REACH: `.af-dashboard h4` in the site's myStyle.css, at (0,1,1).
   `af-dashboard` is put on the page's .row by views/assetflow/items/View.vue:320 whenever the item type is
   customers or service_providers, which IS the module dashboard, and it sets `text-transform: uppercase`,
   `font-size: 18px`, `font-weight: 600`, `margin-top: 1.4rem !important` and a `:before` that injects a
   Font Awesome glyph in front of the text. It is the most damaging leak of the five sheets because it changes what
   the heading SAYS, not only its box, and at (0,1,1) it beats the guard above.
   It reaches `h4` AND NOTHING ELSE: `.af-dashboard h4` is the only heading rule in the five sheets (checked
   2026-09-20, `grep "af-dashboard[^,{]*h[1-6]"` matches myStyle.css:4984 and its :before at 4991, nothing more).
   So a class on an h1, h2, h3, h5 or h6 is not at risk from it, and only the h4 cases are listed below.
   `.ftd .ftd-x` is (0,2,0), which beats (0,1,1). A blanket `.ftd h4` would also beat it -- and would then beat the
   single-class rules this sheet is built on, killing 466 elements' worth of deliberate uppercase in table-mock alone.
   WHAT IS LISTED, and why it is not just today's three. Measured in the engine 2026-09-20, one probe per class on a
   real h4 under all five sheets: EVERY heading class the sheet declares takes the full takeover when it is drawn on
   an h4 -- 22.4px of top margin, the glyph, and for most of them 18px/600/uppercase as well. Only the classes named
   here escape. In today's mocks just three of them sit on an h4 (ftd-panel__head 48, ftd-summary__title 12,
   ftd-table__title 3) and the rest happen to be drawn on div/span/h1, which is why no render ever caught it. But
   nothing in this sheet or in the markup FIXES a class to a tag, and the builder rewriting these pages in Vue
   chooses the tag freely, so the cover names every heading-like class the sheet declares.
   THE COVER IS IN TWO HALVES, and the split is not cosmetic. `text-transform: none` and `margin-top: 0` are the
   right answer only for a class that wants neither. Measured sheet-alone 2026-09-20, a single blanket rule over all
   33 classes changed NINE of them against their signed-off value (ftd-card__head, ftd-sec__title, ftd-list__head,
   ftd-table__head, ftd-sstep__title, ftd-hstep__title and ftd-doneline__title all want uppercase; ftd-head__title
   wants margin-top 1px and ftd-asset__name 14px) and broke dashboard-mock at 390 by 324,040 pixels. So the blanket
   covers only the classes that genuinely want none/0, and the nine restate their own value at the same (0,2,0).
   A LATER PAGE THAT DRAWS A NEW HEADING CLASS MUST STILL ADD IT HERE, to whichever half matches what it wants.
   A bare h4 inside .ftd cannot be defended, because defending it would mean `.ftd h4`, the blanket this avoids. */
.ftd .ftd-panel__head, .ftd .ftd-summary__title, .ftd .ftd-table__title,
.ftd .ftd-modal__title, .ftd .ftd-band__title, .ftd .ftd-post__line,
.ftd .ftd-empty__title, .ftd .ftd-filehead, .ftd .ftd-toast__title,
.ftd .ftd-files__name, .ftd .ftd-row__name, .ftd .ftd-summary__name,
.ftd .ftd-ptile__name, .ftd .ftd-optrow__name, .ftd .ftd-choice__name,
.ftd .ftd-swatch__name, .ftd .ftd-thumbrow__name, .ftd .ftd-drop__head,
.ftd .ftd-sec__head, .ftd .ftd-modal__head, .ftd .ftd-postform__head,
.ftd .ftd-toast__line, .ftd .ftd-empty__line,
.ftd .ftd-table__head--single { margin-top: 0 !important; text-transform: none; }
/* the nine that want their own value: same (0,2,0), each restating what the sheet alone draws.
   .ftd-table__head--single is NOT among them: it is the modifier that turns the uppercase column head
   back to sentence case, so it belongs in the blanket above. */
.ftd .ftd-card__head, .ftd .ftd-sec__title, .ftd .ftd-list__head,
.ftd .ftd-table__head,
.ftd .ftd-sstep__title, .ftd .ftd-hstep__title,
.ftd .ftd-doneline__title { margin-top: 0 !important; text-transform: uppercase; }
/* ftd-head__title and ftd-asset__name take the text-transform and the glyph cover but NOT a restated
   margin-top, and that is deliberate. A margin restated with !important is a CONSTANT, and neither of
   these has a constant top margin: .ftd-asset__name is 14px normally and 0 inside
   `@container ftdcard (min-width: 640px)` (the latest-asset block), and pinning it to 14px broke dashboard-mock at
   1366 by 18,766 pixels. They are drawn on h1/div today, which `.af-dashboard h4` cannot reach, so the
   margin half costs nothing today; if either is ever put on an h4 it gains 22.4px of top margin and
   that must be answered by changing the class's own rule, not by an !important here. */
.ftd .ftd-head__title, .ftd .ftd-asset__name { text-transform: none; }
/* THE SECOND LEAK THAT NEEDS !important, and the widest one in the five sheets. `2-myStyle.css:5644`, inside
   `@media (min-width: 768px) and (max-width: 1199px)`, is
   `body[data-sidebar="dark"] h1, ...h2, ...h3, ...h4, ...h5, ...h6 { color: #605d5d !important }`.
   `data-sidebar="dark"` is the DEFAULT, not an exotic state: mixins/layouts.mixin.js:6 sets `sidebar: "dark"` and
   :161 writes the attribute onto <body>. So on any tablet-width viewport EVERY heading inside .ftd goes grey, on
   every page. Measured 2026-09-20: at 1024 and at 900, 12 of 12 heading classes changed colour -- .ftd-panel__head
   rgb(43,52,64) -> rgb(96,93,93), .ftd-summary__title rgb(102,112,125) -> rgb(96,93,93), and so on; at 1366 and
   1920, 0 of 12, because the media query does not apply there.
   `!important` is the only answer to `!important`, as it is for [type=search] below. Each class restates ITS OWN
   colour rather than `inherit`, because several of these are deliberately not the body ink. This is invisible to
   every render the mocks produce (they load no site stylesheet) and to check_under_site_css.py as it stands (its
   ancestry starts at #app and never sets the <body> attribute, and its three widths -- 1920, 1366, 390 -- all miss
   the 768 to 1199 band). It therefore has to be remembered: do not take these !important markers off.
   WHICH CLASSES ARE RESTATED. A class may only be named here if the value restated is the one it really
   draws in the context named. The five below have ONE unconditional colour, checked against every other
   rule that names them. */
.ftd .ftd-panel__head, .ftd .ftd-table__title, .ftd .ftd-head__title,
.ftd .ftd-asset__name, .ftd .ftd-band__title { color: var(--ftd-ink) !important; }
/* THE FOUR THAT WERE LEFT, now restated PER CONTEXT (2026-09-21). The first pass left these out because
   each has more than one rightful colour, and a single flat value with !important beat the sheet's own
   later rule and broke table-mock at 1920 by 8,262 pixels. That was the right diagnosis and the wrong
   conclusion: what broke it was restating one LITERAL over every context, not the !important. Restating
   the VARIABLE the rule itself uses, once per context, cannot fight the sheet, because it says the same
   thing the sheet already said.
   Every context was enumerated by grepping each class's own colour rules, 2026-09-21:
     .ftd-card__head   one rule (the card head), --ftd-head-ink, which a theme may move and which this
                       restates by name so it still moves.
     .ftd-sec__title   two: --ftd-ink from the later of its two base rules, and --ftd-head-ink inside
                       .ftd-sec--boxed. The boxed selector below MIRRORS that rule exactly, so the pair
                       resolves the same way the sheet alone resolves it.
     .ftd-list__head   one rule, #fff on the primary band.
     .ftd-table__head  one rule, #fff on the primary band. (.ftd-table__head--single needs none: it is
                       always drawn with .ftd-table__head and inherits the white correctly.)
   THE COST, and it is accepted with open eyes: a later single class can no longer recolour these four,
   because (0,2,0) + !important beats it. That is already true of .ftd-sec__title through its own
   (0,2,0) boxed rule, and of the five above. The guard's promise is that THE GUARD loses to one class;
   a leak cover answering !important is the documented exception, not a breach of it.
   KNOWN RESIDUAL, unchanged by this and written down so it is not mistaken for mended: 24 further
   heading classes still take the grey at tablet widths when they are drawn on a heading tag, and a
   separate, width-independent leak (`.af-dashboard h4`, (0,1,1)) still moves font-size, weight and
   line-height on the classes the cover above does not restate those for. Both need their own decision. */
.ftd .ftd-card__head { color: var(--ftd-head-ink) !important; }
.ftd .ftd-sec__title { color: var(--ftd-ink) !important; }
.ftd .ftd-sec--boxed > .ftd-sec__head .ftd-sec__title,
.ftd .ftd-sec--boxed > .ftd-sec__toggle .ftd-sec__title { color: var(--ftd-head-ink) !important; }
.ftd .ftd-list__head, .ftd .ftd-table__head { color: #fff !important; }
/* and .ftd-summary__title, which the first pass missed because no check could see it: it is drawn on an
   h4 twelve times across r1 and r4, so it took the tablet grey on every real page. Found 2026-09-21 once
   check_under_site_css.py was given the 1024 width and the <body> attribute.
   IT MUST RESTATE --ftd-label, NOT --ftd-ink. The class has two rules -- var(--ftd-muted) where it is
   declared in F8, and var(--ftd-label) in the later form-label group, which wins unconditionally. This
   cover sits ABOVE both, so restating anything else here would beat the later rule and change the
   signed-off colour. Restating what actually wins changes nothing and answers the leak. */
.ftd .ftd-summary__title { color: var(--ftd-label) !important; }
/* the site's myStyle.css puts `border-radius: .25rem !important` on every [type=search], at (0,1,0). !important
   outranks every normal declaration at any weight, so this is the one place the sheet must answer in kind.
   The value restated is 0, which is what the sheet alone draws and what Tristian signed off: a search input in this
   design has `border: 0` and sits INSIDE the rounded .ftd-search pill, so a corner on the input itself is a corner
   on an invisible border. The pill's own 6px is untouched, declared on .ftd-search in the corners list below.
   Measured 2026-09-20: 6 search inputs across the signed-off pages (an earlier draft said 18). Apart from this and
   the two heading leaks above, nothing else in the five sheets needs !important. */
.ftd :where(input[type="search"]) { border-radius: 0 !important; }
/* ...except where the sheet gives the input itself a box, as .ftd-input does. (0,2,0) + !important beats the site. */
.ftd input.ftd-input[type="search"] { border-radius: var(--ftd-corner) !important; }
/* `content: none` already stops the glyph painting -- a pseudo-element with no content does not exist -- but the
   font-family is restated too so the check script can prove the cover clean rather than allow an exception. */
.ftd .ftd-panel__head::before, .ftd .ftd-summary__title::before, .ftd .ftd-table__title::before,
.ftd .ftd-head__title::before, .ftd .ftd-card__head::before, .ftd .ftd-asset__name::before,
.ftd .ftd-modal__title::before, .ftd .ftd-sec__title::before, .ftd .ftd-band__title::before,
.ftd .ftd-post__line::before, .ftd .ftd-list__head::before, .ftd .ftd-table__head::before,
.ftd .ftd-empty__title::before, .ftd .ftd-filehead::before, .ftd .ftd-toast__title::before,
.ftd .ftd-files__name::before, .ftd .ftd-row__name::before, .ftd .ftd-summary__name::before,
.ftd .ftd-sstep__title::before, .ftd .ftd-hstep__title::before, .ftd .ftd-doneline__title::before,
.ftd .ftd-ptile__name::before, .ftd .ftd-optrow__name::before, .ftd .ftd-choice__name::before,
.ftd .ftd-swatch__name::before, .ftd .ftd-thumbrow__name::before, .ftd .ftd-drop__head::before,
.ftd .ftd-sec__head::before, .ftd .ftd-modal__head::before, .ftd .ftd-postform__head::before,
.ftd .ftd-toast__line::before, .ftd .ftd-empty__line::before, .ftd .ftd-table__head--single::before { content: none; padding-right: 0; font-family: inherit; display: inline; }
/* ================================================================ THE GUARD ends */

/* ---------------------------------------------------------------- the three rows */
.ftd-grid { display: grid; gap: 20px; margin-bottom: 20px; }
.ftd-grid--assets { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.ftd-grid--thirds { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ftd-grid--halves { grid-template-columns: repeat(2, minmax(0, 1fr)); }   /* an Object Dashboard's two lists of files */
.ftd-grid--files  { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.4fr) minmax(0, 1.4fr); }
/* under 1300px of room the latest asset drops beneath the ASSETS card (and lies on its side, see .ftd-asset);
   under 900px every row is one column */
@container ftd (max-width: 1300px) {
    .ftd-grid--assets { grid-template-columns: minmax(0, 1fr); }
    .ftd-grid--files { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .ftd-grid--files > .ftd-card:first-child { grid-column: 1 / -1; }
}
@container ftd (max-width: 900px) {
    .ftd-grid--thirds, .ftd-grid--files, .ftd-grid--halves { grid-template-columns: minmax(0, 1fr); }
    .ftd-grid--files > .ftd-card:first-child { grid-column: auto; }
}
@container ftd (max-width: 560px) {
    .ftd-grid { gap: 14px; margin-bottom: 14px; }
    .ftd-card__head { padding: 10px 14px; }
    .ftd-card__body { padding: 14px; }
}
@supports not (container-type: inline-size) {
    @media (max-width: 1100px) {
        .ftd-grid--assets, .ftd-grid--thirds, .ftd-grid--files { grid-template-columns: minmax(0, 1fr); }
    }
}

/* ---------------------------------------------------------------- a card */
.ftd-card { min-width: 0; display: flex; flex-direction: column; overflow: hidden; background: var(--ftd-surface); border: 1px solid var(--ftd-line); border-radius: 8px; box-shadow: 0 5px 30px rgba(0, 28, 56, .06); }
/* the head is drawn at the weight Tristian chose, Balanced (Log #355): the inset grey with the title in ink, framed on
   the line colour, which is the ONE grammar a card's head, a boxed section's head and a modal's head now share. The
   variables are declared in the section FOUNDATION below; the primary band it carried before was the `strong` setting,
   which the walk compared and he did not choose. ONE definition: nothing below restates this rule. */
.ftd-card__head { display: flex; justify-content: space-between; align-items: center; padding: 12px 20px; background: var(--ftd-head-bg); color: var(--ftd-head-ink); border-bottom: 1px solid var(--ftd-head-line); font-size: 14px; font-weight: 500; text-transform: uppercase; }
.ftd .ftd-card__head a { font-size: 12px; font-weight: 400; text-transform: none; color: inherit; opacity: .8; }
/* EVERY HOVER UNDERLINE IN THIS SHEET CARRIES !important, and this is the reason, written once here for all
   eleven of them (card head, band title, panel head, asset name, post text, ftd-link, sec link, fact value,
   crumb, table cell, bulk bar). The site's app.css ends with `a { text-decoration: none !important }`.
   !important outranks every normal declaration at any specificity, so without !important of its own not one
   of these underlines appears on a real page -- and a mock never shows it, because a mock loads no site
   stylesheet. Proved in the engine 2026-09-20: the rule as written computed `none`; only `underline
   !important` survived. This is invisible to a pixel diff of the mocks, which render no hover state, so it
   cannot be caught by the renders: it has to be remembered. Do not take these !important markers off. */
.ftd .ftd-card__head a:hover { opacity: 1; text-decoration: underline !important; color: inherit; }
.ftd-card__body { flex: 1; min-width: 0; padding: 18px 20px 20px; container-type: inline-size; container-name: ftdcard; }

/* ---------------------------------------------------------------- a band: a card's main link */
.ftd-band { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; min-height: 74px; padding: 14px 18px; border-radius: 8px; border: 1px solid var(--ftd-accent-soft); background: linear-gradient(90deg, var(--ftd-accent-soft), rgba(255, 255, 255, 0) 85%); }
.ftd-band + .ftd-band { margin-top: 10px; }
.ftd-band + .ftd-panel, .ftd-band + .ftd-group { margin-top: 16px; }
.ftd-band__icon { flex: none; display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: 50%; background: var(--ftd-primary); color: #fff; font-size: 1.4rem; }
.ftd-band__count { font-size: 26px; font-weight: 600; line-height: 1; color: var(--ftd-primary); }
.ftd-band__text { flex: 1 1 130px; min-width: 110px; }
.ftd-band__title { display: block; font-size: 14px; font-weight: 500; color: var(--ftd-ink); }
.ftd .ftd-band__title a:hover { text-decoration: underline !important; }
.ftd-band__sub { display: block; font-size: 12px; color: var(--ftd-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* the band's buttons: one group, pushed right; when the name would be squeezed the group drops to a second line */
.ftd-band__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-left: auto; }
.ftd-band__links { display: flex; flex-wrap: wrap; gap: 8px; }
.ftd-band--sm { min-height: 58px; padding: 10px 14px; gap: 12px; }
.ftd-band--sm .ftd-band__icon { width: 36px; height: 36px; font-size: 1.15rem; }
.ftd-band--sm .ftd-band__count { min-width: 34px; font-size: 20px; text-align: right; }
.ftd-band--sm .ftd-band__title { font-size: 13px; }

/* the filled button: Open, Open asset */
.ftd .ftd-btn { flex: none; display: inline-flex; align-items: center; gap: 4px; height: 32px; padding: 0 14px; border-radius: var(--ftd-corner); background: var(--ftd-primary); color: #fff; font-size: 12px; font-weight: 500; transition: filter .15s, background .15s; }   /* the corner is the one central 6px (Log #355); it was the 16px pill, which the walk compared and he did not choose */
.ftd .ftd-btn:hover { background: var(--ftd-accent); color: #fff; }
/* the outlined button: a list's other tabs, on its band (More..., Add, Import, Export) */
.ftd .ftd-pill { display: inline-flex; align-items: center; gap: 5px; height: 32px; padding: 0 13px; border: 1px solid var(--ftd-accent-soft); border-radius: var(--ftd-corner); background: var(--ftd-surface); color: var(--ftd-primary); font-size: 12px; font-weight: 500; transition: all .15s; }   /* the one central corner, as .ftd-btn */
.ftd-pill i { font-size: 1.05rem; }
.ftd .ftd-pill:hover { background: var(--ftd-primary); border-color: var(--ftd-primary); color: #fff; }
.ftd-band--sm .ftd-pill { height: 28px; padding: 0 11px; }
/* a small band in a narrow card: Open and the pills keep their icon and lose their word (the title is the link, and
   each keeps its title attribute), so the name still has room on one line */
@container ftdcard (max-width: 400px) {
    .ftd-band--sm .ftd-btn__label, .ftd-band--sm .ftd-pill__label { display: none; }
    .ftd-band--sm .ftd-btn, .ftd-band--sm .ftd-pill { width: 30px; height: 30px; padding: 0; justify-content: center; border-radius: 50%; }
    .ftd-band--sm .ftd-band__text { flex-basis: 80px; min-width: 70px; }
    .ftd-band--sm .ftd-band__sub { display: none; }   /* the name may take two lines here; the band stays its height */
}

/* ---------------------------------------------------------------- a captioned group of panels */
.ftd-group { display: flex; align-items: center; gap: 10px; margin: 20px 0 10px; font-size: 11px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--ftd-primary); }
.ftd-group::after { content: ""; flex: 1; height: 1px; background: var(--ftd-line); }
.ftd-panels { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
/* four across, for a card the full width of the page (an Object Dashboard's filters); it steps down to three and then follows .ftd-panels */
.ftd-panels--four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@container ftdcard (max-width: 1100px) { .ftd-panels--four { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@container ftdcard (max-width: 760px) { .ftd-panels { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@container ftdcard (max-width: 480px) {
    .ftd-panels, .ftd-panel__rows--two { grid-template-columns: minmax(0, 1fr); }
    .ftd-panel__rows--two > .ftd-row:nth-last-child(2):nth-child(odd) { border-bottom: 1px solid var(--ftd-hairline); }
}

/* ---------------------------------------------------------------- a panel: a headed list of links with counts */
.ftd-panel { overflow: hidden; border: 1px solid var(--ftd-line); border-radius: 7px; background: var(--ftd-surface); }
.ftd .ftd-panel__head { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--ftd-inset); border-bottom: 1px solid var(--ftd-line); font-size: 12px; font-weight: 600; color: var(--ftd-ink); }
.ftd-panel__head i { font-size: 1.05rem; color: var(--ftd-accent); }
.ftd-panel__head span { flex: 1; }
/* a panel whose rows open the list in a view of their own says so in its head */
.ftd-panel__view { flex: none; display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; border-radius: 9px; background: var(--ftd-accent-soft); color: var(--ftd-primary); font-size: 10.5px; font-weight: 500; font-style: normal; }
.ftd .ftd-panel__head .ftd-panel__view i { font-size: .9rem; color: var(--ftd-primary); }
.ftd .ftd-panel__head a { font-size: 11.5px; font-weight: 400; color: var(--ftd-accent); }
.ftd .ftd-panel__head a:hover { text-decoration: underline !important; color: var(--ftd-accent); }
.ftd-panel__rows { padding: 4px 12px 6px; }
.ftd-panel__rows--two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 22px; }
.ftd .ftd-row { display: flex; align-items: center; gap: 8px; height: 30px; margin: 0 -6px; padding: 0 6px; border-radius: 5px; border-bottom: 1px solid var(--ftd-hairline); transition: background .15s; }
.ftd-panel__rows > .ftd-row:last-child, .ftd-panel__rows--two > .ftd-row:nth-last-child(2):nth-child(odd) { border-bottom: 0; }
.ftd-row__name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ftd-row__count { font-weight: 600; color: var(--ftd-ink); }
.ftd .ftd-row:hover { background: var(--ftd-hover); color: var(--ftd-primary); }
.ftd-row--zero .ftd-row__name, .ftd-row--zero .ftd-row__count { color: var(--ftd-muted); font-weight: 400; }
.ftd-card__body > .ftd-panel + .ftd-panel { margin-top: 14px; }   /* panels stacked in a card, not panels side by side in a grid */
.ftd-panel__empty { padding: 10px 0 8px; color: var(--ftd-muted); font-size: 12px; }

/* ---------------------------------------------------------------- the list's toolbar: ONE bar, and it is the LIST'S OWN
   The pattern is the assets list's existing row: what is shown on the left ("Items 1-10 of 424"), the controls on the right. The same
   bar heads the object's Dashboard tab, where the left reads the total ("424 assets") and a choice saves and redraws nothing, and the
   list tab, where it saves and redraws the list. No box, no title, no sentence: the page is white and the bar is a row on it.
   On the Module Dashboard it sits inside a card, under the band, so there it is an inset strip; the controls are the same. */
.ftd-settings { position: relative; container-type: inline-size; container-name: ftdsettings; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin: 0 0 18px; }   /* `position: relative` anchors the Saved flash; `ftdsettings` lets the flash answer THE BAR'S own width, not the page's: see ONE ROW AT 1366 below */
.ftd-settings__count { margin-right: auto; font-size: 14px; color: var(--ftd-muted); }
.ftd-settings__count b { font-weight: 500; color: var(--ftd-ink); }
.ftd-settings__saved { font-size: 12px; font-weight: 500; color: var(--ftd-good-ink); opacity: 0; transition: opacity .3s; }
.ftd-settings__saved.is-shown { opacity: 1; }
.ftd-settings__controls { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 8px 12px; margin-left: auto; }
.ftd-settings--card { margin: 16px 0 0; padding: 10px 14px; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-inset); }
.ftd-settings--card .ftd-settings__count { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--ftd-ink); }
.ftd-settings--card .ftd-settings__count i { font-size: 1.05rem; color: var(--ftd-accent); }
.ftd-control { min-width: 0; display: flex; align-items: center; gap: 6px; }
.ftd-control > span { font-size: 10px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd-seg { max-width: 100%; display: inline-flex; flex-wrap: wrap; overflow: hidden; border: 1px solid var(--ftd-control-line); border-radius: 6px; background: var(--ftd-surface); }
.ftd .ftd-seg button { display: inline-flex; align-items: center; height: 26px; margin: 0; padding: 0 7px; border: 0; border-right: 1px solid var(--ftd-control-line); border-radius: 0; background: var(--ftd-surface); color: var(--ftd-text); font-size: 11.5px; line-height: 1; cursor: pointer; }
.ftd .ftd-seg button:last-child { border-right: 0; }
.ftd .ftd-seg button:hover { background: var(--ftd-hover); }
.ftd .ftd-seg button.is-on { background: var(--ftd-primary); color: #fff; box-shadow: inset 0 2px 4px rgba(0, 0, 0, .35); }
.ftd-seg button i { font-size: 1rem; }
.ftd .ftd-settings select { height: 28px; max-width: 160px; padding: 0 4px; border: 1px solid var(--ftd-control-line); border-radius: 6px; background: var(--ftd-surface); color: var(--ftd-text); font-size: 11.5px; }
/* ONE ROW AT 1366 (2026-09-21). Measured here on the pages themselves: at 1366 the mock's shell leaves a
   list toolbar a 1030px content box, and the LIST tab's bar ("Items 1-10 of 424") asked 1030.8px of it on
   r3 and 1033.8px on r12, so it wrapped to a second row and stood 57px instead of 28px. The Dashboard
   tab's bar ("424 assets") asked 990.5px and fitted, but only once the Saved flash was out of the way.
   The component adds no CSS of its own and its markup is the mock's, so the bar has to fit here or not
   at all.
   Three things buy the room back, each the smallest change that reaches what it reaches. The Saved flash
   held 53px of the row plus a 16px gap for a message that is invisible until a choice is made, so it
   comes out of flow and sits just under the bar, in the 18px bottom margin the bar already has -- exactly
   18px of it, which is why that rule carries no margin-top and why it lives in its own `ftdsettings` query
   below rather than here. The segmented control's side padding drops from 7px to 5px, worth 36px over the
   eighteen buttons of the widest bar. And the gap between control groups drops from 12px to 10px, worth
   6px over the four groups of the list bar, which is what turns a 0.8px and a 3.8px shortfall into 5.2px
   and 2.2px of slack.
   THE SHORTFALL IS SMALL AND THE SLACK IS SMALL. A longer count ("Items 100-110 of 1,424") or one more
   control would wrap the list bar again. If that happens the answer is a shorter control set, not another
   two pixels: the honest levers are spent.
   ONE NOTE ON WEIGHT, because it is the one place this pass does not keep the usual promise. The button
   rule is `.ftd .ftd-settings .ftd-seg button`, (0,3,1). It has to beat the base rule it overrules,
   `.ftd .ftd-seg button` at (0,2,1), AND stay inside the toolbar so a segmented control elsewhere keeps
   its 7px, and (0,3,1) is the least that does both. A single plain class on such a button therefore does
   NOT win its padding -- measured. That is true of the base rule already, so the rules this pass adds
   change nothing about it; it is written down here so the next reader does not have to find it twice.
   The other four rules this pass adds are all (0,1,0) and a single class beats every one of them.
   WHY A CONTAINER QUERY AND NOT A PLAIN RULE. Measured against container width, the bar re-packs at
   400-420, 500 and 900-920 as well, so a global change moves the narrow renders: taking the flash out of
   flow unprompted moved dashboard-mock at 390 by 622,068 pixels. The floor of 1000 sits above the 980 at
   which the bar wraps for its own reasons and below the 1030 the shell gives at 1366, so nothing under
   1000px of `.ftd` moves at all. `.ftd` is already a named size container (`container-name: ftd`, the
   block at the head of this sheet), so this is the sheet's own mechanism, not a new one.
   WHICH BARS THESE TWO LEVERS REACH, stated plainly because `@container ftd` names the PAGE root and not
   the bar: at 1000px of page and up they reach EVERY `.ftd-settings` on the page, including a narrow one
   inside a card. That is safe for these two and only these two, because both only ever shrink a control
   -- a 298px card strip measured 5px of button padding and a 10px control gap with no overlap and no
   overflow. The third lever, which takes the flash OUT OF FLOW, is not safe on that query and is written
   against `ftdsettings` instead; the block below says why.
   The guard's `.ftd :where(button, ...)` is (0,1,0) and sets no padding, so it and the button rule above
   do not meet.
   WHAT THIS DOES NOT FIX, and it is recorded rather than bodged. The card form (`.ftd-settings--card`
   inside the Module Dashboard's ASSETS card) is still two rows. Measured here after the mend: it asks
   1024px and is given 971px at 1920 and 958px at 1366, so it is short by 53px and 66px. The cause is the
   card's width, not the breakpoint -- `.ftd-grid--assets` gives ASSETS 2fr of 3, so the card is about the
   same width at 1920 as at 1366, and the form is two rows at BOTH. This mend does not change that at
   either, and it should not: closing 53px would mean cutting the card's own strip and body padding,
   which is a signed-off surface. The three ways out -- accept two rows in a settings strip, give the card
   form a shorter control set (its Show carries five options against the plain bar's three, its Sort ten),
   or widen the ASSETS card at this breakpoint -- are decisions for the walk, not for this sheet. */
@container ftd (min-width: 1000px) {
    .ftd .ftd-settings .ftd-seg button { padding: 0 5px; }
    .ftd-settings__controls { column-gap: 10px; }
}
/* THE FLASH ANSWERS THE BAR, NOT THE PAGE (2026-09-21, corrected the same day). Taking the flash out of
   flow was first written into the 1000px `ftd` query above, with the other two levers. That is wrong for
   this one declaration: `@container ftd` names the page root, so a 298px settings strip inside a card got
   an absolutely positioned flash whenever the PAGE was wide. Measured: bar 298px on a 1400px page, the
   flash landed 19px INTO the table below it, green on the dark head band. The two padding levers above
   may stay on the page's query because they only ever shrink a control; a rule that takes a box out of
   flow must answer the box that has to hold it.
   900px is the floor because the plain list bar is 1030px at 1366 and the card strip is about 300px:
   nothing between the two exists in the signed-off set, so the floor sits in the gap. Measured after the
   change: plain bar 1030px, flash absolute, 0px of overlap with the table; card strip 298px on a 1400px
   page, flash back in flow, 16px of clearance.
   NO `margin-top`. The flash is 18px of line-height and the bar's bottom margin is 18px, so the 2px the
   first draft added needed 20px of an 18px gap and put the flash 2px into the table head -- measured,
   and invisible in every render only because the flash is `opacity: 0` until a choice is made. At 0 it
   fits exactly. */
@container ftdsettings (min-width: 900px) {
    .ftd-settings__saved { position: absolute; left: 0; top: 100%; }
}

/* ---------------------------------------------------------------- latest asset */
.ftd-gallery { min-height: 230px; border-radius: 6px; overflow: hidden; background: var(--ftd-inset); }
.ftd-gallery__placeholder { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; height: 230px; padding: 0 20px; text-align: center; font-size: 12px; color: var(--ftd-muted); background: linear-gradient(135deg, var(--ftd-inset), var(--ftd-line)); }
.ftd-gallery__placeholder i { font-size: 3rem; opacity: .6; }
@container ftdcard (min-width: 640px) {
    .ftd-asset { display: grid; grid-template-columns: minmax(0, 340px) minmax(0, 1fr); gap: 0 24px; align-items: start; }
    .ftd-asset__info > .ftd-asset__name { margin-top: 0; }
}
.ftd-asset__name { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-top: 14px; font-size: 16px; font-weight: 500; }
/* T6: "a long name, one line with ellipsis, never a wrap". The row is the name and its status badge. The name is the
   flex item that gives way: min-width 0 lets it shrink below its text (a flex item's floor is auto, which is its
   content, so without this it pushes the badge out instead of truncating), and the badge keeps `flex: none` on
   .ftd-badge, so it is never squeezed. A short name is unchanged: it does not fill the row, so nothing truncates.
   The small band's name is allowed to wrap (T6's own exception) and is a different class, untouched. */
.ftd .ftd-asset__name a { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--ftd-primary); }
.ftd .ftd-asset__name a:hover { color: var(--ftd-accent); text-decoration: underline !important; }
.ftd-asset__desc { margin: 2px 0 12px; color: var(--ftd-muted); }
.ftd .ftd-asset__facts { display: grid; grid-template-columns: auto 1fr; gap: 5px 14px; }
.ftd .ftd-asset__facts dt { color: var(--ftd-muted); font-weight: 400; }
.ftd .ftd-asset__facts dd { text-align: right; color: var(--ftd-ink); }
.ftd-asset__foot { display: flex; flex-wrap: wrap; gap: 8px; justify-content: space-between; align-items: center; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--ftd-line); }
.ftd-muted { color: var(--ftd-muted); font-size: 12px; }
.ftd-badge { flex: none; padding: 2px 9px; border-radius: 4px; background: var(--ftd-good-bg); color: var(--ftd-good-ink); font-size: 11px; font-weight: 500; }
.ftd-badge--grey { background: #e9ecef; color: #5c6670; }
.ftd-badge--red { background: var(--ftd-bad-bg); color: var(--ftd-bad-ink); }

/* ---------------------------------------------------------------- headline numbers: a strip of linked counts under a band (an Object Dashboard's statuses) */
.ftd-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-top: 16px; }
@container ftdcard (max-width: 560px) { .ftd-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.ftd .ftd-stat { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: 10px 14px; border: 1px solid var(--ftd-line); border-radius: 7px; background: var(--ftd-surface); transition: all .15s; }
.ftd .ftd-stat:hover { background: var(--ftd-hover); border-color: var(--ftd-accent); }
.ftd-stat__n { font-size: 20px; font-weight: 600; line-height: 1.15; color: var(--ftd-primary); }
.ftd-stat__label { font-size: 12px; color: var(--ftd-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* a file's page: the band is the file and what can be done with it; the counts beneath it may be five across and may carry good or bad news */
.ftd-stats--five { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@container ftd (max-width: 900px) { .ftd-stats--five { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.ftd .ftd-stat--good { border-color: #cfe6d1; background: #f1f8f2; }
.ftd-stat--good .ftd-stat__n { color: var(--ftd-good-ink); }
.ftd .ftd-stat--bad { border-color: #f1cccc; background: #fdf1f1; }
.ftd-stat--bad .ftd-stat__n { color: var(--ftd-bad-ink); }
/* A COUNT OF NONE IS QUIET ON A TILE TOO, 2026-09-21. The panel row already has this device
   (.ftd-row--zero above): a status with nothing in it should not shout the theme colour at the same
   weight as a status with 212 in it. The tile had nothing for it, because .ftd-stat__n is --ftd-primary
   unconditionally and .ftd-stat carried only --good and --bad. This is OPT-IN and no signed-off page sets
   it, so nothing moves today; it is here for the pages still to come.
   It echoes the row's device exactly, muted colour and weight 400, and it is (0,2,0), the same as
   --good and --bad, so a tile that is BOTH zero and good takes whichever the markup means last in this
   file: --good and --bad are written above, so --zero wins, which is right (a zero is not good news).
   The tile stays a link: this names only the number inside it, never the anchor, so the hover on
   .ftd-stat is untouched. */
.ftd-stat--zero .ftd-stat__n { color: var(--ftd-muted); font-weight: 400; }
.ftd-filehead .ftd-band__title { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 16px; }
.ftd-filehead .ftd-band__sub { white-space: normal; }
.ftd-filehead__chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.ftd-filehead__about { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--ftd-line); }
.ftd .ftd-pill--danger { border-color: #f1cccc; color: var(--ftd-bad-ink); }
.ftd .ftd-pill--danger:hover { background: var(--ftd-bad-ink); border-color: var(--ftd-bad-ink); color: #fff; }
.ftd .ftd-btn i { font-size: 1.05rem; }

/* what happened, newest first: a file's Activity tab */
/* a run of chips: an export's columns */
.ftd-chips { display: flex; flex-wrap: wrap; gap: 6px; }

/* ---------------------------------------------------------------- a row with a thumbnail: the latest assets, the latest dispositions */
.ftd .ftd-thumbrow { display: flex; align-items: center; gap: 10px; min-height: 54px; margin: 0 -6px; padding: 6px; border-radius: 6px; border-bottom: 1px solid var(--ftd-hairline); transition: background .15s; }
.ftd .ftd-thumbrow:last-child { border-bottom: 0; }
.ftd .ftd-thumbrow:hover { background: var(--ftd-hover); }
.ftd-thumbrow__img { flex: none; display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; overflow: hidden; border-radius: 6px; background: var(--ftd-inset); color: var(--ftd-muted); font-size: 1.2rem; }
.ftd-thumbrow__img img { width: 100%; height: 100%; object-fit: cover; }
.ftd-thumbrow__text { flex: 1; min-width: 0; }
.ftd-thumbrow__name, .ftd-thumbrow__sub { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ftd-thumbrow__name { font-weight: 500; color: var(--ftd-ink); }
.ftd-thumbrow__sub { font-size: 12px; color: var(--ftd-muted); }

/* ---------------------------------------------------------------- a list of records: one row a record, the column names above (the Files list)
   One line per record where the old list spent two: the four big boxes (status, items, successful, failed) become a badge and a short count. */
.ftd-list { overflow: hidden; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); box-shadow: 0 5px 30px rgba(0, 28, 56, .06); }
.ftd-list__head, .ftd .ftd-listrow { display: grid; grid-template-columns: minmax(0, 2.4fr) 76px minmax(0, .7fr) minmax(0, .9fr) 104px 222px 132px; align-items: center; gap: 0 16px; padding: 0 18px; }
.ftd-list__head { height: 40px; background: var(--ftd-primary); color: #fff; font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }
.ftd .ftd-listrow { min-height: 62px; border-bottom: 1px solid var(--ftd-hairline); transition: background .15s; }
.ftd .ftd-listrow:last-child { border-bottom: 0; }
.ftd .ftd-listrow:hover { background: var(--ftd-hover); }
.ftd-listrow__main { display: flex; align-items: center; gap: 12px; min-width: 0; }
.ftd-listrow__meta { display: contents; }
.ftd-listrow__meta > * { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ftd-listrow__counts { font-size: 12px; color: var(--ftd-muted); }
.ftd-listrow__counts b { font-weight: 600; color: var(--ftd-ink); }
.ftd-listrow__counts .is-bad { color: var(--ftd-bad-ink); font-weight: 600; }
.ftd-listrow__meta > .ftd-counts { overflow: visible; }
/* a record's counts, the same three in the same three places on every row: items, successful, failed */
.ftd-counts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
.ftd-count { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 40px; border: 1px solid var(--ftd-line); border-radius: 6px; background: var(--ftd-surface); line-height: 1.15; }
.ftd a.ftd-count:hover { border-color: var(--ftd-accent); background: var(--ftd-hover); }
.ftd-count b { font-size: 14px; font-weight: 600; color: var(--ftd-ink); }
.ftd-count span { font-size: 9.5px; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd-count--good { border-color: #cfe6d1; background: #f1f8f2; }
.ftd-count--good b { color: var(--ftd-good-ink); }
.ftd-count--bad { border-color: #f1cccc; background: #fdf1f1; }
.ftd-count--bad b { color: var(--ftd-bad-ink); }
/* an import that was uploaded and never run has nothing to count: it says so in words, in the counts' place */
.ftd-counts--words { display: block; font-size: 12px; color: var(--ftd-muted); }
.ftd-listrow__actions { display: flex; justify-content: flex-end; gap: 6px; }
.ftd .ftd-chip { display: inline-block; padding: 1px 9px; border-radius: var(--ftd-corner); background: var(--ftd-accent-soft); color: var(--ftd-primary); font-size: 11px; font-weight: 500; }   /* the one central corner */
.ftd-chip--export { background: var(--ftd-primary-soft); }
/* what one row of an import did to one record: made it, or found it already there */
.ftd .ftd-chip--made { background: var(--ftd-good-bg); color: var(--ftd-good-ink); }
.ftd .ftd-chip--matched { background: #e9ecef; color: #5c6670; }
.ftd-listrow__meta > .ftd-listrow__note { overflow: visible; white-space: normal; font-size: 12px; color: var(--ftd-muted); }
.ftd-listrow__note .ftd-chips { display: inline-flex; margin-right: 6px; vertical-align: middle; }
.ftd .ftd-listrow__note a.ftd-chip:hover { filter: brightness(.94); }
/* the legend is written on a <p>, so the FIRST reset -- the plain `.ftd p` one, (0,1,1) -- cancelled this -8px pull,
   and the approved render has it flush. The guard at the head of this sheet is (0,1,0) and loses to a single class,
   so the pull would appear for the first time and move a page Tristian has approved. It is taken off on a <p>, which
   is the only tag it is used on, so the approved render stands; the declaration is left for any other tag, as the
   sheet wrote it. (This has been true of the guard since it was :where(), and stays true now that it is .ftd :where(p):
   both lose to one class. The 2026-09-20 mend changed which SITE rules the guard beats, not which of this sheet's.) */
.ftd-legend { margin: -8px 0 12px; font-size: 12px; color: var(--ftd-muted); }
.ftd p.ftd-legend { margin: 0; }   /* on a <p> the old reset cancelled it, and the approved render has it flush */
.ftd-legend .ftd-chip { margin: 0 4px 0 10px; }
.ftd-legend .ftd-chip:first-child { margin-left: 0; }
.ftd .ftd-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 1px solid var(--ftd-accent-soft); border-radius: var(--ftd-corner); background: var(--ftd-surface); color: var(--ftd-primary); font-size: 1.05rem; transition: all .15s; }   /* the one central corner; it was a circle, which the round setting made of it */
.ftd .ftd-iconbtn:hover { background: var(--ftd-primary); border-color: var(--ftd-primary); color: #fff; }
.ftd .ftd-iconbtn--danger { color: var(--ftd-bad-ink); }
.ftd .ftd-iconbtn--danger:hover { background: var(--ftd-bad-ink); border-color: var(--ftd-bad-ink); color: #fff; }
/* an import's log: one row of the file a row, and what became of it (row, object, department, outcome, note, the record it made) */
.ftd-list--log .ftd-list__head, .ftd .ftd-list--log .ftd-listrow { grid-template-columns: minmax(0, 2.4fr) minmax(0, .7fr) minmax(0, .9fr) 112px minmax(0, 1.8fr) 96px; }
.ftd .ftd-list--log .ftd-listrow { min-height: 54px; padding-top: 8px; padding-bottom: 8px; }
.ftd-rowno { flex: none; display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 6px; background: var(--ftd-inset); color: var(--ftd-muted); font-size: 12px; font-weight: 600; }
.ftd-badge--amber { background: #fdf0d5; color: #8a5a00; }
/* with less room the column names go and each record is its name, then its facts on a second line */
@container ftd (max-width: 1100px) {
    .ftd-list__head { display: none; }
    .ftd .ftd-listrow { grid-template-columns: minmax(0, 1fr) auto; gap: 6px 12px; padding: 10px 14px; }
    .ftd-listrow__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; grid-column: 1 / -1; order: 3; }
}

/* ---------------------------------------------------------------- latest imports, latest exports */
.ftd-files { width: 100%; border-collapse: collapse; }
.ftd .ftd-files td { padding: 9px 6px; border: 0; border-bottom: 1px solid var(--ftd-line); vertical-align: middle; }
.ftd .ftd-files tr:last-child td { border-bottom: 0; }
.ftd .ftd-files tr:hover td { background: var(--ftd-hover); }
.ftd .ftd-files__name { width: 60%; max-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--ftd-ink); }
.ftd-files__name i { margin-right: 6px; font-size: 1.1rem; vertical-align: middle; color: var(--ftd-accent); }
.ftd .ftd-files__when { text-align: right; white-space: nowrap; color: var(--ftd-muted); }
@container ftdcard (max-width: 420px) {
    .ftd .ftd-files__when { white-space: normal; font-size: 11px; }
    .ftd .ftd-files__name { width: 50%; }
}

/* ================================================================ ACTIVITY (the Timeline, rebuilt)
   One shared component in place of the block pasted into twenty files (AUDIT_SHARED_UI_2026-09-19.md, section 2). It does
   what the Timeline does today and nothing it has no back end for: no filter, no search, no edit, delete or reply.
     - the row above it is the list toolbar (T12): the count on the left, the layout on the right, the pressed button
       being the saved one (`preferred_timeline_layout`: 0 Timeline, 1 List; saved silently, as today);
     - "Timeline" is a card per entry; "List" is the same entries in THE TABLE (its columns are today's: Created By,
       Action, Title, Images, Date);
     - an entry: what kind it is on the rail (New, Update, Override, Comment: the List layout's own three words and the
       user's own posts), who, what they did in words (getItemPostTitle), when (the date and how long ago), then the text
       with its links, the list of what changed, and its images (a click opens the lightbox, on every page);
     - "Post an update" only where the page has it (`form` on or off per page), folded or open as the user left it
       (`post_update_form_appearance`). */
.ftd-posts { position: relative; margin: 0; padding: 0; }
.ftd-posts::before { content: ""; position: absolute; top: 18px; bottom: 18px; left: 15px; width: 2px; background: var(--ftd-line); }
.ftd-post { position: relative; display: grid; grid-template-columns: 32px 36px minmax(0, 1fr); gap: 0 12px; padding: 0 0 18px; }
.ftd-post:last-child { padding-bottom: 0; }
.ftd-post__kind { z-index: 1; display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; border: 2px solid var(--ftd-surface); border-radius: 50%; background: var(--ftd-accent-soft); color: var(--ftd-primary); font-size: 1rem; }
.ftd-post--new .ftd-post__kind { background: var(--ftd-good-bg); color: var(--ftd-good-ink); }
.ftd-post--override .ftd-post__kind { background: #fdf0d5; color: #8a5a00; }
.ftd-post--bad .ftd-post__kind { background: var(--ftd-bad-bg); color: var(--ftd-bad-ink); }
.ftd-post--comment .ftd-post__kind { background: var(--ftd-inset); color: var(--ftd-muted); }
.ftd-avatar { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; background: var(--ftd-primary); color: #fff; font-size: 12px; font-weight: 600; letter-spacing: .02em; }
.ftd-avatar--sm { width: 24px; height: 24px; font-size: 10px; }
.ftd-avatar--system { background: var(--ftd-inset); color: var(--ftd-muted); }
.ftd-post__body { min-width: 0; }
.ftd-post__line { color: var(--ftd-text); }
.ftd-post__line b { font-weight: 600; color: var(--ftd-ink); }
.ftd-post__when { font-size: 12px; color: var(--ftd-muted); }
.ftd-post__text { margin-top: 8px; padding: 10px 14px; border-radius: 8px; background: var(--ftd-inset); color: var(--ftd-text); overflow-wrap: break-word; }
.ftd .ftd-post__text a { color: var(--ftd-accent); font-weight: 500; }
.ftd .ftd-post__text a:hover { text-decoration: underline !important; }
.ftd .ftd-post__text > * + * { margin-top: 10px; }
/* what changed, as name and value: the entry's `post_meta`, the object's own dictionary deciding the names */
.ftd-post__facts { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px 18px; }
.ftd-post__facts dt { font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd-post__facts dd { color: var(--ftd-ink); overflow-wrap: break-word; }
.ftd-post__none { font-style: italic; color: var(--ftd-muted); }
.ftd-post__images { display: flex; flex-wrap: wrap; gap: 8px; }
.ftd .ftd-post__img { display: flex; align-items: center; justify-content: center; width: 96px; height: 72px; border: 1px solid var(--ftd-line); border-radius: 6px; background: var(--ftd-surface); color: var(--ftd-control-line); font-size: 1.6rem; cursor: zoom-in; }
.ftd .ftd-post__img:hover { border-color: var(--ftd-accent); color: var(--ftd-accent); }
.ftd-posts__more { display: flex; align-items: center; gap: 12px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--ftd-hairline); font-size: 12px; color: var(--ftd-muted); }
.ftd-posts__empty { padding: 28px 0 22px; text-align: center; color: var(--ftd-muted); }
.ftd-posts__empty i { display: block; margin-bottom: 6px; font-size: 2rem; color: var(--ftd-control-line); }

/* Post an update: the heading is the fold; the text, the images to go with it, Cancel (which folds it) and Submit */
.ftd-postform { margin: 0 0 18px; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); }
.ftd .ftd-postform__head { display: flex; align-items: center; justify-content: space-between; width: 100%; height: 42px; padding: 0 14px; border: 0; background: transparent; color: var(--ftd-ink); font-size: 13px; font-weight: 600; cursor: pointer; }
.ftd-postform__head i { font-size: 1.3rem; color: var(--ftd-muted); transition: transform .2s; }
.ftd-postform.is-open .ftd-postform__head i { transform: rotate(180deg); }
.ftd-postform__body { display: none; padding: 0 14px 14px; }
.ftd-postform.is-open .ftd-postform__body { display: block; }
.ftd-postform__fields { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 12px; }
.ftd .ftd-postform textarea { width: 100%; min-height: 112px; padding: 10px 12px; border: 1px solid var(--ftd-control-line); border-radius: 6px; font: inherit; color: var(--ftd-ink); resize: vertical; }
.ftd-postform__drop { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 112px; padding: 10px; border: 1px dashed var(--ftd-control-line); border-radius: 6px; background: var(--ftd-inset); color: var(--ftd-muted); font-size: 12px; text-align: center; }
.ftd-postform__drop i { font-size: 1.8rem; color: var(--ftd-control-line); }
.ftd-postform__foot { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 8px; margin-top: 12px; }
.ftd-postform__foot .ftd-postform__left { margin-right: auto; display: flex; gap: 8px; }
@container ftdcard (max-width: 520px) {
    .ftd-post { grid-template-columns: 32px minmax(0, 1fr); }
    .ftd-post > .ftd-avatar { display: none; }
    .ftd-postform__fields { grid-template-columns: minmax(0, 1fr); }
}
/* ================================================================ ACTIVITY ends */

/* ================================================================ BULK ACTIONS
   The one shared block behind every Bulk Actions tab (BulkActionsBlock.vue and its five sub-components), redrawn. It does
   what the block does today and keeps its contract (the page still says which actions to offer; one endpoint; the same
   saved preferences). Three numbered steps and a bar that always says what is about to happen:
     1  WHICH RECORDS   the ticked ones, or all N of the list as it is filtered now (it says how it is filtered);
     2  WHAT TO DO      the actions the page offers, as tiles, grouped; the chosen one is pressed;
     3  THE DETAIL      the panel of the chosen action: one panel drawn for tags, one for the six "change" actions, one
                        each for delete and storage planning, all from the same few pieces below;
     then WHEN IT IS DONE (the two saved preferences, each a one-of-N choice, which is what they are) and the submit bar. */
.ftd-steps { display: grid; gap: 20px; }
.ftd-step__no { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; margin-right: 10px; border-radius: 50%; background: rgba(255, 255, 255, .18); font-size: 11px; font-weight: 600; }
.ftd-card__head .ftd-step__hint { margin-left: auto; font-size: 11px; font-weight: 400; letter-spacing: 0; text-transform: none; opacity: .8; }

/* a choice among a few: a tile with an icon, a name and a line of explanation; the chosen one is pressed */
.ftd-choices { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
.ftd-choices--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.ftd .ftd-choice { position: relative; display: flex; align-items: flex-start; gap: 10px; width: 100%; min-height: 58px; padding: 10px 12px; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); color: var(--ftd-text); font: inherit; text-align: left; cursor: pointer; transition: all .15s; }
.ftd .ftd-choice:hover { border-color: var(--ftd-accent); background: var(--ftd-hover); }
.ftd .ftd-choice.is-on { border-color: var(--ftd-primary); background: var(--ftd-hover); box-shadow: inset 0 0 0 1px var(--ftd-primary); }
.ftd-choice__tick { display: none; position: absolute; top: 7px; right: 9px; font-size: 1.05rem; color: var(--ftd-primary); }
.ftd-choice.is-on .ftd-choice__tick { display: block; }
.ftd-choice > i { flex: none; display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 8px; background: var(--ftd-inset); color: var(--ftd-primary); font-size: 1.15rem; }
.ftd-choice.is-on > i { background: var(--ftd-surface); }
.ftd-choice__name { display: block; font-weight: 600; color: var(--ftd-ink); }
.ftd-choice__sub { display: block; font-size: 12px; color: var(--ftd-muted); }
.ftd .ftd-choice--danger > i, .ftd .ftd-choice--danger .ftd-choice__name { color: var(--ftd-bad-ink); }
.ftd .ftd-choice--danger.is-on { border-color: var(--ftd-bad-ink); background: #fdf1f1; box-shadow: inset 0 0 0 1px var(--ftd-bad-ink); }
.ftd-choice--danger .ftd-choice__tick { color: var(--ftd-bad-ink); }
.ftd .ftd-choice[disabled] { cursor: not-allowed; opacity: .5; }
.ftd-choicegroup { margin: 14px 0 6px; font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd-choicegroup:first-child { margin-top: 0; }

/* the chosen action's panel */
.ftd-field { margin: 0 0 14px; }
.ftd-field > label, .ftd-field__label { display: block; margin: 0 0 5px; font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd .ftd-field select, .ftd .ftd-field input[type="text"] { width: 100%; max-width: 460px; height: 36px; padding: 0 10px; border: 1px solid var(--ftd-control-line); border-radius: 6px; background: var(--ftd-surface); font: inherit; color: var(--ftd-ink); }
/* the help line is drawn on a <div>, a <span> AND a <p> across the mocks, and under the FIRST reset -- the plain
   `.ftd p` one, (0,1,1) -- the same class rendered two ways: 5px under a div or a span, nothing under a p, because
   that reset beat this single class on that one tag and nobody saw it. The guard at the head of this sheet is
   (0,1,0) and loses to a single class, so the p case would gain 5px it never had and move pages Tristian has
   approved. The approved render is the truth, so the 5px is kept where it was drawn and taken off where it was not.
   A single rule for both is a change to ask for, not one to slip in with a merge. */
.ftd-field__help { margin-top: 5px; font-size: 12px; color: var(--ftd-muted); }
.ftd p.ftd-field__help { margin-top: 0; }   /* on a <p> the old reset cancelled it, and the approved render has it flush */
.ftd-note { display: flex; gap: 8px; margin: 0 0 14px; padding: 9px 12px; border-left: 3px solid var(--ftd-accent); border-radius: 0 6px 6px 0; background: var(--ftd-inset); font-size: 12px; color: var(--ftd-text); }
.ftd-note i { flex: none; font-size: 1.05rem; color: var(--ftd-accent); }
.ftd-note--danger { border-left-color: var(--ftd-bad-ink); background: #fdf1f1; }
.ftd-note--danger i { color: var(--ftd-bad-ink); }
/* OPTIONS IN VIEW, TO CLICK (Tristian, 2026-09-19: not a dropdown, "the user can see the options and click to select", with the box above to narrow them).
   One of many (a department, a character, a set, a folder, a service provider: `data-single`) or several (tags). A chip is off or on; on shows the tick.
   No "x": nothing here removes anything from the system. The list scrolls inside its own height when it is long. */
.ftd-tagpick { display: flex; flex-wrap: wrap; gap: 6px; max-height: 190px; overflow-y: auto; padding: 2px; }
.ftd .ftd-tag { display: inline-flex; align-items: center; gap: 4px; height: 26px; padding: 0 10px; border: 1px solid var(--ftd-control-line); border-radius: 13px; background: var(--ftd-surface); color: var(--ftd-text); font: inherit; font-size: 12px; cursor: pointer; }
.ftd .ftd-tag:hover { border-color: var(--ftd-accent); }
.ftd .ftd-tag.is-on { border-color: var(--ftd-primary); background: var(--ftd-primary); color: #fff; }
.ftd-tag > i { display: none; font-size: .95rem; }
.ftd-tag.is-on > i { display: inline; }
.ftd-check { display: inline-flex; align-items: center; gap: 8px; color: var(--ftd-text); cursor: pointer; }
.ftd-check input { width: 16px; height: 16px; margin: 0; accent-color: var(--ftd-accent); }

/* when it is done: the two saved preferences, each one of a few */
.ftd-prefs { display: flex; flex-wrap: wrap; gap: 14px 34px; }
.ftd-prefs .ftd-control > span { min-width: 0; }

/* what is about to happen, and the button that does it; it stays in view */
.ftd-submitbar { position: sticky; bottom: 12px; z-index: 5; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin-top: 20px; padding: 12px 16px; border: 1px solid var(--ftd-line); border-radius: 10px; background: var(--ftd-surface); box-shadow: 0 10px 30px rgba(0, 28, 56, .14); }
.ftd-submitbar__what { margin-right: auto; color: var(--ftd-text); }
.ftd-submitbar__what b { font-weight: 600; color: var(--ftd-ink); }
.ftd .ftd-btn--danger { background: var(--ftd-bad-ink); }
.ftd .ftd-btn--danger:hover { background: #7f1d1d; }
.ftd .ftd-btn[aria-disabled="true"] { background: var(--ftd-control-line); cursor: not-allowed; }
/* after it is sent: the run, in place, for every action and not only for an export */
.ftd-run { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; padding: 12px 16px; border: 1px solid #cfe6d1; border-radius: 8px; background: #f1f8f2; }
.ftd-run__bar { flex: 1 1 220px; height: 8px; border-radius: 4px; background: #d8ecd9; overflow: hidden; }
.ftd-run__bar > span { display: block; height: 100%; background: var(--ftd-good-ink); }
/* the saved preferences as a quiet strip BELOW the button: settings, not a step of the action */
.ftd-prefstrip { margin-top: 14px; }
.ftd-submitbar--inline { position: static; margin-top: 18px; box-shadow: none; background: var(--ftd-inset); }
/* small tiles (a name and its icon, no second line) and a one-column list of them */
.ftd-choices--small { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 8px; }
.ftd-choices--small .ftd-choice, .ftd-choices--list .ftd-choice { align-items: center; min-height: 44px; padding: 6px 10px; }
.ftd-choices--list { grid-template-columns: minmax(0, 1fr); gap: 6px; }
.ftd-choices--small + .ftd-choices--small { margin-top: 8px; }
/* the first design's tiles, slimmed for the compact card: the icon, the name and ONE line of explanation; a group's name ABOVE its row of tiles (Tristian, 2026-09-19: not beside it, which pushed the tiles to the right) */
.ftd-choice > span:not(.ftd-choice__tick) { min-width: 0; }
.ftd-choices--slim { grid-template-columns: repeat(auto-fill, minmax(245px, 1fr)); gap: 8px; }
.ftd .ftd-choices--slim .ftd-choice { align-items: center; min-height: 46px; padding: 5px 28px 5px 9px; }
.ftd-choices--slim .ftd-choice > i { width: 30px; height: 30px; font-size: 1.05rem; }
.ftd-choices--slim .ftd-choice__name { line-height: 1.3; }
.ftd-choices--slim .ftd-choice__sub { font-size: 11.5px; line-height: 1.35; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ftd-choices--which { grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) auto; align-items: center; }
.ftd-choices--which .ftd-formrow__link { margin-left: 4px; white-space: nowrap; }
.ftd-choicerow + .ftd-choicerow { margin-top: 10px; }
.ftd-choicerow > .ftd-choicegroup { margin: 0 0 5px; }
@container ftd (max-width: 900px) {
    .ftd-choices--which { grid-template-columns: minmax(0, 1fr); }
}
/* B. conditional steps: one open at a time; a finished step is one line and can be changed; a step not yet reached is only its name */
.ftd-stepper { border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); box-shadow: 0 5px 30px rgba(0, 28, 56, .06); }
.ftd-sstep + .ftd-sstep { border-top: 1px solid var(--ftd-line); }
.ftd-sstep__head { display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 0 18px; }
.ftd-sstep__no { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; background: var(--ftd-inset); color: var(--ftd-muted); font-size: 12px; font-weight: 600; }
.ftd-sstep__title { font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd-sstep__summary { font-weight: 600; color: var(--ftd-ink); }
.ftd .ftd-sstep__change { display: none; margin-left: auto; color: var(--ftd-accent); font-size: 12px; font-weight: 500; }
.ftd-sstep__body { display: none; padding: 4px 18px 18px 56px; }
.ftd-sstep.is-open > .ftd-sstep__body { display: block; }
.ftd-sstep.is-open .ftd-sstep__no { background: var(--ftd-primary); color: #fff; }
.ftd-sstep.is-open .ftd-sstep__title { color: var(--ftd-ink); }
.ftd-sstep.is-open .ftd-sstep__summary { display: none; }
.ftd-sstep.is-done .ftd-sstep__no { background: var(--ftd-good-bg); color: var(--ftd-good-ink); }
.ftd-sstep.is-done .ftd-sstep__change { display: inline; }
.ftd-sstep.is-todo { opacity: .55; }
.ftd-sstep.is-todo .ftd-sstep__summary { display: none; }
/* C. side by side */
.ftd-bulk-split { display: grid; grid-template-columns: minmax(280px, .8fr) minmax(0, 1.7fr); gap: 20px; align-items: start; }
.ftd-bulk-split__left, .ftd-bulk-split__right { display: grid; gap: 20px; }
.ftd-bulk-split__right { position: sticky; top: 12px; }
/* D. one compact card: a label on the left, its control on the right */
.ftd-formrow { display: grid; grid-template-columns: 130px minmax(0, 1fr); gap: 8px 18px; padding: 12px 0; border-bottom: 1px solid var(--ftd-hairline); }
.ftd-formrow:first-child { padding-top: 0; }
.ftd-formrow__label { padding-top: 6px; font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd-formrow__body > .ftd-field:last-child, .ftd-formrow__body [data-panel] > .ftd-field:last-child { margin-bottom: 0; }
.ftd .ftd-formrow__link { margin-left: 12px; color: var(--ftd-accent); font-size: 12px; font-weight: 500; }
.ftd .ftd-seg--big button { height: 34px; padding: 0 16px; font-size: 13px; }
@container ftd (max-width: 900px) {
    .ftd-bulk-split { grid-template-columns: minmax(0, 1fr); }
    .ftd-bulk-split__right { position: static; }
    .ftd-formrow { grid-template-columns: minmax(0, 1fr); }
    .ftd-sstep__body { padding-left: 18px; }
}
@container ftd (max-width: 700px) {
    .ftd-choices--two { grid-template-columns: minmax(0, 1fr); }
}
/* ================================================================ BULK ACTIONS ends */

/* ================================================================ FOUNDATION
   The design walk's shared layer, folded in (DESIGN_WALK_PLAN_2026-09-19.md section B, primitives F1 to F20).
   It was `design-walk/foundation.css`, loaded after this sheet while the twenty-one designs were drawn; now it is
   part of the one sheet Abed ships, and foundation.css is no longer needed to draw a page.

   WHAT CHANGED IN THE FOLDING, and nothing else did:
     1. ONE VALUE FOR CORNERS. The walk compared square, 6px and round behind html[data-corners]; Tristian chose 6px
        (Log #355). The switch is gone and 6px is the only value. An underline tab still has no corners.
     2. ONE VALUE FOR WEIGHT. The walk compared strong, Balanced and quiet behind html[data-weight]; Tristian chose
        Balanced (Log #355). The switch is gone. A card's head, a boxed section's head and a modal's head are ONE
        grammar: the inset grey with the title in ink, framed on the real line colour.
     3. THE REDEFINITIONS ARE FOLDED WHERE THE THING IS DECLARED, not restated here. Where this layer used to
        overrule a rule above it by loading later, that rule above now carries the signed-off result itself and says
        so in its own one-line comment. Nothing in the sheet fights anything else in it.

   Everything else is as Tristian signed it off on r1 to r8: the section as a bordered box with a heading, always
   open (Log #356), the meaning colours (Yes / On Camera green, No / Off Camera red, Unknown grey), and the restyle
   of the central boxes, forms and modals for the whole site (Log #360).

   IT OBEYS THE SHEET'S OWN RULES, as it did as a separate file: everything under `.ftd`, every class `ftd-`, theme
   colour only through the four --ftd-* variables, container queries and never media queries, a one-line comment on
   every class, no Bootstrap. Two literal neutrals are named here for states the sheet already writes literally in
   the same way: --ftd-warn-bg / --ftd-warn-ink (the amber of .ftd-badge--amber and .ftd-post--override) and
   --ftd-focus (the accent at 35%).

   WHERE IT SITS, and why: BEFORE the section THE TABLE, which must stay last because the table's builder rewrites
   everything from THE TABLE's marker to the end of the file. ACTIVITY and BULK ACTIONS splice themselves in just
   before THE TABLE too, so the order of the three is not fixed and this section never relies on it: the three
   places where a later section declares the same thing carry their weight in specificity instead, and each says so
   where it stands. */

.ftd {
    /* the amber the approved sheet already writes literally at .ftd-badge--amber and .ftd-post--override, named once
       so a warning is the same amber everywhere; and the focus ring, which is the accent, not a colour of its own */
    --ftd-warn-bg: #fdf0d5;
    --ftd-warn-ink: #8a5a00;
    --ftd-warn-line: #ecd9ab;
    --ftd-focus: rgba(85, 110, 230, .35);
    --ftd-focus: color-mix(in srgb, var(--ftd-accent) 35%, transparent);
    /* THE OFF INK: the colour of text that is switched off - an absent value's dash, a disabled tab, a disabled menu
       entry, a disabled page number. It is NOT --ftd-control-line. --ftd-control-line is a LINE colour (#d3d9e2) and
       measures 1.4:1 on white: as a line it is right, as words it is very nearly invisible, and "invisible" is not the
       same as "off". Measured: 3.35:1 on the surface and 3.10:1 on the inset, so a disabled thing still reads as words
       the eye can find and then dismiss. A dash nobody can see is a bug report, not a design.
       Large decorative glyphs (the dropzone's cloud, an empty state's icon at 2.4rem) keep --ftd-control-line: they
       are furniture, not words, and at that size the lighter tone is the point. */
    --ftd-off-ink: #848d9b;
}

/* ONE FOCUS RING for every control this file draws, so a keyboard user is never lost. It is the accent. */
.ftd :focus-visible { outline: 2px solid var(--ftd-accent); outline-offset: 1px; }

/* THE BUTTON-ELEMENT RESET. THIS IS LOAD-BEARING: READ IT BEFORE YOU WRITE A CONTROL.
   .ftd-btn, .ftd-pill and .ftd-iconbtn were written in the approved sheet for an <a>: they set a background and a
   radius but NO border, because an anchor has none. The moment the same class goes on a real <button> - which F1
   REQUIRES, because only a <button> carries `disabled`, is keyboard-operable and is announced as an action - the
   browser's own button box comes back underneath: a 2px grey `outset` border and a #f0f0f0 fill. Measured: a
   <button class="ftd-pill"> drew a GREY RECTANGLE where the <a> drew a pill, and a <button class="ftd-btn"> carried a
   2px grey ring around the fill. The kit alone writes 84 <button class="ftd-pill"> and 42 <button class="ftd-iconbtn">,
   so twenty-one designs would each have drawn it. This rule makes the ELEMENT TYPE STOP MATTERING: the three classes
   look identical on an <a> and on a <button>, so a designer picks the right element for the job and never the look.
   Only these three need it. .ftd-tag, .ftd-choice, .ftd-seg's buttons, .ftd-table__more and .ftd-table__toggle already
   declare their own `border` in the approved sheet, and .ftd-control is a flex WRAPPER, not a button: leave all of
   those alone, and never add one to the list below without checking the approved sheet first. */
.ftd button.ftd-btn, .ftd button.ftd-pill, .ftd button.ftd-iconbtn {
    -webkit-appearance: none; appearance: none; margin: 0; font: inherit; line-height: normal; cursor: pointer;
}
.ftd button.ftd-btn { border: 0; }
.ftd button.ftd-pill, .ftd button.ftd-iconbtn { border: 1px solid var(--ftd-accent-soft); }
/* the modifiers of those three that change the border, restated at the same weight so the reset cannot flatten them */
.ftd button.ftd-pill--danger { border-color: #f1cccc; }
.ftd button.ftd-pill--good { border-color: #cfe6d1; }
.ftd button.ftd-btn--ghost { border: 1px solid var(--ftd-control-line); }
.ftd button.ftd-pill:hover, .ftd button.ftd-iconbtn:hover { border-color: var(--ftd-primary); }
.ftd button.ftd-pill--danger:hover, .ftd button.ftd-iconbtn--danger:hover { border-color: var(--ftd-bad-ink); }
.ftd button.ftd-pill--good:hover { border-color: var(--ftd-good-ink); }
.ftd button.ftd-btn--ghost:hover { border-color: var(--ftd-accent); }
/* the OTHER classes this file itself draws on a <button>: they are new here, so they set their own border already and
   need only the appearance reset, which a UA stylesheet would otherwise win on `appearance` alone. */
.ftd button.ftd-tile, .ftd button.ftd-page, .ftd button.ftd-dots, .ftd button.ftd-tab, .ftd button.ftd-hstep, .ftd button.ftd-sec__toggle, .ftd button.ftd-picker__add, .ftd button.ftd-multi__x, .ftd button.ftd-modal__x, .ftd button.ftd-toast__x, .ftd button.ftd-banner__x, .ftd button.ftd-carousel__dot, .ftd button.ftd-carousel__arrow, .ftd button.ftd-link, .ftd button.ftd-tabs__more {
    -webkit-appearance: none; appearance: none; margin: 0; font: inherit; cursor: pointer;
}

/* ---------------------------------------------------------------- F1  BUTTON
   The approved .ftd-btn (32px, filled, pill) and .ftd-pill (32px, outlined) are the primary and the secondary. This
   section adds only the variants and the states the plan names, as modifiers of those two. A BUTTON MUST SURVIVE A
   REFUSED SAVE: `is-busy` is a state the component sets and clears, never a one-way `click.once`, so the mock shows
   the busy button and the same button live again beside it. */
.ftd .ftd-btn--ghost { background: transparent; border: 1px solid var(--ftd-control-line); color: var(--ftd-primary); }   /* F1 a third weight: no fill, a hairline; for a toolbar where a filled button would shout */
.ftd .ftd-btn--ghost:hover { background: var(--ftd-hover); border-color: var(--ftd-accent); color: var(--ftd-primary); }
.ftd .ftd-pill--good { border-color: #cfe6d1; color: var(--ftd-good-ink); }   /* F1 an affirmative outlined button (Approve, Mark done) */
.ftd .ftd-pill--good:hover { background: var(--ftd-good-ink); border-color: var(--ftd-good-ink); color: #fff; }
.ftd .ftd-link { display: inline-flex; align-items: center; gap: 4px; color: var(--ftd-accent); font-weight: 500; }   /* F1 the link button: an action drawn as a link (Change, View all, Set Usage) */
.ftd .ftd-link:hover { color: var(--ftd-accent); text-decoration: underline !important; }
.ftd .ftd-link--danger { color: var(--ftd-bad-ink); }   /* F1 a link button that destroys something (Remove All, Clear) */
.ftd .ftd-link--danger:hover { color: var(--ftd-bad-ink); }
.ftd .ftd-btn--sm, .ftd .ftd-pill--sm { height: 26px; padding: 0 11px; font-size: 11.5px; }   /* F1 the small size, the same height as a settings control so the two sit on one line */
.ftd .ftd-btn--sm i, .ftd .ftd-pill--sm i { font-size: .95rem; }
.ftd .ftd-iconbtn--sm { width: 26px; height: 26px; font-size: .95rem; }   /* F1 icon-only, small: a row's inline action */
.ftd .ftd-btn--block { width: 100%; justify-content: center; }   /* F1 full width: a modal's footer on a phone, a card's one action */
.ftd .ftd-btn[disabled], .ftd .ftd-pill[disabled], .ftd .ftd-iconbtn[disabled],
.ftd .ftd-btn.is-off, .ftd .ftd-pill.is-off, .ftd .ftd-iconbtn.is-off { cursor: not-allowed; opacity: .55; pointer-events: auto; }   /* F1 disabled: still hoverable, so its `title` reason can be read */
.ftd .ftd-btn[disabled]:hover, .ftd .ftd-btn.is-off:hover { background: var(--ftd-control-line); color: #fff; }
.ftd .ftd-pill[disabled]:hover, .ftd .ftd-pill.is-off:hover { background: var(--ftd-surface); border-color: var(--ftd-control-line); color: var(--ftd-muted); }
.ftd .ftd-btn.is-busy, .ftd .ftd-pill.is-busy { cursor: progress; }   /* F1 busy: the label reads "Saving..." and a spinner leads it; the button is NOT destroyed, so a refusal leaves it usable */
.ftd .ftd-btn.is-busy > .ftd-spin, .ftd .ftd-pill.is-busy > .ftd-spin { display: inline-block; }
.ftd-btnrow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }   /* F1 a run of buttons that wraps rather than clips */
.ftd-btnrow--right { justify-content: flex-end; }   /* F1 the same run, pushed right: a footer */
.ftd-btnrow--split > .ftd-btnrow__left { margin-right: auto; display: flex; flex-wrap: wrap; gap: 8px; }   /* F1 a footer with something on the left (Delete) and the submit on the right */

/* the spinner mark, shared by a busy button (F1) and the loading state (F14) */
.ftd-spin { display: none; flex: none; width: 13px; height: 13px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: ftd-spin .7s linear infinite; }
@keyframes ftd-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .ftd-spin { animation-duration: 2.4s; } }

/* ---------------------------------------------------------------- F2  FIELD CONTROL
   The approved .ftd-field carries the label, the help line and a 36px text input inside a bulk panel. A FORM needs the
   agreed 32px control, every type the plan names, and the four states. One class on the control, `.ftd-input`, so a
   text box, a number, a select, a textarea and a date all agree; the modifiers say what is different about each. */
.ftd-fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 0 18px; }   /* F2 fields side by side, as many as the room allows, one column when it does not */
.ftd-fields--one { grid-template-columns: minmax(0, 1fr); }   /* F2 one column: a modal, a narrow panel */
.ftd-fields--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }   /* F2 exactly two columns, whatever the room (a form's agreed pair) */
.ftd-field--wide { grid-column: 1 / -1; }   /* F2 a field that takes the whole row (a description, a note) */
/* the approved label, able to carry a required mark and a counter beside its words. It restates the approved rule's
   own margin and size: this selector outranks `.ftd-field > label` (0,3,1 against 0,1,1), so anything the approved
   rule set that is NOT restated here would be kept, but a reader cannot see that at a glance, and a later edit that
   changed `display` alone would silently move every label on the site. Written out, it is checkable. */
.ftd .ftd-field > label, .ftd .ftd-field__label { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 6px; margin: 0 0 5px; font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd-field__req { color: var(--ftd-bad-ink); }   /* F2 the required mark: one asterisk beside the label, nothing else */
.ftd-field__opt { font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--ftd-muted); }   /* F2 a quiet aside in a label ("2 chosen", "optional") */
/* F2 THE control: text, number, date, select and a textarea all start here. 32px, the agreed height.
   It is written at the specificity of the approved sheet's own `.ftd .ftd-field input[type="text"]` rule (0,3,1), and
   only that: this file loads after the approved one, so an equal specificity is enough to win and nothing here needs
   `!important`. Every rule that changes a control's box below is written at the same weight for the same reason. */
.ftd .ftd-field .ftd-input, .ftd .ftd-input { width: 100%; height: 32px; padding: 0 10px; border: 1px solid var(--ftd-control-line); border-radius: 6px; background: var(--ftd-surface); color: var(--ftd-ink); font: inherit; max-width: none; }
.ftd .ftd-input::placeholder { color: var(--ftd-muted); }
.ftd .ftd-input:hover { border-color: var(--ftd-accent-soft); }
.ftd .ftd-field .ftd-input:focus, .ftd .ftd-input:focus { border-color: var(--ftd-accent); box-shadow: 0 0 0 3px var(--ftd-focus); outline: none; }
.ftd .ftd-field select.ftd-input, .ftd select.ftd-input { padding-right: 26px; appearance: none; background-image: linear-gradient(45deg, transparent 49%, var(--ftd-muted) 50%), linear-gradient(135deg, var(--ftd-muted) 50%, transparent 51%); background-position: right 12px center, right 7px center; background-size: 5px 5px; background-repeat: no-repeat; }   /* F2 the native select, with the site's own chevron rather than the platform's */
.ftd .ftd-field textarea.ftd-input, .ftd textarea.ftd-input { height: auto; min-height: 76px; padding: 8px 10px; line-height: 1.5; resize: vertical; }   /* F2 the textarea: the same box, free to grow downwards only */
.ftd .ftd-field .ftd-input--num, .ftd .ftd-input--num { max-width: 150px; text-align: right; font-variant-numeric: tabular-nums; }   /* F2 a number: right aligned, tabular figures, so a column of them lines up */
.ftd-field--max { max-width: 460px; }   /* F2 a field that must not stretch across a wide page */
.ftd-inputwrap { position: relative; display: flex; align-items: center; width: 100%; }   /* F2 a control with something fixed beside it: a currency, a unit, a calendar button. It is as wide as its control, so the fixed part sits on the control's edge and never over its value */
.ftd .ftd-field .ftd-inputwrap > .ftd-input, .ftd-inputwrap > .ftd-input { flex: 1 1 auto; min-width: 0; padding-right: 46px; }
.ftd-inputwrap--num { max-width: 150px; }   /* F2 the wrap around a number: the agreed narrow width lives on the wrap, so the suffix keeps to the box's edge */
.ftd .ftd-field .ftd-inputwrap > .ftd-input--num, .ftd .ftd-inputwrap > .ftd-input--num { max-width: none; }
.ftd .ftd-field .ftd-inputwrap--pre > .ftd-input, .ftd-inputwrap--pre > .ftd-input { padding-right: 10px; padding-left: 34px; }
.ftd-input__suffix, .ftd-input__prefix { position: absolute; display: flex; align-items: center; justify-content: center; min-width: 26px; height: 30px; padding: 0 8px; color: var(--ftd-muted); font-size: 12px; pointer-events: none; }   /* F2 the fixed part: "GBP", "%", "kg" (the money rules decide the word, not this file) */
.ftd-input__suffix { right: 1px; border-left: 1px solid var(--ftd-hairline); }
.ftd-input__prefix { left: 1px; }
.ftd-inputwrap > .ftd-iconbtn { position: absolute; right: 1px; border-color: transparent; }   /* F2 the date picker's calendar button, inside the box */
.ftd .ftd-field.is-invalid .ftd-input, .ftd .ftd-field .ftd-input.is-invalid, .ftd .ftd-input.is-invalid { border-color: var(--ftd-bad-ink); background: #fdf7f7; }   /* F2 invalid: a red edge, and the message below says why */
.ftd .ftd-field__error { display: flex; align-items: flex-start; gap: 5px; margin-top: 5px; color: var(--ftd-bad-ink); font-size: 12px; }   /* F2 the message under an invalid control; it replaces nothing, the help line stays */
.ftd-field__error i { font-size: 1rem; }
.ftd .ftd-field .ftd-input[disabled], .ftd .ftd-input[disabled], .ftd .ftd-input:disabled { background: var(--ftd-inset); color: var(--ftd-muted); cursor: not-allowed; }   /* F2 disabled */
.ftd .ftd-field .ftd-input[readonly], .ftd .ftd-input[readonly] { background: var(--ftd-inset); }   /* F2 read-only, still a box: the value can be selected and copied */
.ftd-plain { display: flex; align-items: center; min-height: 32px; color: var(--ftd-ink); }   /* F2 read-only plaintext: no box at all, the value where the control would be */
.ftd-plain--none { color: var(--ftd-muted); }   /* F2 read-only plaintext with no value: a dash, never an empty line */
.ftd-locked { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; color: var(--ftd-ink); }   /* F2 locked: a value that an import fixed and this user may not change, with the padlock and its reason on hover */
.ftd-locked > i { color: var(--ftd-warn-ink); font-size: 1rem; }
.ftd-toggle { position: relative; display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }   /* F2 the toggle: on or off, nothing between */
.ftd-toggle > input { position: absolute; width: 34px; height: 20px; margin: 0; opacity: 0; cursor: pointer; }
.ftd-toggle__track { flex: none; width: 34px; height: 20px; padding: 2px; border-radius: 10px; background: var(--ftd-control-line); transition: background .15s; }
.ftd-toggle__track::before { content: ""; display: block; width: 16px; height: 16px; border-radius: 50%; background: #fff; transition: transform .15s; }
.ftd-toggle > input:checked + .ftd-toggle__track { background: var(--ftd-accent); }
.ftd-toggle > input:checked + .ftd-toggle__track::before { transform: translateX(14px); }
.ftd-toggle > input:focus-visible + .ftd-toggle__track { box-shadow: 0 0 0 3px var(--ftd-focus); }
.ftd-toggle > input:disabled + .ftd-toggle__track { opacity: .5; }
.ftd-checks { display: flex; flex-wrap: wrap; gap: 6px 22px; min-height: 32px; align-items: center; }   /* F2 a run of tick boxes or radios that wraps */
.ftd-checks--col { flex-direction: column; align-items: flex-start; gap: 6px; }   /* F2 the same, one above the other */
.ftd .ftd-check input[type="radio"] { border-radius: 50%; }
.ftd .ftd-check.is-off { color: var(--ftd-muted); cursor: not-allowed; }   /* F2 a tick box that may not be changed, its reason on hover */
/* F2 THE MULTISELECT: a box that holds the chosen ones as chips and opens the picker below it. The chip's X removes
   it from THIS FIELD only (it deletes nothing in the system: that is why the .ftd-tagpick above has no X). */
.ftd-multi { min-height: 32px; display: flex; flex-wrap: wrap; align-items: center; gap: 5px; padding: 3px 8px; border: 1px solid var(--ftd-control-line); border-radius: 6px; background: var(--ftd-surface); cursor: text; }
.ftd-multi:focus-within { border-color: var(--ftd-accent); box-shadow: 0 0 0 3px var(--ftd-focus); }
.ftd-multi__none { color: var(--ftd-muted); }   /* F2 the multiselect with nothing chosen yet */
.ftd-multi__chip { display: inline-flex; align-items: center; gap: 4px; height: 22px; padding: 0 3px 0 9px; border-radius: 11px; background: var(--ftd-accent-soft); color: var(--ftd-primary); font-size: 11.5px; font-weight: 500; }   /* F2 one chosen value inside the multiselect */
.ftd .ftd-multi__x { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border: 0; border-radius: 50%; background: transparent; color: inherit; font-size: .95rem; cursor: pointer; }
.ftd .ftd-multi__x:hover { background: rgba(255, 255, 255, .6); }
/* F2 THE PICKER, Tristian 2026-09-19: options visible and clicked, with a Find box above them to narrow the list, never
   hidden in a select when there is room. The approved .ftd-tagpick / .ftd-tag are the options; this adds the box around
   them, the Find row, the count and the "Add new" row (the id -99 option the forms already carry). */
.ftd-picker { border: 1px solid var(--ftd-control-line); border-radius: 6px; background: var(--ftd-surface); }
.ftd-picker__find { display: flex; align-items: center; gap: 6px; padding: 5px 6px; border-bottom: 1px solid var(--ftd-hairline); }
.ftd-picker__find > i { flex: none; margin-left: 4px; color: var(--ftd-muted); font-size: 1.05rem; }
.ftd .ftd-field .ftd-picker__find .ftd-input, .ftd .ftd-picker__find .ftd-input { height: 26px; border: 0; padding: 0; }
.ftd .ftd-field .ftd-picker__find .ftd-input:focus, .ftd .ftd-picker__find .ftd-input:focus { box-shadow: none; }
.ftd-picker__count { flex: none; padding-right: 6px; color: var(--ftd-muted); font-size: 11.5px; white-space: nowrap; }   /* F2 how many options the Find box has left, so a narrowed list is never a mystery */
.ftd-picker > .ftd-tagpick { padding: 7px; }
.ftd-picker__add { display: flex; align-items: center; gap: 6px; width: 100%; padding: 7px 12px; border: 0; border-top: 1px solid var(--ftd-hairline); border-radius: 0 0 5px 5px; background: var(--ftd-inset); color: var(--ftd-accent); font: inherit; font-weight: 500; text-align: left; cursor: pointer; }   /* F2 the Add new row at the foot of a picker: it opens the create modal for this kind of thing */
.ftd .ftd-picker__add:hover { background: var(--ftd-hover); }
.ftd-picker__empty { padding: 14px 12px; color: var(--ftd-muted); text-align: center; }   /* F2 a picker whose Find box matched nothing, or which has no options at all */
/* F2 AN OPTION ROW WITH A FACE: the picker option that carries an avatar or a customer logo (MultiSelect.js builds
   this in JS today with inline !important; here it is a class). */
.ftd-optrow { display: flex; align-items: center; gap: 8px; min-width: 0; }
.ftd-optrow__text { min-width: 0; }
.ftd-optrow__name { display: block; color: var(--ftd-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ftd-optrow__sub { display: block; font-size: 11.5px; color: var(--ftd-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ftd .ftd-tag--face { height: auto; min-height: 34px; gap: 8px; padding: 3px 12px 3px 4px; border-radius: 17px; text-align: left; }   /* F2 a chip big enough to hold that face, its name and a second line without crowding the tick */
.ftd .ftd-tag--face.is-on { padding-right: 8px; }
/* the chosen one is drawn on the primary colour, so its two lines of text must turn with it: the name kept its dark ink and
   vanished into the background (Tristian, 2026-09-19: "colours are not correct on this item"). The face gets a light ring so a
   dark avatar does not sink into a dark tile either. */
.ftd .ftd-tag.is-on .ftd-optrow__name { color: #fff; }
.ftd .ftd-tag.is-on .ftd-tag__n { color: #fff; background: rgba(255, 255, 255, .18); }   /* a count inside a chosen chip (D18): it kept its dark ink and vanished */
.ftd .ftd-tag.is-on .ftd-optrow__sub { color: rgba(255, 255, 255, .78); }
.ftd .ftd-tag.is-on .ftd-avatar { box-shadow: 0 0 0 1.5px rgba(255, 255, 255, .7); }
/* F2 RADIO TILES: a three-state choice as tiles rather than a select. Status, Asset Usage, Is Hero?, Is Asset? are
   each one of a few, and the plan asks for them as tiles. The group's heading goes ABOVE its tiles (Tristian). */
.ftd-tiles { display: flex; flex-wrap: wrap; gap: 6px; }
.ftd .ftd-tile { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 13px; border: 1px solid var(--ftd-control-line); border-radius: 16px; background: var(--ftd-surface); color: var(--ftd-text); font: inherit; cursor: pointer; transition: all .15s; }
.ftd .ftd-tile:hover { border-color: var(--ftd-accent); background: var(--ftd-hover); }
.ftd .ftd-tile.is-on { border-color: var(--ftd-primary); background: var(--ftd-primary); color: #fff; }
.ftd-tile > i { display: none; font-size: 1rem; }
.ftd-tile.is-on > i { display: inline; }
.ftd .ftd-tile--yes.is-on { border-color: var(--ftd-good-ink); background: var(--ftd-good-bg); color: var(--ftd-good-ink); }   /* F2 the affirmative tile of a yes / no / unknown set */
.ftd .ftd-tile--no.is-on { border-color: var(--ftd-bad-ink); background: var(--ftd-bad-bg); color: var(--ftd-bad-ink); }   /* F2 its negative */
.ftd .ftd-tile[disabled] { cursor: not-allowed; opacity: .55; }

/* ---------------------------------------------------------------- F3  THE SECTION, in three treatments
   ONE class with two modifiers, so a design chooses a treatment by adding nothing or one word and every design in the
   set can be compared honestly. The plan's recommendation is the default.
     .ftd-sec            FLAT PANEL (the recommended default): a small heading over its content with a hairline above
                         it. No border, no box, no chevron. Recovers the vertical space Tristian objects to.
     .ftd-sec--boxed     a bordered box, ALWAYS OPEN. Nearest to today (NewCustomGenericBlock / AssetInfoSection).
     .ftd-sec--rows      label left, control right; the section reads as a fact sheet being edited.
   An optional right-hand action link (`.ftd-sec__link`) sits on the heading row in all three.
   THE DISCLOSURE VARIANT is a real <button> (`.ftd-sec__toggle`), keyboard-operable, with aria-expanded, for the one
   option that keeps collapse: today's chevron is a div with a @click.self dead zone and no role at all. */
.ftd-sec { margin: 0 0 18px; }
.ftd-sec:last-child { margin-bottom: 0; }
.ftd-sec__head { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; padding: 7px 0 6px; border-top: 1px solid var(--ftd-line); }   /* F3 the heading row: the hairline above it is the whole frame of a flat section */
.ftd-sec__title { display: flex; align-items: center; gap: 7px; font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--ftd-primary); }   /* F3 the section's name, above its content, never beside it */
.ftd-sec__title > i { font-size: 1.05rem; color: var(--ftd-accent); }
.ftd-sec__note { font-size: 11.5px; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--ftd-muted); }   /* F3 a quiet aside on the heading row (a count, a hint) */
.ftd .ftd-sec__link { margin-left: auto; color: var(--ftd-accent); font-size: 12px; font-weight: 500; }   /* F3 the optional right-hand action link (Set Usage, Change, Add) */
.ftd .ftd-sec__link:hover { text-decoration: underline !important; }
.ftd-sec__body { padding: 10px 0 2px; }
.ftd-sec--boxed { border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); }   /* F3 treatment 2: the bordered box, always open */
.ftd-sec--boxed > .ftd-sec__head { padding: 9px 16px 8px; border-top: 0; border-bottom: 1px solid var(--ftd-line); background: var(--ftd-inset); border-radius: 7px 7px 0 0; }
.ftd-sec--boxed > .ftd-sec__body { padding: 14px 16px 16px; }
.ftd-sec--rows > .ftd-sec__body { padding-top: 4px; }   /* F3 treatment 3: rows. The body holds .ftd-formrow, which is approved */
/* the approved .ftd-formrow puts a hairline under EVERY row, last one included, so a rows-section always ended with a
   stray line hanging under its final field with nothing beneath it. The approved sheet has no :last-child rule for it
   and must not be edited, so the last row's line is taken off here, where the rows treatment is defined. */
.ftd-sec__body > .ftd-formrow:last-child { border-bottom: 0; padding-bottom: 0; }
/* in a row a control does not run the width of the page: a text box a metre long reads as a mistake, and the approved
   .ftd-field select / input[type=text] is already capped at 460px for the same reason */
.ftd .ftd-formrow__body > .ftd-input, .ftd .ftd-formrow__body > .ftd-inputwrap,
.ftd .ftd-formrow__body > .ftd-multi, .ftd .ftd-formrow__body > .ftd-picker { max-width: 460px; }
.ftd .ftd-formrow__body > textarea.ftd-input { max-width: 620px; }
.ftd-sec--rows.ftd-sec--boxed > .ftd-sec__body { padding: 8px 16px 12px; }
.ftd-sec--flush > .ftd-sec__head { border-top: 0; padding-top: 0; }   /* F3 the first section of a panel, where a hairline would double the panel's own edge */
.ftd .ftd-sec__toggle { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; width: 100%; padding: 7px 0 6px; border: 0; border-top: 1px solid var(--ftd-line); background: transparent; font: inherit; text-align: left; cursor: pointer; }   /* F3 the real disclosure button, for the option that keeps collapse: focusable, aria-expanded, no dead zone */
.ftd-sec--boxed > .ftd-sec__toggle { padding: 9px 16px 8px; border-top: 0; border-bottom: 1px solid var(--ftd-line); background: var(--ftd-inset); border-radius: 7px 7px 0 0; }
.ftd .ftd-sec__toggle:hover { color: var(--ftd-primary); }
.ftd-sec__chev { margin-left: auto; color: var(--ftd-muted); font-size: 1.25rem; transition: transform .2s; }
.ftd-sec__toggle[aria-expanded="false"] > .ftd-sec__chev { transform: rotate(-90deg); }
.ftd-sec__toggle[aria-expanded="false"] + .ftd-sec__body { display: none; }
.ftd-sec__toggle[aria-expanded="false"] { border-bottom-color: transparent; border-radius: 7px; }

/* ---------------------------------------------------------------- F4  THE FACT GRID (label and value)
   The central label-and-value cell, and the answer to the one property that lets eleven card boxes be re-pointed
   rather than redrawn: ABSENT PRINTS A DASH AND KEEPS ITS LABEL. Today a false v-if takes the label away with the
   value, which is why the card boxes refuse the shared cell. Two, three or four columns by the room, not the screen. */
.ftd-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 20px; container-type: inline-size; container-name: ftdfacts; }
.ftd-facts--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }   /* F4 three columns where the room allows */
.ftd-facts--four { grid-template-columns: repeat(4, minmax(0, 1fr)); }   /* F4 four: a full-width section on a wide page */
.ftd-facts--one { grid-template-columns: minmax(0, 1fr); }   /* F4 one column: a narrow summary panel */
@container ftdcard (max-width: 760px) { .ftd-facts--four { grid-template-columns: repeat(2, minmax(0, 1fr)); } .ftd-facts--three { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@container ftdcard (max-width: 420px) { .ftd-facts, .ftd-facts--three, .ftd-facts--four { grid-template-columns: minmax(0, 1fr); } }
@container ftd (max-width: 760px) { .ftd-facts--four { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@container ftd (max-width: 430px) { .ftd-facts, .ftd-facts--three, .ftd-facts--four { grid-template-columns: minmax(0, 1fr); } }
.ftd-fact { min-width: 0; }   /* F4 one cell: its label, then its value */
.ftd-fact--wide { grid-column: 1 / -1; }   /* F4 a cell that needs the whole row (a description, an address) */
.ftd .ftd-fact__label { display: flex; align-items: center; gap: 5px; font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); }   /* F4 the label. It is drawn whether or not there is a value */
.ftd-fact__label > i { font-size: .95rem; }   /* F4 the optional icon beside a label */
.ftd-fact__label[title] { cursor: help; border-bottom: 1px dotted var(--ftd-control-line); align-self: flex-start; }   /* F4 a label with a tooltip says so, rather than hiding it */
.ftd-fact__value { min-width: 0; color: var(--ftd-ink); overflow-wrap: break-word; }   /* F4 the value: plain words, a link, a badge, or built HTML */
.ftd .ftd-fact__value a { color: var(--ftd-accent); font-weight: 500; }   /* F4 a value that is a link (a folder, a supplier, an account) */
.ftd .ftd-fact__value a:hover { text-decoration: underline !important; }
.ftd-fact__value--none { color: var(--ftd-off-ink); }   /* F4 ABSENT: a muted dash, the label still there. This is the property the card boxes need */
.ftd-fact__value--num { font-variant-numeric: tabular-nums; }   /* F4 a figure, so a column of them lines up */
.ftd-fact__sub { display: block; font-size: 11.5px; color: var(--ftd-muted); }   /* F4 a second line under a value (a code under a name) */
.ftd-facts--table { display: block; gap: 0; }   /* F4 the two-column variant (today's `use_table`): label left, value right, one hairline a row */
.ftd-facts--table > .ftd-fact { display: grid; grid-template-columns: minmax(90px, 40%) minmax(0, 1fr); gap: 4px 14px; align-items: baseline; padding: 6px 0; border-bottom: 1px solid var(--ftd-hairline); }
.ftd-facts--table > .ftd-fact:last-child { border-bottom: 0; }
.ftd-facts--table .ftd-fact__value { text-align: right; }
@container ftdfacts (max-width: 320px) { .ftd-facts--table > .ftd-fact { grid-template-columns: minmax(0, 1fr); } .ftd-facts--table .ftd-fact__value { text-align: left; } }

/* ---------------------------------------------------------------- F5  THE MODAL SHELL
   One shell for the 135 b-modal tags in 127 files, the 147 hand-rolled footers and the four footer grammars. Title
   bar, scrolling body, STICKY FOOTER with one submit and one cancel. Three sizes, a side-sheet variant and a confirm
   variant. Each instance owns its own id (two files today hard-code the same one). In the mock the sheet is drawn
   open by `is-open`; the component uses whatever it uses. */
.ftd-scrim { position: fixed; inset: 0; z-index: 40; display: none; align-items: flex-start; justify-content: center; padding: 28px 14px; overflow-y: auto; background: rgba(15, 22, 33, .45); }
.ftd-scrim.is-open { display: flex; }
.ftd-scrim--right { align-items: stretch; justify-content: flex-end; padding: 0; }   /* F5 the scrim of a side sheet: the sheet comes to the right-hand edge */
/* THE SHELL DOES NOT CLIP, and that is the whole of the picker mend (2026-09-21).
   It used to carry `overflow: hidden`, which was doing two jobs at once. The obvious one is the 10px
   corner: the head and the foot declare no radius of their own, so the shell's clip is what rounded the
   coloured title bar and the inset footer. That job now belongs to the head and the foot themselves, two
   rules below. The second job was hiding the spill of a body that is not a scroller, and the body below
   keeps `overflow-y: auto` by default, so that job is still done by the body itself and nothing spills.
   WHY IT HAD TO GO. An open picker's list (vue-multiselect's `.multiselect__content-wrapper`, which is an
   absolutely positioned child, not a native select) was cut to the dialogue's edge: measured in the rig, a
   300px list showed 35.5px in a short dialogue. A BootstrapVue modal today lets that list lie over the
   dialogue's edge, and the design must too. Measured 2026-09-21: with this visible AND the body opted out
   with `.ftd-modal__body--overflow`, the same list shows 300 of 300px, by the sheet alone and under the
   site's five sheets alike. This rule alone is not enough (the body still clips: 74 of 300px) and the
   body's class alone is not enough either, so both are needed and both are the smallest each can be.
   THIS RULE ON ITS OWN CHANGES NO PAINTING. With the body scrolling by default, a dialogue that does not
   opt out has nothing that reaches the shell's edge, which is why every signed-off render is unmoved.
   `container-type` is NOT part of the fault and stays: measured one property at a time, a size container
   clips nothing and does not trap a `position: fixed` descendant (transform, filter, contain and
   will-change do that; the sheet uses none of them here). The `ftdmodal` container queries below still
   fire exactly as before. */
.ftd-modal { display: flex; flex-direction: column; width: 100%; max-width: 760px; max-height: 100%; overflow: visible; border-radius: 10px; background: var(--ftd-surface); box-shadow: 0 24px 70px rgba(0, 28, 56, .3); container-type: inline-size; container-name: ftdmodal; }   /* F5 the shell. It is a size container, so a fact grid inside it answers to the modal, not the screen */
.ftd-modal--sm { max-width: 440px; }   /* F5 small: a confirm */
.ftd-modal--lg { max-width: 1060px; }   /* F5 large: an action with sections (Key Details, Costs, Sell) */
.ftd-modal--sheet { max-width: 520px; height: 100%; max-height: 100%; border-radius: 0; }   /* F5 the side sheet: full height at the right, so the record stays visible behind it */
.ftd-modal__head { display: flex; align-items: center; gap: 12px; padding: 12px 18px; background: var(--ftd-primary); color: #fff; }   /* F5 the title bar, the same primary as a card's head */
.ftd-modal__title { flex: 1; min-width: 0; font-size: 14px; font-weight: 500; }   /* F5 ONE title grammar. The mocks write it out; the component decides the words once, not eleven times */
.ftd-modal__sub { display: block; font-size: 11.5px; font-weight: 400; opacity: .8; }   /* F5 what the action is about, under the title (the record's name) */
.ftd .ftd-modal__x { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 50%; background: rgba(255, 255, 255, .14); color: #fff; font-size: 1.15rem; cursor: pointer; }
.ftd .ftd-modal__x:hover { background: rgba(255, 255, 255, .28); }
/* THE BODY STILL SCROLLS BY DEFAULT, and the PICKER dialogue opts out (2026-09-21, corrected the same
   day). `overflow-y: auto` here is the clipper that bites an open picker: it cuts the list to the body's
   bottom edge, 300px of list down to 32px in the rig. Two mechanics make that unavoidable rather than
   tunable, both measured in the engine: any value but `visible` on one axis clips a child that overflows
   it, and `overflow-x: hidden; overflow-y: visible` cannot be written at all, because the engine computes
   the visible axis to `auto` the moment the other is not visible. So a body cannot both scroll and let a
   list hang outside it, and one of the two has to be the default.
   WHICH ONE IS THE DEFAULT, and why this way round. The first draft of this pass made `visible` the
   default and asked the shell to add a class to every dialogue that is too tall. Measured, that fails
   unsafely: a 60-row body in a --sm shell paints its last rows THROUGH the footer and out onto the
   scrim, and the scrim's scrollHeight goes 600 -> 1983px, so the whole page scrolls. A tall body is the
   common case and an open picker is the rare one, so the rare one opts out. Measured both ways in the
   rig, sheet alone and under the site's five sheets:
       default (this rule)                 tall body clipped and scrolling inside, scrim 420 = 420
       + .ftd-modal__body--overflow        picker list 300 of 300px visible
   The shell's `overflow: visible` above is still required for the picker: with the shell clipping and
   only this class on the body, the same list shows 74 of 300px. The two work together, and neither
   alone is enough. */
.ftd-modal__body { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 18px 18px; container-type: inline-size; container-name: ftdcard; }   /* F5 the body scrolls, the head and foot do not. It carries `ftdcard` so approved panels inside it size themselves */
.ftd-modal__foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 11px 18px; border-top: 1px solid var(--ftd-line); background: var(--ftd-inset); }   /* F5 the sticky footer: ONE submit, ONE cancel, and nothing hand-rolled */
.ftd-modal__foot > .ftd-modal__left { margin-right: auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }   /* F5 what sits at the left of a footer: a Delete, a count, a note */
.ftd-modal__foot > .ftd-btn, .ftd-modal__foot > .ftd-pill { margin-left: 0; }
.ftd-modal__foot--right { justify-content: flex-end; }
.ftd-modal__banner { display: flex; align-items: flex-start; gap: 8px; padding: 9px 18px; background: var(--ftd-warn-bg); color: var(--ftd-warn-ink); font-size: 12px; }   /* F5 the read-only banner: this record may be looked at and not changed, and why */
.ftd-modal__banner > i { font-size: 1.05rem; }
.ftd-modal__loading { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; min-height: 180px; color: var(--ftd-muted); }   /* F5 the body while it is fetching what it is about to show */
/* THE CORNER THE SHELL USED TO CLIP, now carried by the head and the foot themselves (2026-09-21).
   `inherit` and not `10px`, deliberately: the side sheet is `border-radius: 0`, so its head and foot
   inherit 0 and stay square, while --sm and --lg inherit the shell's 10px. Measured both ways.
   `:where(:first-child)` / `:where(:last-child)` holds these at (0,1,0), the same weight as every other
   class rule in this sheet, so a single class still beats them. Written plainly as `:first-child` they
   would be (0,1,1) and would beat one class, which is the guard's contract broken for the sake of a
   corner. The child test matters: a dialogue with a banner above its head, or a body under its foot,
   must not round an edge that is no longer the dialogue's own. */
.ftd-modal__head:where(:first-child) { border-top-left-radius: inherit; border-top-right-radius: inherit; }
.ftd-modal__foot:where(:last-child) { border-bottom-left-radius: inherit; border-bottom-right-radius: inherit; }
/* THE DIALOGUE THAT OPENS A PICKER opts out of the scroll, and only that one. A body that scrolls
   cannot also let a child hang outside it, so a dialogue whose body holds an open picker (vue-multiselect's
   `.multiselect__content-wrapper`, an absolutely positioned child) says so with this class and gives up
   scrolling in exchange. It is the rare case: put it on the body of a dialogue whose content fits the
   room it has AND which opens a picker. On a body that is also too tall it will paint through the footer,
   which is the trade the class buys and the reason it is not the default.
   THE SAFE DEFAULT IS THE RULE ABOVE, so a shell that has not been told about this class still behaves
   exactly as it did before this pass. Nothing regresses by omission. */
.ftd-modal__body--overflow { overflow: visible; }
/* A CONFIRM'S TWO BUTTONS SIT SIDE BY SIDE (2026-09-21). The footer stacks under 520px of CONTAINER
   width, and `.ftd-modal--sm` is 440px wide at every viewport, so 440 < 520 was always true and EVERY
   confirm on the site stacked Yes over No, at 1920 as at 390. That is against the walk's step 17: a
   confirm's buttons are side by side, and stack only on a phone. Recorded by T33a (ledger/early/T33a.md
   lines 087 and 111, EARLY_FINDINGS item 9).
   The small shell therefore opts back out of the stack and takes a floor of its own. The two labels
   ("No, Cancel" and "Yes, Delete It") need 191px, plus 44px of padding and gap.
   WHAT THE 300 IN THE QUERY BELOW ACTUALLY MEANS, measured a pixel at a time rather than reasoned: the
   footer flips to a row at a MODAL WIDTH of 303px, not 300. `@container` measures the query container's
   CONTENT box, and the shell's own padding puts the flip 3px above the written figure. The behaviour is
   right either way; the figure is the container's content box and not the dialogue's width, and it is
   written down here so nobody re-derives it from the modal's outer width and finds it off by three.
   IN PHONE WIDTHS, which is how the walk asks the question: the scrim's 28px of side padding means a
   360px phone gives the dialogue about 332px, so it is a row with roughly 29px to spare; a 320px phone
   gives about 292px and it stacks. Both measured. A medium or large dialogue squeezed under 520px keeps
   stacking exactly as before; only --sm changes.
   A FOOTER WITH A LEFT BLOCK (a count, a Delete) behaves the same: swept 240 to 420 by 1px with a
   `.ftd-modal__left` present, the footer is a single 55px row from 303 up and a stack below it, with no
   band of ragged wrapping in between.
   `:where(.ftd-modal--sm)` holds the qualifier at zero, so these stay (0,1,0) -- the same weight as the
   rules they overrule, winning on source order, and still losing to a single author class. Written
   plainly the qualifier would be (0,2,0) and would beat one class. A `:has(.ftd-confirm)` form was
   considered and rejected for the same reason: `:has()` is (0,2,0), and it keys on what the body
   contains rather than on the room the footer actually has. */
@container ftdmodal (max-width: 520px) {
    .ftd-modal__foot { flex-direction: column-reverse; align-items: stretch; }
    .ftd-modal__foot > .ftd-btn, .ftd-modal__foot > .ftd-pill { justify-content: center; }
    .ftd-modal__foot > .ftd-modal__left { margin-right: 0; justify-content: center; }
    :where(.ftd-modal--sm) > .ftd-modal__foot { flex-direction: row; align-items: center; }
    :where(.ftd-modal--sm) > .ftd-modal__foot > .ftd-modal__left { margin-right: auto; justify-content: flex-start; }
}
@container ftdmodal (max-width: 300px) {
    :where(.ftd-modal--sm) > .ftd-modal__foot { flex-direction: column-reverse; align-items: stretch; }
    :where(.ftd-modal--sm) > .ftd-modal__foot > .ftd-modal__left { margin-right: 0; justify-content: center; }
}
/* F12 THE CONFIRM. The same shell at its small size, with the question in the body and the two labels settled. */
.ftd-confirm { text-align: left; }
.ftd-confirm__icon { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; margin: 0 0 10px; border-radius: 50%; background: var(--ftd-bad-bg); color: var(--ftd-bad-ink); font-size: 1.5rem; }   /* F12 the mark that says this one destroys something */
.ftd-confirm__icon--warn { background: var(--ftd-warn-bg); color: var(--ftd-warn-ink); }   /* F12 the same mark where the action is reversible */
.ftd-confirm__what { color: var(--ftd-ink); font-size: 14px; }   /* F12 the question, with the record's name in it */
.ftd-confirm__what b { font-weight: 600; }
.ftd-confirm__extra { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--ftd-hairline); }   /* F12 the optional extra tick box ("Delete {name} even if not empty") */
.ftd-confirm__type { margin-top: 12px; }   /* F12 the type-DELETE variant: the submit stays disabled until the word is typed */
.ftd-confirm__type b { color: var(--ftd-bad-ink); font-weight: 600; letter-spacing: .06em; }

/* ---------------------------------------------------------------- F6  THE PAGE HEADER
   Breadcrumb, title, an optional status label and a right-hand action slot, plus the answer for the four live lists
   that render no header at all. Two treatments: the plain header, and the BAND variant, which is the approved
   .ftd-band shape and merges the header with the list's toolbar so a whole row of vertical space goes. */
.ftd-head { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 16px; margin: 0 0 16px; }
.ftd-head__text { flex: 1 1 260px; min-width: 0; }
.ftd-head__crumb { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; font-size: 12px; color: var(--ftd-muted); }   /* F6 the breadcrumb, above the title */
.ftd .ftd-head__crumb a:hover { color: var(--ftd-accent); text-decoration: underline !important; }
.ftd-head__crumb i { font-size: .95rem; opacity: .7; }
.ftd-head__title { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin: 1px 0 0; color: var(--ftd-ink); font-size: 20px; font-weight: 500; line-height: 1.25; }   /* F6 the title. It may carry a status label beside it */
.ftd .ftd-head__back { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; margin-right: 2px; border: 1px solid var(--ftd-line); border-radius: 50%; color: var(--ftd-primary); font-size: 1.15rem; }   /* F6 the back chevron on a detail page */
.ftd .ftd-head__back:hover { background: var(--ftd-hover); border-color: var(--ftd-accent); }
.ftd .ftd-head__sub { margin-top: 2px; font-size: 12px; color: var(--ftd-muted); }   /* F6 one quiet line under the title (a code, a count, a date) */
.ftd-head__actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; margin-left: auto; }   /* F6 the right-hand action slot. It wraps to its own line rather than squeezing the title */
.ftd-head--band { align-items: center; gap: 10px 16px; min-height: 74px; padding: 12px 18px; border-radius: 8px; border: 1px solid var(--ftd-accent-soft); background: linear-gradient(90deg, var(--ftd-accent-soft), rgba(255, 255, 255, 0) 85%); }   /* F6 the band variant: the approved band's own look, the header and the toolbar in one */
.ftd-head--band .ftd-head__title { font-size: 17px; }
.ftd-head__toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; flex: 1 1 240px; justify-content: flex-end; }   /* F6 the header's own controls where the header IS the toolbar: search, Show, Sort, the counter */
.ftd-search { display: flex; align-items: center; gap: 6px; height: 32px; min-width: 150px; max-width: 280px; flex: 1 1 170px; padding: 0 10px; border: 1px solid var(--ftd-control-line); border-radius: 16px; background: var(--ftd-surface); }   /* F6 the list search box, the agreed 32px, a pill like the buttons beside it */
.ftd-search:focus-within { border-color: var(--ftd-accent); box-shadow: 0 0 0 3px var(--ftd-focus); }
.ftd-search > i { flex: none; color: var(--ftd-muted); font-size: 1.05rem; }
.ftd .ftd-search input { flex: 1; min-width: 0; height: 30px; border: 0; padding: 0; background: transparent; color: var(--ftd-ink); font: inherit; }
.ftd .ftd-search input:focus { outline: none; }
@container ftd (max-width: 620px) {
    .ftd-head__actions { justify-content: flex-start; margin-left: 0; }
    .ftd-head__toolbar { justify-content: flex-start; }
}

/* ---------------------------------------------------------------- F7  THE TAB ROW
   One strip for the 35 copies and the five rival mechanisms. It takes its list as data, carries an optional count, and
   a hidden tab is simply not drawn. IT IS NEVER CSS-ONLY: every tab is a real link or button that still writes
   active_tab, because the panes are v-if and a CSS-only strip blanks every one of them.
   AT NARROW WIDTHS the strip does not squash and does not clip: it scrolls sideways on its own, with the chosen tab
   scrolled into view, and the two marks at its ends say there is more. */
.ftd-tabs { position: relative; display: flex; margin: 0 0 16px; border-bottom: 1px solid var(--ftd-line); overflow-x: auto; scrollbar-width: none; }
.ftd-tabs::-webkit-scrollbar { display: none; }
.ftd .ftd-tab { flex: none; display: inline-flex; align-items: center; gap: 7px; height: 38px; padding: 0 15px; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; background: transparent; color: var(--ftd-text); font: inherit; white-space: nowrap; cursor: pointer; }   /* F7 one tab. A real button or link, never a CSS target */
.ftd .ftd-tab:hover { color: var(--ftd-primary); background: var(--ftd-hover); }
.ftd .ftd-tab.is-on, .ftd .ftd-tab[aria-selected="true"] { color: var(--ftd-primary); border-bottom-color: var(--ftd-accent); font-weight: 500; }
.ftd-tab > i { font-size: 1.1rem; }
.ftd-tab__n { display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 18px; padding: 0 6px; border-radius: 9px; background: var(--ftd-inset); color: var(--ftd-muted); font-size: 10.5px; font-weight: 600; }   /* F7 the optional count on a tab */
.ftd-tab.is-on .ftd-tab__n, .ftd-tab[aria-selected="true"] .ftd-tab__n { background: var(--ftd-accent-soft); color: var(--ftd-primary); }
.ftd .ftd-tab.is-off { color: var(--ftd-off-ink); cursor: not-allowed; }   /* F7 a tab this user may not open, its reason on hover */
.ftd-tabs--fill .ftd-tab { flex: 1; justify-content: center; }   /* F7 the tabs sharing the width equally, which is what the detail pages do today */
.ftd-tabs__more { flex: none; margin-left: auto; position: sticky; right: 0; background: var(--ftd-surface); }   /* F7 the overflow control: the tabs that did not fit, in a menu, rather than a squashed strip */
@container ftd (max-width: 700px) { .ftd-tabs--fill .ftd-tab { flex: none; } }
.ftd-tabs--pills { border-bottom: 0; gap: 6px; flex-wrap: wrap; }   /* F7 the same row as pills, for a strip inside a card where a rule would read as the card's edge */
.ftd-tabs--pills .ftd-tab { height: 32px; border-bottom: 0; border-radius: 16px; border: 1px solid var(--ftd-control-line); }
.ftd .ftd-tabs--pills .ftd-tab.is-on { background: var(--ftd-primary); border-color: var(--ftd-primary); color: #fff; }
.ftd-tabs--pills .ftd-tab.is-on .ftd-tab__n { background: rgba(255, 255, 255, .22); color: #fff; }

/* ---------------------------------------------------------------- F8  THE SUMMARY PANEL
   The right-hand panel of every detail page: a badge band, the three-dots, figure tiles, an image slot with an ABSENT
   state, a titled fact block and the tags. Its own class, and a header-band variant for the option that puts the
   panel across the top and gives the sections below it the full width. */
.ftd-summary { display: flex; flex-direction: column; gap: 14px; padding: 14px; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); box-shadow: 0 5px 30px rgba(0, 28, 56, .06); container-type: inline-size; container-name: ftdcard; }
.ftd-summary__bar { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 6px; }   /* F8 the badge band across the top of the panel, with the three-dots at its right */
.ftd-summary__bar > .ftd-summary__menu { margin-left: auto; }
.ftd-summary__menu { position: relative; }   /* F8 where the panel's three-dots lives, so its menu is positioned to it */
.ftd-summary__name { color: var(--ftd-ink); font-size: 16px; font-weight: 500; line-height: 1.3; }   /* F8 the record's name inside the panel */
.ftd-summary__sub { font-size: 12px; color: var(--ftd-muted); }   /* F8 the line under it (the reference, the code) */
.ftd-summary__block { padding-top: 12px; border-top: 1px solid var(--ftd-hairline); }   /* F8 a titled block inside the panel, divided by a hairline, never by another box */
.ftd-summary__block:first-child { padding-top: 0; border-top: 0; }
.ftd .ftd-summary__title { margin: 0 0 7px; font-size: 10px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ftd-muted); }   /* F8 that block's heading, ABOVE its content */
.ftd-summary--band { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 10px 20px; }   /* F8 the header-band variant: the panel lies across the page, the facts below get the width */
.ftd-summary--band > .ftd-summary__block { flex: 1 1 200px; min-width: 0; padding-top: 0; border-top: 0; border-left: 1px solid var(--ftd-hairline); padding-left: 20px; }
.ftd-summary--band > .ftd-summary__block:first-child { border-left: 0; padding-left: 0; }
.ftd-summary--band > .ftd-summary__bar { flex: 1 1 100%; }
@container ftd (max-width: 720px) {
    .ftd-summary--band > .ftd-summary__block { flex-basis: 100%; padding-left: 0; padding-top: 12px; border-left: 0; border-top: 1px solid var(--ftd-hairline); }
}

/* ---------------------------------------------------------------- F9  THE DROPZONE and its preview tile
   Keyed on a class, not on #imagesgallery, so it works in all 38 places rather than one. Three states, and the tile
   with every input the mixin's HTML string carries today. The approved .ftd-postform__drop is the compact form of the
   same thing; this is its full form with the two buttons and the tiles beneath. */
.ftd-drop { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 130px; padding: 18px; border: 1px dashed var(--ftd-control-line); border-radius: 8px; background: var(--ftd-inset); color: var(--ftd-muted); text-align: center; }   /* F9 state 1, waiting: the cloud, the headline and the sub-line */
.ftd-drop > i { font-size: 2.4rem; color: var(--ftd-control-line); }
.ftd-drop__head { color: var(--ftd-ink); font-size: 14px; font-weight: 500; }   /* F9 "Drop files here or click to upload" */
.ftd-drop__sub { font-size: 12px; }   /* F9 "... or click to select a file from your computer" */
.ftd-drop.is-over { border-color: var(--ftd-accent); border-style: solid; background: var(--ftd-hover); color: var(--ftd-primary); }   /* F9 state 2, a file is over it */
.ftd-drop.is-over > i { color: var(--ftd-accent); }
.ftd-drop--bar { flex-flow: row wrap; align-items: center; justify-content: flex-start; gap: 6px 10px; min-height: 46px; padding: 8px 14px; text-align: left; }   /* F9 state 3, once something is queued the drop area shrinks to a bar and the tiles take the room. It wraps rather than crowding: at a narrow width the count and the buttons drop to a second line */
.ftd-drop--bar > i { font-size: 1.4rem; }
.ftd-drop--bar .ftd-drop__head { font-size: 13px; white-space: nowrap; }
.ftd-drop--bar .ftd-drop__sub { margin-left: auto; white-space: nowrap; }
@container ftdcard (max-width: 560px) { .ftd-drop--bar .ftd-drop__sub { margin-left: 0; } }
@container ftd (max-width: 560px) { .ftd-drop--bar .ftd-drop__sub { margin-left: 0; } }
.ftd-drop__actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }   /* F9 Add New Image and Remove All, under the area, never inside it */
.ftd-drop--bar .ftd-drop__actions { margin-top: 0; }
.ftd-tiles-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; margin-top: 12px; }   /* F9 the queued files, as many across as the room allows */
.ftd-ptile { display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); }   /* F9 one preview tile. Its inputs are the mixin's four, by their exact labels */
.ftd-ptile__img { position: relative; display: flex; align-items: center; justify-content: center; height: 120px; overflow: hidden; background: var(--ftd-inset); color: var(--ftd-control-line); font-size: 2rem; }
.ftd-ptile__img img { width: 100%; height: 100%; object-fit: cover; }
.ftd-ptile__mark { position: absolute; top: 6px; right: 6px; display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; font-size: 1.05rem; }   /* F9 the success tick or the error cross on the tile */
.ftd-ptile__mark--ok { background: var(--ftd-good-bg); color: var(--ftd-good-ink); }
.ftd-ptile__mark--bad { background: var(--ftd-bad-bg); color: var(--ftd-bad-ink); }
.ftd-ptile__body { flex: 1; display: flex; flex-direction: column; gap: 7px; padding: 10px; }
.ftd-ptile__body > .ftd-pill:last-child, .ftd-ptile__body > .ftd-btn:last-child { margin-top: auto; }   /* Remove file sits at the foot of every tile, so a row of tiles has one line of buttons */
.ftd-ptile__name { color: var(--ftd-ink); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }   /* F9 the file's own name, above the fields */
.ftd-ptile__size { font-size: 11.5px; color: var(--ftd-muted); }
.ftd-ptile__err { color: var(--ftd-bad-ink); font-size: 12px; }   /* F9 the error message the server sent back for this file */
.ftd-bar { height: 6px; overflow: hidden; border-radius: 3px; background: var(--ftd-line); }   /* F9 and F14: the progress bar, one shape everywhere */
.ftd-bar > span { display: block; height: 100%; border-radius: 3px; background: var(--ftd-accent); transition: width .2s; }
.ftd-bar--good > span { background: var(--ftd-good-ink); }
.ftd-bar--bad > span { background: var(--ftd-bad-ink); }
.ftd-bar--stripe > span { background-image: linear-gradient(45deg, rgba(255, 255, 255, .28) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .28) 50%, rgba(255, 255, 255, .28) 75%, transparent 75%); background-size: 10px 10px; }   /* F9 the same bar, striped, while it is moving */

/* ---------------------------------------------------------------- F10  THE STEPPER
   The approved .ftd-stepper / .ftd-sstep is the vertical one: a done step collapses to a 52px summary line with a
   Change link, and its body is hidden by CSS, which is what the big wizard's v-show fieldsets need. This adds the
   HORIZONTAL bar for the option that keeps today's shape, and the summary line for a done step in that bar. The
   step count is never hard-coded: the row divides by how many steps there are. */
.ftd-hsteps { display: flex; flex-wrap: wrap; gap: 2px; margin: 0 0 18px; padding: 0; overflow: hidden; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-line); }
.ftd .ftd-hstep { flex: 1 1 130px; display: flex; align-items: center; gap: 9px; min-width: 0; min-height: 52px; padding: 8px 12px; border: 0; background: var(--ftd-surface); color: var(--ftd-muted); font: inherit; text-align: left; cursor: pointer; }   /* F10 one step of the horizontal bar */
.ftd .ftd-hstep:hover { background: var(--ftd-hover); }
.ftd-hstep__no { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; background: var(--ftd-inset); color: var(--ftd-muted); font-size: 11.5px; font-weight: 600; }
.ftd-hstep__text { min-width: 0; }
.ftd-hstep__title { display: block; font-size: 10.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }   /* F10 the step's name */
.ftd-hstep__sum { display: block; font-size: 12px; color: var(--ftd-ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }   /* F10 what a done step decided, on its one line */
.ftd-hstep.is-open { color: var(--ftd-ink); }
.ftd-hstep.is-open .ftd-hstep__no { background: var(--ftd-primary); color: #fff; }
.ftd-hstep.is-open .ftd-hstep__title { color: var(--ftd-primary); }
.ftd-hstep.is-done .ftd-hstep__no { background: var(--ftd-good-bg); color: var(--ftd-good-ink); }
.ftd-hstep.is-todo { opacity: .55; cursor: not-allowed; }
.ftd-hstep.is-todo .ftd-hstep__sum { display: none; }
.ftd-hstep__change { flex: none; margin-left: auto; color: var(--ftd-accent); font-size: 11.5px; font-weight: 500; }   /* F10 the Change link on a done step */
.ftd-hstep:not(.is-done) .ftd-hstep__change { display: none; }
.ftd-hbody { padding: 0; }   /* F10 the one body the horizontal bar shows, below it */
/* F10 the done step's summary line on its own, for the option that folds a finished step in place on a long page */
.ftd-doneline { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 52px; padding: 8px 16px; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-inset); }
.ftd-doneline > i { flex: none; color: var(--ftd-good-ink); font-size: 1.2rem; }
.ftd-doneline__title { font-size: 10.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd-doneline__sum { min-width: 0; color: var(--ftd-ink); font-weight: 500; }
.ftd-doneline > .ftd-link { margin-left: auto; }

/* ---------------------------------------------------------------- F11  MESSAGES
   One delay, one title grammar, for the 277 toast call sites. The inline alert, the server warning banner, and the
   refusal-in-a-strip the Accounts list uses. The approved .ftd-note is the quiet inline aside; these are the loud ones. */
.ftd-toasts { position: fixed; z-index: 60; top: 14px; right: 14px; display: flex; flex-direction: column; gap: 8px; width: min(340px, calc(100vw - 28px)); }   /* F11 where toasts stack: top right, newest at the top, one width */
.ftd-toasts--bottom { top: auto; bottom: 14px; }   /* F11 the same stack at the foot, for the option that puts them there */
.ftd-toast { display: flex; align-items: flex-start; gap: 10px; padding: 11px 13px; border: 1px solid var(--ftd-line); border-left: 3px solid var(--ftd-accent); border-radius: 8px; background: var(--ftd-surface); box-shadow: 0 12px 34px rgba(0, 28, 56, .18); }   /* F11 one toast: a mark, a title, a line, a close */
.ftd-toast > i { flex: none; color: var(--ftd-accent); font-size: 1.2rem; }
.ftd-toast__text { flex: 1; min-width: 0; }
.ftd-toast__title { display: block; color: var(--ftd-ink); font-weight: 600; }   /* F11 the title. One grammar: "Saved", not "Success" beside "Success!" */
.ftd-toast__line { display: block; font-size: 12px; color: var(--ftd-text); overflow-wrap: break-word; }
.ftd .ftd-toast__x { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border: 0; border-radius: 50%; background: transparent; color: var(--ftd-muted); font-size: 1.05rem; cursor: pointer; }
.ftd .ftd-toast__x:hover { background: var(--ftd-inset); color: var(--ftd-ink); }
.ftd-toast--good { border-left-color: var(--ftd-good-ink); }
.ftd-toast--good > i { color: var(--ftd-good-ink); }
.ftd-toast--bad { border-left-color: var(--ftd-bad-ink); }
.ftd-toast--bad > i { color: var(--ftd-bad-ink); }
.ftd-alert { display: flex; align-items: flex-start; gap: 9px; margin: 0 0 14px; padding: 10px 14px; border: 1px solid var(--ftd-accent-soft); border-radius: 8px; background: var(--ftd-hover); color: var(--ftd-text); }   /* F11 the inline alert: information the page needs to state, in place, not in a toast */
.ftd-alert > i { flex: none; color: var(--ftd-accent); font-size: 1.15rem; }
.ftd-alert__text { min-width: 0; }
.ftd-alert__text b { color: var(--ftd-ink); font-weight: 600; }
.ftd-alert > .ftd-btnrow, .ftd-alert__text > .ftd-btnrow { margin-top: 8px; }   /* F11 an alert may carry its own buttons (the import's three prompt blocks do) */
.ftd-alert--good { border-color: #cfe6d1; background: #f1f8f2; }
.ftd-alert--good > i { color: var(--ftd-good-ink); }
.ftd-alert--warn { border-color: var(--ftd-warn-line); background: var(--ftd-warn-bg); color: var(--ftd-warn-ink); }
.ftd-alert--warn > i { color: var(--ftd-warn-ink); }
.ftd-alert--bad { border-color: #f1cccc; background: #fdf1f1; }
.ftd-alert--bad > i { color: var(--ftd-bad-ink); }
.ftd-alert--bad .ftd-alert__text b { color: var(--ftd-bad-ink); }
.ftd-banner { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; margin: 0 0 14px; padding: 9px 16px; border-radius: 8px; background: var(--ftd-warn-bg); color: var(--ftd-warn-ink); }   /* F11 the server warning banner: one countdown and one wording, not five */
.ftd-banner > i { font-size: 1.2rem; }
.ftd-banner__count { margin-left: auto; font-size: 11.5px; font-variant-numeric: tabular-nums; opacity: .8; }   /* F11 its countdown, so the same number is not written five ways */
.ftd .ftd-banner__x { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border: 0; border-radius: 50%; background: rgba(0, 0, 0, .06); color: inherit; font-size: 1.05rem; cursor: pointer; }
.ftd-strip { display: flex; align-items: flex-start; gap: 8px; margin: 0 0 12px; padding: 8px 13px; border-left: 3px solid var(--ftd-bad-ink); border-radius: 0 6px 6px 0; background: #fdf1f1; color: var(--ftd-bad-ink); font-size: 12px; }   /* F11 the refusal-in-a-strip: a server said no, and the page says so where the action was */
.ftd-strip > i { flex: none; font-size: 1.05rem; }

/* ---------------------------------------------------------------- F13  THE EMPTY STATE
   One shape for the 111 files with a bare sentence, and the slot for the obvious next action they all lack. Its four
   live shapes are modifiers, not four different blocks. */
.ftd-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 30px 20px; text-align: center; color: var(--ftd-muted); }
.ftd-empty > i { font-size: 2.4rem; color: var(--ftd-control-line); }   /* F13 the icon; the page says which one */
.ftd-empty__line { color: var(--ftd-ink); font-size: 14px; }   /* F13 the sentence, as the site words it today */
.ftd-empty__sub { font-size: 12px; }   /* F13 one more line where the sentence needs it */
.ftd-empty > .ftd-btnrow { margin-top: 6px; }   /* F13 THE ACTION SLOT: what to do about it, which today's 111 sentences do not offer */
.ftd-empty--sm { padding: 16px 12px; }   /* F13 inside a panel or a card, where 30px of padding would be most of the box */
.ftd-empty--sm > i { font-size: 1.7rem; }
.ftd-empty--sm .ftd-empty__line { font-size: 13px; }
.ftd-empty--soon { color: var(--ftd-warn-ink); }   /* F13 the never-built variant: this page is not here yet, and says so */
.ftd-empty--soon > i { color: var(--ftd-warn-ink); opacity: .55; }
.ftd-empty--refused > i { color: var(--ftd-bad-ink); opacity: .6; }   /* F13 permission refused, drawn in place of the content rather than lost in a redirect */
.ftd-empty--refused .ftd-empty__line { color: var(--ftd-bad-ink); }

/* ---------------------------------------------------------------- F14  THE LOADING STATE
   A spinner with a named line ("Fetching Assets..."), skeleton rows for a table, and an inline busy bar. */
.ftd-loading { display: flex; align-items: center; justify-content: center; gap: 9px; padding: 26px 16px; color: var(--ftd-muted); }   /* F14 the spinner line. It names what it is fetching */
.ftd-loading > .ftd-spin { display: inline-block; width: 16px; height: 16px; color: var(--ftd-accent); }
.ftd-loading--block { flex-direction: column; min-height: 160px; }   /* F14 the same, filling a panel or a modal body */
.ftd-skel { display: block; height: 12px; border-radius: 6px; background: linear-gradient(90deg, var(--ftd-inset) 25%, var(--ftd-hairline) 37%, var(--ftd-inset) 63%); background-size: 400% 100%; animation: ftd-skel 1.4s ease-in-out infinite; }   /* F14 one skeleton bone; a width on it says how long */
@keyframes ftd-skel { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) { .ftd-skel { animation: none; } }
.ftd-skel--line { height: 10px; }
.ftd-skel--title { height: 15px; }
.ftd-skel--box { height: 40px; border-radius: 6px; }
.ftd-skelrows { border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); }   /* F14 the skeleton table: the rows a list is about to show, at their real heights */
.ftd-skelrow { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) 90px; gap: 0 14px; align-items: center; min-height: 46px; padding: 6px 18px; border-bottom: 1px solid var(--ftd-hairline); }
.ftd-skelrow:last-child { border-bottom: 0; }
.ftd-busy { position: relative; overflow: hidden; height: 3px; border-radius: 2px; background: var(--ftd-line); }   /* F14 the inline busy bar: something is happening and how long is unknown */
.ftd-busy::after { content: ""; position: absolute; inset: 0 auto 0 0; width: 40%; border-radius: 2px; background: var(--ftd-accent); animation: ftd-busy 1.2s ease-in-out infinite; }
@keyframes ftd-busy { 0% { transform: translateX(-100%); } 100% { transform: translateX(250%); } }

/* ---------------------------------------------------------------- F15  BADGES, PILLS, CHIPS
   The approved .ftd-badge (--grey --red --amber) and .ftd-chip (--export --made --matched) are the badge and the tag
   chip. This adds THE STATUS PILL'S SCALE, which is the atom copied into 46 files, the count badge, and the colour
   swatch, which is copied into 20. The scale answers the eleven sustainability states from the four theme variables
   and the neutral good / bad / warn tokens: eleven states, six looks, no eleventh colour invented. */
.ftd-pillstat { display: inline-flex; align-items: center; gap: 5px; padding: 2px 10px; border-radius: 11px; background: var(--ftd-inset); color: var(--ftd-text); font-size: 11.5px; font-weight: 500; white-space: nowrap; }   /* F15 the status pill: today's `button_size`, as a class rather than a JS string */
.ftd-pillstat > i { font-size: .95rem; }
.ftd-pillstat--on { background: var(--ftd-good-bg); color: var(--ftd-good-ink); }   /* F15 a good, settled state (Active, Completed, Approved, Kept) */
.ftd-pillstat--off { background: #e9ecef; color: #5c6670; }   /* F15 a finished or inactive state (Ended, Not started, Cancelled) */
.ftd-pillstat--work { background: var(--ftd-accent-soft); color: var(--ftd-primary); }   /* F15 in hand: the theme's own accent (In Progress, Planning, Proposed) */
.ftd-pillstat--wait { background: var(--ftd-warn-bg); color: var(--ftd-warn-ink); }   /* F15 waiting on somebody (Pending, Awaiting Approval, Locked) */
.ftd-pillstat--bad { background: var(--ftd-bad-bg); color: var(--ftd-bad-ink); }   /* F15 gone wrong (Failed, Rejected, Overdue) */
.ftd-pillstat--none { background: transparent; color: var(--ftd-muted); box-shadow: inset 0 0 0 1px var(--ftd-line); }   /* F15 no state recorded at all, which is not the same as a bad one */
.ftd-countbadge { display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 18px; padding: 0 6px; border-radius: 9px; background: var(--ftd-accent); color: #fff; font-size: 10.5px; font-weight: 600; }   /* F15 the count badge: the bell's number, a tab's number when it must stand out */
.ftd-countbadge--quiet { background: var(--ftd-inset); color: var(--ftd-muted); }
.ftd-swatch { display: inline-flex; align-items: center; gap: 6px; }   /* F15 the colour swatch: a colour's name and the colour itself (20 files build this in JS today) */
.ftd-swatch__bar { flex: none; width: 26px; height: 14px; border-radius: 3px; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12); }   /* the colour is data: the mock and the component set it inline, which is the one place an inline colour is right */
.ftd-swatch__name { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ftd-mode { display: inline-flex; align-items: center; gap: 5px; padding: 1px 9px; border-radius: 4px; background: var(--ftd-primary-soft); color: var(--ftd-primary); font-size: 10.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }   /* F15 the mode badge: what kind of thing this is (IMPORT, EXPORT, DEMO) */

/* ---------------------------------------------------------------- F16  THE MENU
   The approved .ftd-menu is the body, and .ftd-table__more the trigger inside a table. One trigger for the four styles
   in use, wherever it stands, and the disabled-with-a-reason state which exists in exactly three good places today
   and nowhere else. The menu is never clipped: its host is the positioning context and nothing around it hides
   overflow; near the foot of the page it flips up with .ftd-menu--up. */
.ftd-menuwrap { position: relative; display: inline-flex; }   /* F16 the one thing a menu needs around it: a positioning context that does not clip */
.ftd .ftd-dots { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0; border: 1px solid transparent; border-radius: 50%; background: transparent; color: var(--ftd-primary); font-size: 1.25rem; cursor: pointer; transition: all .15s; }   /* F16 THE three-dots trigger. One size, one look, on a row, a card, a panel and a header alike */
.ftd .ftd-dots:hover, .ftd .ftd-dots[aria-expanded="true"] { border-color: var(--ftd-accent-soft); background: var(--ftd-surface); color: var(--ftd-accent); }
.ftd .ftd-dots--onbar { color: #fff; }   /* F16 the same trigger on a coloured band or a panel head */
.ftd .ftd-dots--onbar:hover, .ftd .ftd-dots--onbar[aria-expanded="true"] { border-color: rgba(255, 255, 255, .3); background: rgba(255, 255, 255, .16); color: #fff; }
.ftd-menu--here { top: calc(100% + 4px); right: 0; }   /* F16 a menu hung off .ftd-menuwrap rather than off a table row */
.ftd-menu--here.ftd-menu--up { top: auto; bottom: calc(100% + 4px); }
.ftd-menu--left, .ftd-menu--here.ftd-menu--left { right: auto; left: 0; }   /* F16 a menu that opens to the RIGHT of its trigger, for a trigger at the left edge of its row; the default opens to the left, for a trigger at the right */
.ftd .ftd-menu button { display: flex; align-items: center; gap: 9px; width: 100%; padding: 6px 14px; border: 0; background: transparent; color: var(--ftd-ink); font: inherit; text-align: left; cursor: pointer; }   /* F16 an entry that acts rather than navigates; it looks identical to the approved link entry */
.ftd .ftd-menu button i { font-size: 1.05rem; color: var(--ftd-muted); }
.ftd .ftd-menu button:hover { background: var(--ftd-hover); }
.ftd .ftd-menu button.is-danger, .ftd .ftd-menu button.is-danger i { color: var(--ftd-bad-ink); }
.ftd .ftd-menu a.is-off.is-off, .ftd .ftd-menu button.is-off { color: var(--ftd-off-ink); cursor: not-allowed; }   /* the class is doubled because THE TABLE declares this selector too, after this section, at the same weight */
.ftd .ftd-menu .is-off i { color: var(--ftd-off-ink); }
.ftd-menu__why { display: block; margin-left: auto; padding-left: 8px; color: var(--ftd-muted); font-size: 11px; font-style: italic; white-space: nowrap; }   /* F16 THE REASON an entry is disabled, written beside it rather than only in a tooltip */
.ftd-menu__empty { padding: 10px 14px; color: var(--ftd-muted); font-size: 12px; }   /* F16 a menu with nothing in it for this user, which today renders as an empty white box */

/* ---------------------------------------------------------------- F17  THE FIGURE TILE
   The approved .ftd-stat and .ftd-count are the linked figure and the small count box. This adds the quantity trio's
   own colours (total, available, reserved, copied into six files today) and the net-over-gross pair, which is a
   money cell with two figures and a tooltip on each. The currency word comes from the money rules, not from here. */
.ftd-qty { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }   /* F17 the quantity trio, always the same three in the same three places */
.ftd-qty__box { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px; min-height: 46px; padding: 6px 8px; border: 1px solid var(--ftd-line); border-radius: 7px; background: var(--ftd-surface); }
.ftd-qty__n { font-size: 17px; font-weight: 600; line-height: 1.15; color: var(--ftd-ink); font-variant-numeric: tabular-nums; }
.ftd-qty__label { font-size: 9.5px; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); text-align: center; }
.ftd-qty__box--total { border-color: var(--ftd-accent-soft); background: var(--ftd-hover); }   /* F17 Total: the theme's accent, tinted */
.ftd-qty__box--total .ftd-qty__n { color: var(--ftd-primary); }
.ftd-qty__box--available { border-color: #cfe6d1; background: #f1f8f2; }   /* F17 Available */
.ftd-qty__box--available .ftd-qty__n { color: var(--ftd-good-ink); }
.ftd-qty__box--reserved { border-color: var(--ftd-warn-line); background: var(--ftd-warn-bg); }   /* F17 Reserved */
.ftd-qty__box--reserved .ftd-qty__n { color: var(--ftd-warn-ink); }
.ftd-money { display: inline-flex; flex-direction: column; line-height: 1.25; }   /* F17 the net-over-gross pair: net first because it is the one people read, gross beneath it */
.ftd-money__net { color: var(--ftd-ink); font-weight: 500; font-variant-numeric: tabular-nums; }
.ftd-money__gross { font-size: 11.5px; color: var(--ftd-muted); font-variant-numeric: tabular-nums; }
.ftd-money__gross[title], .ftd-money__net[title] { cursor: help; }   /* F17 each carries its own tooltip, which today is an inert v-b-tooltip inside a string */
.ftd-money--none { color: var(--ftd-off-ink); }   /* F17 a money cell with nothing in it: a dash, not an empty string */

/* ---------------------------------------------------------------- F18  THE PAGER
   Page numbers, the "Items 1-10 of 424" counter and the Show control beside it, in one row, for the 75 files that
   hand-write a pager today. The counter is the approved .ftd-settings__count's wording; this is the row at the foot. */
.ftd-pager { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin: 16px 0 0; }
.ftd-pager__count { font-size: 12px; color: var(--ftd-muted); }   /* F18 "Items 1-10 of 424", the same words as the toolbar's */
.ftd-pager__count b { font-weight: 600; color: var(--ftd-ink); }
.ftd-pager__pages { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin-left: auto; }
.ftd .ftd-page { display: inline-flex; align-items: center; justify-content: center; min-width: 30px; height: 30px; padding: 0 8px; border: 1px solid var(--ftd-line); border-radius: 6px; background: var(--ftd-surface); color: var(--ftd-text); font: inherit; font-size: 12px; cursor: pointer; }   /* F18 one page number, or a step arrow */
.ftd .ftd-page:hover { border-color: var(--ftd-accent); background: var(--ftd-hover); }
.ftd .ftd-page.is-on { border-color: var(--ftd-primary); background: var(--ftd-primary); color: #fff; cursor: default; }
.ftd .ftd-page[disabled] { color: var(--ftd-off-ink); cursor: not-allowed; }
.ftd .ftd-page[disabled]:hover { border-color: var(--ftd-line); background: var(--ftd-surface); }
.ftd-page--gap { border: 0; background: transparent; color: var(--ftd-muted); cursor: default; }   /* F18 the break in a long run of page numbers */
.ftd-pager__show { display: flex; align-items: center; gap: 6px; }   /* F18 the Show control, beside the numbers rather than in a second row */
.ftd-pager__show > span { font-size: 10px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd .ftd-pager__show select { height: 28px; padding: 0 4px; border: 1px solid var(--ftd-control-line); border-radius: 6px; background: var(--ftd-surface); color: var(--ftd-text); font: inherit; font-size: 11.5px; }
@container ftd (max-width: 520px) { .ftd-pager__pages { margin-left: 0; } }

/* ---------------------------------------------------------------- F19  THE AVATAR
   The approved .ftd-avatar is 36px with .ftd-avatar--sm at 24px and .ftd-avatar--system for a faceless one. This adds
   the third size, the logo form and the fall-back image, so the top bar, a picker option row and the supplier page
   all use one avatar. */
.ftd-avatar--lg { width: 52px; height: 52px; font-size: 16px; }   /* F19 the large size: a profile page, a supplier's header */
.ftd-avatar--logo { background: var(--ftd-surface); box-shadow: inset 0 0 0 1px var(--ftd-line); overflow: hidden; }   /* F19 a customer's logo instead of initials; the box stays the same size and shape */
.ftd-avatar--logo > img { width: 100%; height: 100%; object-fit: contain; }
.ftd-avatar--photo { overflow: hidden; }   /* F19 a photograph, cropped to the circle */
.ftd-avatar--photo > img { width: 100%; height: 100%; object-fit: cover; }
.ftd-avatar--square { border-radius: 7px; }   /* F19 the same avatar as a rounded square, for a thing rather than a person */

/* ---------------------------------------------------------------- F20  THE IMAGE TILE
   A carousel with a pager, the five per-image buttons (Edit info, Crop, Full image, Show media, Delete), a caption,
   and an ABSENT state that collapses rather than leaving a full-width grey band. The approved .ftd-gallery and
   .ftd-gallery__placeholder are the frame and the empty state; this is the carousel inside it. */
.ftd-carousel { position: relative; overflow: hidden; border-radius: 6px; background: var(--ftd-inset); }
.ftd-carousel__stage { display: flex; align-items: center; justify-content: center; min-height: 190px; color: var(--ftd-control-line); font-size: 2.6rem; }
.ftd-carousel__stage > img { display: block; width: 100%; max-height: 320px; object-fit: cover; }
.ftd-carousel__pager { position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 5px; padding: 4px 8px; border-radius: 11px; background: rgba(15, 22, 33, .5); }   /* F20 the pager: which of how many, and the two arrows */
.ftd .ftd-carousel__dot { width: 7px; height: 7px; padding: 0; border: 0; border-radius: 50%; background: rgba(255, 255, 255, .5); cursor: pointer; }
.ftd .ftd-carousel__dot.is-on { background: #fff; }
.ftd .ftd-carousel__arrow { position: absolute; top: 50%; transform: translateY(-50%); display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: 50%; background: rgba(15, 22, 33, .45); color: #fff; font-size: 1.2rem; cursor: pointer; }
.ftd .ftd-carousel__arrow:hover { background: rgba(15, 22, 33, .7); }
.ftd-carousel__arrow--prev { left: 8px; }
.ftd-carousel__arrow--next { right: 8px; }
.ftd-carousel__n { position: absolute; top: 8px; right: 8px; padding: 1px 8px; border-radius: 9px; background: rgba(15, 22, 33, .5); color: #fff; font-size: 11px; }   /* F20 "3 / 12", so the count is readable without counting dots */
.ftd-carousel--none { display: none; }   /* F20 ABSENT: no images, so the carousel is not there at all. Today this is a grey band the width of the panel */
.ftd-itile { display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); }   /* F20 one image in a grid, with its facts and its five buttons */
.ftd-itile__img { display: flex; align-items: center; justify-content: center; height: 150px; overflow: hidden; background: var(--ftd-inset); color: var(--ftd-control-line); font-size: 2rem; cursor: zoom-in; }
.ftd-itile__img > img { width: 100%; height: 100%; object-fit: cover; }
.ftd-itile__body { flex: 1; display: flex; flex-direction: column; gap: 8px; padding: 10px; }   /* it takes the room left over, so the buttons below it sit at the foot of every tile in the row */
.ftd-itile__cap { color: var(--ftd-ink); font-weight: 500; overflow-wrap: break-word; }   /* F20 the caption: the image's own title */
.ftd-itile__acts { display: flex; flex-wrap: wrap; gap: 5px; padding: 8px 10px; border-top: 1px solid var(--ftd-hairline); background: var(--ftd-inset); }   /* F20 the five buttons, on their own strip at the foot of the tile */

/* ---------------------------------------------------------------- LAYOUT HELPERS used by more than one primitive */
.ftd-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 340px); gap: 20px; align-items: start; }   /* the detail page's two columns: the content, then the summary panel */
.ftd-two--sticky > :last-child { position: sticky; top: 12px; }   /* the summary panel stays in view while the sections scroll */
@container ftd (max-width: 900px) { .ftd-two { grid-template-columns: minmax(0, 1fr); } .ftd-two--sticky > :last-child { position: static; } }
.ftd-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 28px; }   /* a form's two columns of sections, which become one when the room goes */
@container ftd (max-width: 820px) { .ftd-cols { grid-template-columns: minmax(0, 1fr); } }
.ftd-sheet { padding: 18px 20px 20px; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); box-shadow: 0 5px 30px rgba(0, 28, 56, .06); container-type: inline-size; container-name: ftdcard; }   /* ONE SURFACE for a page of flat sections: the card without a coloured head, which is what the flat treatment needs */
.ftd-stack > * + * { margin-top: 16px; }   /* things in a column, evenly apart, without a margin on each */
.ftd-stack--tight > * + * { margin-top: 10px; }
.ftd-rowline { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; }   /* a line of things that wraps rather than clips */
.ftd-rowline--end { justify-content: flex-end; }
.ftd-sep { height: 1px; margin: 16px 0; background: var(--ftd-line); }   /* a hairline between two things on one surface */
.ftd-scroll { max-height: 260px; overflow-y: auto; }   /* a long list that scrolls inside its own height instead of pushing the page down */
.ftd-nowrap { white-space: nowrap; }
.ftd-truncate { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ftd-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }   /* for a screen reader only: the text under an icon that has no words */

/* ---------------------------------------------------------------- CORNERS: ONE CENTRAL SETTING
   Tristian, 2026-09-19, on seeing a whole page of these: "everything is very round ... this was never part of an approved
   design"; "the form elements seem to have the right level of border-radius". The pill corner (16px on a 32px button) came
   from the Module Dashboard's band buttons, where there were a handful; carried to every button, tile, chip and tab it is
   too much. So the corner is ONE variable, and it is the form field's own 6px, which is what Tristian chose (Log #355).
     --ftd-corner     buttons, pills, tiles, tags, the search box, pill tabs
     --ftd-corner-sm  the small things: multiselect chips, status pills, the carousel pager
   Circles stay circles (avatars, count badges, the toggle, the radio). Cards and inputs already use 6px and 8px.
   The walk's three settings behind html[data-corners] are gone: the application ships this one value, and the literal
   radii the sheet declared above (.ftd-btn, .ftd-pill, .ftd-tag, .ftd-chip, .ftd-choice, .ftd-iconbtn) now carry the
   variable where they are declared, so nothing here overrules anything. */
.ftd { --ftd-corner: 6px; --ftd-corner-sm: 4px; }
.ftd .ftd-tabs .ftd-tab, .ftd .ftd-tab { border-radius: 0; }   /* an underline tab has no corners */
.ftd .ftd-btn, .ftd .ftd-pill, .ftd .ftd-tile, .ftd .ftd-search, .ftd .ftd-tabs--pills .ftd-tab, .ftd .ftd-tag, .ftd .ftd-tag--face, .ftd .ftd-chip, .ftd .ftd-choice, .ftd .ftd-run, .ftd .ftd-iconbtn { border-radius: var(--ftd-corner); }
/* AN UNDERLINE TAB HAS NO CORNERS. Its selected state is a straight rule along the bottom, and a radius curls the ends of that
   rule upwards (Tristian, 2026-09-19: "it just looks like a broken design"). Only the PILL tab is a shape that has a corner. */
/* (the rule itself sits just ABOVE the corners list, so the pill tab, named in that list, still wins by order) */
.ftd .ftd-multi__chip, .ftd .ftd-pillstat, .ftd .ftd-carousel__pager { border-radius: var(--ftd-corner-sm); }
/* .ftd-tag and .ftd-choice are declared AGAIN in the section BULK ACTIONS, which may sit after this one (it splices
   itself in just before THE TABLE, as this section does), each at the same weight as the list above. Order cannot be
   relied on between the two, so the chosen corner is written here once more at a weight that wins wherever it stands.
   It is the same 6px: this is a tie-break, not a second opinion. */
.ftd .ftd-tag.ftd-tag, .ftd .ftd-choice.ftd-choice { border-radius: var(--ftd-corner); }

/* ---------------------------------------------------------------- THE MARGIN GUARD, and the one rule it was hiding
   The guard at the head of this sheet is now `.ftd :where(h1..h6, p, dl, dt, dd, ...)`, which is (0,1,0): it beats
   the site's Bootstrap reboot at (0,0,1), and a single-class margin on one of those tags still wins, on source
   order, as a reader expects. Written plainly as `.ftd dl` it was (0,1,1) and cancelled such margins silently, 31
   times over during the walk (find_cancelled_margins.py, re-run 2026-09-20; an earlier draft said 63, which the
   script has never printed); written with :where() on BOTH halves it was (0,0,0) and lost to the site outright.
   Both of those are fixed; what follows was true under every version and stays true, because the guard has never
   beaten a single class.
   THE TABLE's `.ftd-table__detail` is the one rule in this sheet caught by the ORIGINAL (0,1,1) reset. It is drawn
   on a <dl> 148 times and on a <div> 9 times, and under that reset the same class rendered two ways: the 8px under
   a div, nothing under a dl, because it beat the class on that one tag. Under the guard the dl would gain the 8px
   and move renders Tristian has approved. THE TABLE is rewritten from its own file by build_table_mock.py and must
   not be edited, so the margin is held at what the approved render actually draws, here, at a weight that wins
   wherever the sections fall. The signed-off render is the truth, not the rule's intention: one margin for both
   tags is a change to ask for, not one to slip in with a merge. */
.ftd dl.ftd-table__detail { margin: 0; }   /* on a <dl> the original reset cancelled it, and the approved render has it flush */
@container ftdtable (max-width: 480px) { .ftd dl.ftd-table__detail { margin-right: 0; } }   /* the narrow block's margin-right was cancelled the same way */

/* ---------------------------------------------------------------- WEIGHT: HOW STRONGLY A HEADING AND A SURFACE ARE DRAWN
   Tristian, 2026-09-19, on the kit: "the contrasts and designs are not consistent ... very passive views ... hard to decipher
   the differences between headings and contents, and some sections/backgrounds are so feint its hard to tell whether they are
   one section or 2 ... then immediately below it we've got high contrast modals ... arguably too strong and do not sit well
   alongside".
   THE CAUSE: two heading grammars on one page. The modal kept the approved card's primary band; the section was drawn as a
   quiet flat panel whose title (11px capitals) was almost the same thing as a field label (10px capitals).
   THE RULE NOW: a card head, a section head and a modal head are ONE grammar, and it is BALANCED, which is what Tristian
   chose (Log #355): heads on the inset grey with the title in ink, frames on the real line colour. The walk's three settings
   behind html[data-weight] are gone, with the rules that existed only for strong and for quiet; .ftd-card__head carries the
   same variables where it is declared above, so nothing here overrules it.
   ALWAYS: a section's title is clearly bigger and darker than a field's label, a value is ink at 500, a label is a darker
   grey than the help text, and a frame is drawn on --ftd-line, never on the hairline, so two sections can be told apart. */
.ftd {
    --ftd-label: #66707d;                 /* a field's label: darker than --ftd-muted, which stays for help text and asides */
    --ftd-head-bg: var(--ftd-inset);      /* balanced: the one value the application ships */
    --ftd-head-ink: var(--ftd-ink);
    --ftd-head-sub: var(--ftd-text);
    --ftd-head-line: var(--ftd-line);
    --ftd-head-x-bg: rgba(0, 28, 56, .07);
}

/* one grammar: the approved card's head, a boxed section's head, a modal's head */
.ftd .ftd-sec--boxed > .ftd-sec__head, .ftd .ftd-sec--boxed > .ftd-sec__toggle, .ftd .ftd-modal__head { background: var(--ftd-head-bg); color: var(--ftd-head-ink); border-bottom: 1px solid var(--ftd-head-line); }
.ftd .ftd-modal__title { font-size: 15px; font-weight: 600; color: var(--ftd-head-ink); }
.ftd .ftd-modal__sub { opacity: 1; color: var(--ftd-head-sub); }
.ftd .ftd-modal__x { background: var(--ftd-head-x-bg); color: var(--ftd-head-ink); }
.ftd .ftd-modal__x:hover { background: var(--ftd-head-x-bg); filter: brightness(.9); }
.ftd .ftd-sec--boxed > .ftd-sec__head .ftd-sec__title, .ftd .ftd-sec--boxed > .ftd-sec__toggle .ftd-sec__title { color: var(--ftd-head-ink); }

/* a section's title is a HEADING, not another label */
.ftd .ftd-sec__title { font-size: 12.5px; letter-spacing: .06em; color: var(--ftd-ink); }
/* THE HEADING INSIDE A FORM BOX, 2026-09-21. Tristian's rule, and the design walk's r2: a box heading in a
   FORM is sentence case, 14px, weight 600, so it cannot be mistaken for the small uppercase field label
   beneath it. The small capitals ABOVE are deliberately kept for the detail pages, for the reason written
   against "the nine that want their own value" in the guard: restyling .ftd-sec__title centrally broke
   dashboard-mock by hundreds of thousands of pixels. So this is an OPT-IN modifier on the SECTION and
   changes nothing until a page wears it. No signed-off page does.
   WHAT IT HAS TO BEAT, each one checked by grepping this file for the property on this class, and the whole
   set measured in the engine 2026-09-21:
     text-transform  the guard's uppercase half, .ftd .ftd-sec__title, (0,2,0)
     font-size       .ftd .ftd-sec__title just above, (0,2,0), and the base rule at (0,1,0)
     font-weight     the base rule only, (0,1,0)
     letter-spacing  .ftd .ftd-sec__title just above, (0,2,0)
     colour          the guard's .ftd .ftd-sec__title, (0,2,0) + !important, and inside a boxed section
                     .ftd .ftd-sec--boxed > .ftd-sec__head .ftd-sec__title, (0,3,0) + !important
   (0,3,0) beats every one of those on the four visual properties without !important, which is why this is
   written with the page scope's own shape. THE COLOUR IS THE EXCEPTION AND IT COSTS NOTHING: an !important
   cannot be beaten by a normal declaration at any weight, so the guard still decides the colour here. It
   decides it as --ftd-head-ink inside a box and --ftd-ink outside, and --ftd-head-ink IS var(--ftd-ink)
   (declared above), so the colour this rule asks for is the colour that is painted -- measured rgb(43,52,64)
   in all three cases. The declaration is kept so the rule states the whole grammar in one place; if a theme
   ever moves --ftd-head-ink away from --ftd-ink, a form heading inside a box follows the head, and that is
   the right answer anyway.
   It also beats the theme's `.af-dashboard h4` (0,1,1) on all four, measured under the five site sheets with
   a canary proving they loaded, so this class needs NO entry in the cover above. The glyph and the top margin
   are already answered there for .ftd-sec__title.
   The three page-scoped copies of this rule (.ftd-r2-cols, .ftd-r8-page, .ftd-r10-stack, in the walk's own
   page CSS) stay where they are: they are those pages' signed-off work and they resolve identically. */
.ftd .ftd-sec--form .ftd-sec__title { font-size: 14px; font-weight: 600; letter-spacing: 0; text-transform: none; color: var(--ftd-ink); }
.ftd .ftd-sec__head, .ftd .ftd-sec__toggle { border-top-color: var(--ftd-control-line); padding-top: 10px; }
.ftd .ftd-sec--boxed > .ftd-sec__head, .ftd .ftd-sec--boxed > .ftd-sec__toggle { border-top: 0; padding-top: 9px; }
.ftd .ftd-sec--flush > .ftd-sec__head { border-top: 0; padding-top: 0; }

/* a label reads as a label and a value as a value */
.ftd .ftd-fact__label, .ftd .ftd-field > label, .ftd .ftd-field__label, .ftd .ftd-summary__title { color: var(--ftd-label); }
.ftd .ftd-fact__value { font-size: 13.5px; font-weight: 500; color: var(--ftd-ink); }
.ftd .ftd-fact__value--none { font-weight: 400; color: var(--ftd-off-ink); }
.ftd .ftd-facts--table > .ftd-fact { border-bottom-color: var(--ftd-line); }

/* a frame is drawn where it can be seen */
.ftd .ftd-sec--boxed, .ftd .ftd-summary, .ftd .ftd-modal { border-color: var(--ftd-control-line); }
.ftd .ftd-modal { border: 1px solid var(--ftd-control-line); }

/* ---------------------------------------------------------------- MEANING: A CHOICE THAT MEANS YES IS GREEN, ONE THAT MEANS NO IS RED
   Tristian, 2026-09-19: "we are using the entity theme - but we have values like 'Yes' and 'No' which are much better for the
   user if they are green/red. The statuses also have colours in the existing site. On Camera is also similar to Yes, and Off
   Camera is similar to No - which means colours can be consistent."
   So a choice carries its MEANING in data-tone, and the chosen one is filled with that meaning's colour, not the theme's:
     good     Yes, On Camera, and the status Active (In Use): the site's green
     pre      the status Acquired (Not Used): the site's LIGHT green, a pre-active colour (Tristian, 2026-09-19: "'Active' would be
              green, and Acquired a pre-active colour (hence light green)"). THE STATUS COLOURS ARE THE EXISTING SYSTEM'S: the
              build takes each status's colour from where the site already defines it, and these two tones stand in for them here
     bad      No, Off Camera
     info     kept for a status the site draws in its second colour; not used by Acquired or Active
     neutral  Unknown, and anything that is neither
   A choice with no data-tone keeps the theme's primary, as before. One rule, used by the form's tiles (.ftd-tile) and by the
   list card's choice rows alike, so Yes is the same green on the Add Asset page, in an action modal and on a card. */
.ftd .ftd-tile[data-tone="good"].is-on, .ftd .ftd-tile--yes.is-on, .ftd [data-tone="good"].is-on { border-color: var(--ftd-good-ink); background: var(--ftd-good-ink); color: #fff; }
.ftd .ftd-tile[data-tone="pre"].is-on, .ftd [data-tone="pre"].is-on { border-color: var(--ftd-good-ink); background: var(--ftd-good-bg); color: var(--ftd-good-ink); }
.ftd .ftd-tile[data-tone="bad"].is-on, .ftd .ftd-tile--no.is-on, .ftd [data-tone="bad"].is-on { border-color: var(--ftd-bad-ink); background: var(--ftd-bad-ink); color: #fff; }
.ftd .ftd-tile[data-tone="info"].is-on, .ftd [data-tone="info"].is-on { border-color: var(--ftd-accent); background: var(--ftd-accent); color: #fff; }
.ftd .ftd-tile[data-tone="neutral"].is-on, .ftd [data-tone="neutral"].is-on { border-color: #5c6670; background: #5c6670; color: #fff; }
.ftd [data-tone="good"]:not(.is-on):hover { border-color: var(--ftd-good-ink); color: var(--ftd-good-ink); }
.ftd [data-tone="bad"]:not(.is-on):hover { border-color: var(--ftd-bad-ink); color: var(--ftd-bad-ink); }
/* ================================================================ FOUNDATION ends */

/* ================================================================ THE TABLE
   The central replacement for the site's table (PoLines / NewPoLines; AUDIT_SHARED_UI_2026-09-19.md, section 3).
   One component draws every table on the site, so these rules are loaded once, site-wide, and the component's root
   carries `ftd` itself (it is not always inside a dashboard): <div class="ftd ftd-tablewrap">.

     1. IT RESPONDS TO ITS OWN WIDTH (`ftdtable`), not the screen's: the same table sits in a full page, in two thirds
        of a detail page's tab and in a modal.
     2. IT NEVER SCROLLS SIDEWAYS AND NEVER CLIPS. With less room the lower-priority columns leave the row and are
        listed in the row's own detail, opened by the chevron; nothing is `overflow: hidden` around a menu.
     3. THE COLUMNS ARE A GRID the component computes from the column list and sets as three variables on the table:
        --ftd-cols (all columns), --ftd-cols-md (priority 1 and 2), --ftd-cols-sm (priority 1). A column's priority:
        `show_on_mobile: true` is 1 and never leaves the row; the rest are 2, then 3 from the right.
     4. HEADINGS WRAP BETWEEN WORDS, NEVER INSIDE ONE. Cell text wraps to two lines and then ends in an ellipsis with
        the whole value as its title. */
.ftd-tablewrap { container-type: inline-size; container-name: ftdtable; }
.ftd .ftd-table__title { margin: 0 0 10px; font-size: 15px; font-weight: 600; color: var(--ftd-ink); }
.ftd-table { position: relative; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); box-shadow: 0 5px 30px rgba(0, 28, 56, .06); }
.ftd-table__head, .ftd .ftd-table__row, .ftd-table__foot { display: grid; grid-template-columns: var(--ftd-cols); align-items: center; gap: 0 14px; padding-left: 18px; padding-right: 14px; }
.ftd-table__head { min-height: 40px; padding-top: 8px; padding-bottom: 8px; border-radius: 7px 7px 0 0; background: var(--ftd-primary); color: #fff; font-size: 11px; font-weight: 600; letter-spacing: .05em; line-height: 1.3; text-transform: uppercase; }
.ftd-table__head > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; overflow-wrap: normal; word-break: normal; hyphens: none; }
.ftd-table__head--single { display: block; padding-top: 11px; }
.ftd-table__head small { display: block; font-size: 9.5px; font-weight: 500; letter-spacing: 0; text-transform: none; white-space: nowrap; opacity: .75; }

.ftd .ftd-table__row { position: relative; min-height: 46px; padding-top: 6px; padding-bottom: 6px; border-bottom: 1px solid var(--ftd-hairline); transition: background .15s; }
.ftd .ftd-table__row:last-child { border-bottom: 0; border-radius: 0 0 7px 7px; }
.ftd .ftd-table__row:hover { background: var(--ftd-hover); }
.ftd .ftd-table__row.is-selected { background: var(--ftd-accent-soft); }
.ftd-table__row--clickable { cursor: pointer; }
.ftd-table__row--struck .ftd-table__cell:not(.ftd-table__cell--actions) { color: var(--ftd-muted); text-decoration: line-through; }
/* the row's state, the bar the cards carry too (getAssetClasses / getLineClassUsing decide it; these are its colours) */
.ftd-table__row::before { content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 4px; background: var(--ftd-bar, transparent); }
.ftd-table__row--current { --ftd-bar: #4f8a3a; }
.ftd-table__row--ended { --ftd-bar: #b9c0c9; }
.ftd-table__row--locked { --ftd-bar: #d9a441; }
.ftd-table__row--bad { --ftd-bar: var(--ftd-bad-ink); }

.ftd-table__cell { min-width: 0; color: var(--ftd-text); overflow-wrap: break-word; }
.ftd-table__text { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.ftd-table__sub { display: block; font-size: 11.5px; color: var(--ftd-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ftd .ftd-table__cell--id a { font-weight: 500; color: var(--ftd-primary); white-space: nowrap; }
.ftd .ftd-table__cell a:hover { text-decoration: underline !important; }
.ftd-table__cell--strong { font-weight: 500; color: var(--ftd-ink); }
.ftd-table__num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* a column of words after a column of numbers keeps its distance */
.ftd-table__num + :not(.ftd-table__num):not(.ftd-table__cell--actions) { padding-left: 14px; }
.ftd-table__cell--actions { display: flex; justify-content: flex-end; align-items: center; gap: 4px; }
/* BULK ACTIONS. The first column is a tick box when, and only when, the page's bulk actions are turned on (the bar's
   Turn On / Turn Off, saved per user as `is_actions_bar_opened`): the table is handed the page's own selection
   (SharedBulkUpdate.mixin.js: selected_ids, addToSelectedList, should_hide_unselected, bulk_actions_customer_id) and
   draws it; it keeps no selection of its own. A row that may not be chosen (another customer's, once one is chosen)
   has its box disabled. THE CONTROL ON A ROW IS ONE SHARED COMPONENT WITH A LAYOUT VARIABLE (Log #298): in a table it is this tick
   box; on the large List card, where there is room, it stays the Select / Unselect button with Continue and Export
   beside a chosen item. One component, one set of rules, the drawing chosen by the layout it is mounted in. */
.ftd-table__check { width: 16px; height: 16px; margin: 0; accent-color: var(--ftd-accent); cursor: pointer; }
.ftd-table__check:disabled { cursor: not-allowed; opacity: .45; }
.ftd-table__cell--check { display: flex; align-items: center; }
.ftd-bulkbar { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin: 0 0 14px; padding: 9px 16px; border-radius: 8px; background: var(--ftd-primary); color: #fff; }
.ftd-bulkbar b { font-weight: 600; }
.ftd-bulkbar__sep { opacity: .4; }
.ftd .ftd-bulkbar a { color: #fff; opacity: .85; }
.ftd .ftd-bulkbar a:hover { opacity: 1; text-decoration: underline !important; }
.ftd .ftd-bulkbar a.is-main { font-weight: 600; opacity: 1; }
.ftd-bulkbar__right { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; margin-left: auto; }
.ftd-bulkbar--off { background: var(--ftd-accent-soft); color: var(--ftd-primary); }
.ftd .ftd-bulkbar--off a { color: var(--ftd-primary); }
.ftd-table__empty { color: var(--ftd-control-line); }
/* three small yes / no / unknown marks in one cell (the assets table's Usage column: on camera, hero, asset) */
.ftd-marks { display: inline-flex; gap: 6px; }
.ftd-mark { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%; background: var(--ftd-inset); color: var(--ftd-muted); font-size: .95rem; }
.ftd-mark--yes { background: var(--ftd-good-bg); color: var(--ftd-good-ink); }
.ftd-mark--no { background: var(--ftd-bad-bg); color: var(--ftd-bad-ink); }

/* the three-dots button and the chevron: one size, one look, in every table */
.ftd .ftd-table__more, .ftd .ftd-table__toggle { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0; border: 1px solid transparent; border-radius: 50%; background: transparent; color: var(--ftd-primary); font-size: 1.25rem; cursor: pointer; transition: all .15s; }
.ftd .ftd-table__more:hover, .ftd .ftd-table__more[aria-expanded="true"], .ftd .ftd-table__toggle:hover { border-color: var(--ftd-accent-soft); background: var(--ftd-surface); color: var(--ftd-accent); }
.ftd .ftd-table__toggle { display: none; }
.ftd-table__toggle i { transition: transform .2s; }
.ftd-table__row.is-open .ftd-table__toggle i { transform: rotate(180deg); }

/* the menu: a real dropdown that owns its button, bounded by the window, so it flips up near the foot of the screen
   and nothing around it can clip it. The mock opens it with a class; the component uses b-dropdown (right, boundary
   "window"). Group headings and the red Delete are the asset menu's own. */
.ftd-menu { display: none; position: absolute; z-index: 30; top: calc(100% - 8px); right: 14px; min-width: 214px; padding: 6px 0; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); box-shadow: 0 14px 36px rgba(0, 28, 56, .2); text-align: left; }
.ftd-menu.is-open { display: block; }
.ftd-menu--up { top: auto; bottom: calc(100% - 8px); }
.ftd-menu__group { padding: 7px 14px 3px; font-size: 10.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd .ftd-menu a { display: flex; align-items: center; gap: 9px; padding: 6px 14px; color: var(--ftd-ink); text-decoration: none; }
.ftd .ftd-menu a i { font-size: 1.05rem; color: var(--ftd-muted); }
.ftd .ftd-menu a:hover { background: var(--ftd-hover); text-decoration: none; }
.ftd .ftd-menu a.is-danger, .ftd .ftd-menu a.is-danger i { color: var(--ftd-bad-ink); }
.ftd .ftd-menu a.is-off { color: var(--ftd-control-line); cursor: not-allowed; }
.ftd-menu hr { margin: 5px 0; border: 0; border-top: 1px solid var(--ftd-hairline); }

/* the row's detail: every column that is not in the row at this width, as name and value. Closed until the chevron. */
.ftd-table__detail { display: none; grid-column: 1 / -1; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px 18px; margin: 8px 0 4px; padding: 10px 12px; border-radius: 6px; background: var(--ftd-inset); }
.ftd-table__row.is-open > .ftd-table__detail { display: grid; }
.ftd-table__fact { display: none; min-width: 0; }
.ftd-table__fact dt { font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--ftd-muted); }
.ftd-table__fact dd { color: var(--ftd-ink); overflow-wrap: break-word; }
/* a block a page hangs under a row (the `new_line` slot) and a last row it appends (`appended_last_item`) */
.ftd-table__under { grid-column: 1 / -1; margin: 6px 0 2px; padding: 8px 12px; border-left: 3px solid var(--ftd-accent-soft); font-size: 12px; color: var(--ftd-muted); }
.ftd-table__foot { min-height: 42px; border-top: 1px solid var(--ftd-line); border-radius: 0 0 7px 7px; background: var(--ftd-inset); font-weight: 600; color: var(--ftd-ink); }

/* less room: priority 3 leaves the row */
@container ftdtable (max-width: 980px) {
    .ftd-table__head, .ftd .ftd-table__row, .ftd-table__foot { grid-template-columns: var(--ftd-cols-md, var(--ftd-cols)); }
    .ftd-table__head > .ftd-p3, .ftd-table__row > .ftd-p3, .ftd-table__foot > .ftd-p3 { display: none; }
    .ftd-table__fact.ftd-p3 { display: block; }
    .ftd .ftd-table--folds .ftd-table__toggle { display: inline-flex; }
}
/* less again: priority 2 leaves it too */
@container ftdtable (max-width: 640px) {
    .ftd-table__head, .ftd .ftd-table__row, .ftd-table__foot { grid-template-columns: var(--ftd-cols-sm, var(--ftd-cols)); gap: 0 10px; padding-left: 14px; padding-right: 8px; }
    .ftd-table__head > .ftd-p2, .ftd-table__row > .ftd-p2, .ftd-table__foot > .ftd-p2 { display: none; }
    .ftd-table__fact.ftd-p2 { display: block; }
}
/* A PHONE. The column names go. A row is its priority-1 columns one above the other, the chevron and the three dots
   always in reach on the right, and everything else in the detail. Nothing here needs a script to be usable. */
@container ftdtable (max-width: 480px) {
    .ftd-table__head { display: none; }
    .ftd-table { border-radius: 8px; }
    .ftd .ftd-table__row { grid-template-columns: minmax(0, 1fr) 34px 34px; gap: 0 4px; min-height: 56px; padding: 9px 6px 9px 16px; }
    .ftd .ftd-table__row:first-of-type { border-radius: 7px 7px 0 0; }
    .ftd-table__row > .ftd-table__cell { grid-column: 1; }
    .ftd-table__row > .ftd-table__cell.ftd-p1 + .ftd-table__cell.ftd-p1 { font-size: 14px; font-weight: 500; color: var(--ftd-ink); }
    /* ONE TITLE TO A ROW, 2026-09-21. The rule above dresses EVERY priority-1 cell that follows another one,
       which was right while a stacked row had two lines. Switch Production's List view has three -- initials,
       name, user count -- and the count came out in the same bold dark title type as the name, so the row
       read as if it had two titles. A third priority-1 TEXT cell is drawn in the ordinary row type instead.
       WHY THE TWO :not()s, both of them measured rather than assumed. The chain counts CELLS, and two cells in
       a row are not text: the tick box is priority 1 and comes first when bulk actions are on, so without the
       first :not() a selecting row's ID and name count as the second and third and the NAME loses its title --
       that is table-mock's own case, and it broke that page at 390 by 359,241 pixels. The actions cell is
       priority 1 and comes last, so without the second :not() it would be counted as a third text cell; it
       paints nothing either way (it holds only a button, which the guard gives its own type), but the rule
       should say what it means.
       Measured in a rig of three priority-1 text cells: the first stays plain, the second keeps 14px/500 ink,
       the third is 13px/400 text. table-mock at 390 is pixel-identical.
       IT IS NOT WHOLLY INERT, and this is the one place these three additions touch an existing render. Every
       approved mock and all fifteen r pages are pixel-identical at 1920, 1366 and 390. ONE page moves:
       design-walk/d18-filter-sidebar-look at 390, by 145,765 pixels. That page has rows of exactly this shape
       -- acquisition ID, supplier name, amount, all priority 1, no tick box -- so the amount was being drawn
       as a second title, which is the very defect this rule mends: the picture before and after shows
       "1,240.00" going from bold dark title type to the ordinary row type, and each such row losing 2px of
       height. d18 is a superseded OPTIONS draft (option A / option B) from which r15 was chosen, and r15
       itself does not move. So this is the fix landing where the fault was, not a regression -- but it IS a
       change to a rendered page and it is written down here rather than left to be discovered. */
    .ftd-table__row > .ftd-table__cell.ftd-p1:not(.ftd-table__cell--check) + .ftd-table__cell.ftd-p1 + .ftd-table__cell.ftd-p1:not(.ftd-table__cell--actions) { font-size: 13px; font-weight: 400; color: var(--ftd-text); }
    .ftd-table__row > .ftd-table__cell--actions { grid-column: 3; grid-row: 1 / span 3; align-self: center; }
    .ftd-table__row > .ftd-table__toggle { grid-column: 2; grid-row: 1 / span 3; align-self: center; }
    .ftd-table__row > .ftd-table__num { text-align: left; }
    .ftd-table__detail { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-right: 8px; }
    .ftd-menu { right: 8px; left: 16px; }
    /* with bulk actions on, the tick box stands at the left of the row, level with its middle */
    .ftd .ftd-table--selecting .ftd-table__row { grid-template-columns: 26px minmax(0, 1fr) 34px 34px; }
    .ftd-table--selecting .ftd-table__row > .ftd-table__cell { grid-column: 2; }
    .ftd-table--selecting .ftd-table__row > .ftd-table__cell--check { grid-column: 1; grid-row: 1 / span 3; align-self: center; }
    .ftd-table--selecting .ftd-table__row > .ftd-table__toggle { grid-column: 3; }
    .ftd-table--selecting .ftd-table__row > .ftd-table__cell--actions { grid-column: 4; }
}

/* ---------------------------------------------------------------- F-PF  THE PRE-FLIGHT AS STEPS (D3, approved by Tristian 2026-09-28; corrected 2026-09-29)
   The chart import's pre-flight draws its answer cards as four numbered steps that open and close (the approved
   disclosure .ftd-sec--boxed > .ftd-sec__toggle, aria-expanded), with Open all and Close all above them. Drawn in
   walk/2026-09-28/mock/preflight-accordion.html.
   EVERY RULE HERE IS SCOPED TO THE STEPS. A step is the one boxed section whose toggle carries the number badge
   (.ftd-stepsec__no, used by no other page), so the rules that touch a shipped primitive are written under
   `.ftd-sec--boxed:has(> .ftd-sec__toggle > .ftd-stepsec__no)` or under `.ftd-rowlists`, and nothing outside the
   pre-flight moves: not the choice-tile grid (.ftd-choices, F2), not the asset form's disclosure, not the stepper.
   The first version of this section (03aeb3484) declared a bare .ftd-choices and bare toggle rules, which turned
   every choice-tile grid into a column (BulkActionsMeasuresTest) and moved the asset form's chevron; this replaces it.
   Checked 2026-09-29 by two readers against LedgerImport.vue: no rule reaches another page; the rules that matched
   nothing are gone; the action bar is in the flow (an ancestor of the import page hides overflow, so it never stuck).
   Nothing above this line changes. */
.ftd-stepsec__no { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; background: var(--ftd-primary); color: #fff; font-size: 12px; font-weight: 600; }
.ftd-sec__toggle > .ftd-stepsec__no { margin-right: 2px; }
.ftd-sec--boxed > .ftd-sec__toggle:has(> .ftd-stepsec__no) .ftd-sec__title { flex: none; }
.ftd-rowlists .ftd-sec--boxed > .ftd-sec__toggle .ftd-sec__title { flex: none; }
.ftd-sec--boxed > .ftd-sec__toggle:has(> .ftd-stepsec__no) .ftd-sec__note { flex: 1 1 auto; margin-left: auto; text-align: right; color: var(--ftd-muted); font-size: 12.5px; font-weight: 400; text-transform: none; letter-spacing: 0; }   /* the step's one-line summary, right against the chevron */
.ftd-rowlists .ftd-sec--boxed > .ftd-sec__toggle .ftd-sec__note { flex: 1 1 auto; margin-left: auto; text-align: right; color: var(--ftd-muted); font-size: 12.5px; font-weight: 400; text-transform: none; letter-spacing: 0; }
.ftd-sec--boxed > .ftd-sec__toggle:has(> .ftd-stepsec__no) .ftd-sec__note.is-todo { color: var(--ftd-warn-ink); }   /* the step still needs an answer */
.ftd-sec--boxed > .ftd-sec__toggle:has(> .ftd-stepsec__no) .ftd-sec__note.is-done { color: var(--ftd-good-ink); }   /* the step is answered */
.ftd-stepsbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 14px; margin: 14px 0 10px; }   /* "Four steps ..." with Open all / Close all */
.ftd-stepsbar__what { color: var(--ftd-muted); font-size: 12.5px; }
.ftd-fileline { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; margin: -6px 0 14px; color: var(--ftd-muted); font-size: 12.5px; }   /* the uploaded file, its sheet, its counts, the columns ignored */
.ftd-fileline b { color: var(--ftd-ink); font-weight: 500; }
.ftd-substep { margin-top: 16px; }   /* a block inside a step (Accounts with no heading inside Headings) */
.ftd-substep__title { display: flex; align-items: baseline; gap: 10px; margin: 0 0 8px; font-size: 13.5px; font-weight: 600; color: var(--ftd-head-ink); }
.ftd .ftd-substep__title { margin-top: 0 !important; color: var(--ftd-head-ink) !important; text-transform: none; }   /* drawn on an h5: the site's own sheets grey every h5 at tablet widths, so the colour is restated at (0,2,0) as the heading cover does */
.ftd-substep__title small { color: var(--ftd-muted); font-weight: 400; }
.ftd-sec--boxed:has(> .ftd-sec__toggle > .ftd-stepsec__no) .ftd-choices { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 14px; }   /* the three choices of the no-department step, INSIDE A STEP ONLY; the choice-tile grid keeps F2's rules everywhere else */
.ftd-sec--boxed:has(> .ftd-sec__toggle > .ftd-stepsec__no) .ftd-choices .ftd-check b { font-weight: 600; }
.ftd-rowlists { margin-top: 16px; }   /* the row lists under the summary, each a closed disclosure */
.ftd-rowlists .ftd-sec--boxed { margin: 0 0 8px; }
.ftd-figline { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; color: var(--ftd-text); }   /* "123 accounts imported · 6 headings · ..." */
.ftd-figline > span { white-space: nowrap; }
.ftd-figline b { color: var(--ftd-ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.ftd-reasons { margin: 6px 0 0; padding: 0; list-style: none; }   /* why rows are skipped, one reason a line */
.ftd-reasons > li { display: flex; align-items: baseline; gap: 8px; padding: 3px 0; }
.ftd-reasons b { min-width: 34px; color: var(--ftd-ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.ftd-actbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin-top: 18px; padding: 10px 16px; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); }   /* Stop and fix / Import and skip, with what the import would do beside them */
.ftd-actbar__what { min-width: 0; color: var(--ftd-muted); font-size: 12px; }
.ftd-actbar__what b { font-weight: 600; color: var(--ftd-ink); }
.ftd-actbar > .ftd-btnrow { margin-left: auto; }
@container ftd (max-width: 520px) { .ftd-actbar > .ftd-btnrow { margin-left: 0; width: 100%; } }
.ftd-bulkline { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; margin: 0 0 12px; padding: 8px 12px; border: 1px solid var(--ftd-accent-soft); border-radius: 8px; background: var(--ftd-hover); }   /* "Put all of these under" */
.ftd-bulkline__label { flex: none; color: var(--ftd-ink); font-weight: 500; }
.ftd-bulkline__control { flex: 1 1 240px; min-width: 0; max-width: 340px; }
.ftd .ftd-caplist__cap { margin-top: 8px; color: var(--ftd-muted); font-size: 12px; }   /* "Showing the first N of M." */
/* F-PF, added 2026-10-02 (the delivery check; the pre-flight run's rows PF-8 and PF-7, Tristian: "all recs").
   PF-8: the steps' sentences wear .ftd-blockp and .ftd-blockp--muted, which the approved drawing takes from the mock's
   own sheet (design-walk/d5-import-family.css) and this sheet never carried, so on the page they sat with no gap
   beneath and the "None." lines at full size. The values are the drawing's as it renders: 8px beneath, the text ink;
   the muted line 12px in the same ink. Only LedgerImport.vue uses the class.
   PF-7: under 576px the wizard's fieldset keeps its 105px side padding (resources/sass/_file_import_styles.scss),
   which left each step about 130px wide; the pre-flight's fieldset alone (the one holding #ledger-import-preflight)
   takes 12px. No other step, wizard or module moves. */
.ftd .ftd-blockp { margin: 0 0 8px; color: var(--ftd-text); }
.ftd-blockp b { color: var(--ftd-ink); font-weight: 600; }
.ftd-blockp--muted { font-size: 12px; }
@media (max-width: 575.98px) { .multi_step_form #msform fieldset:has(> #ledger-import-preflight) { padding-left: 12px; padding-right: 12px; } }

/* ---------------------------------------------------------------- F-IMP  THE LIFE OF AN IMPORT (D3 of the walk of 2026-10-02; approved by Tristian 2026-10-02)
   Drawn in walk/2026-10-02/mock/import-wizard-states.html and import-page-states.html; the stepper is option E of
   walk/2026-10-02/mock/import-stepper-options.html. Every class here is new and is used by the import wizards and the
   import's page alone: nothing above this line changes, and no shipped primitive is restyled.
   .ftd-track        the wizards' stepper: today's green progress line kept and redrawn. A done step is a tick on a
                     green line, the current step is navy with a ring, the steps ahead are grey; under each its name,
                     what it decided (one line), and Change on a done step while nothing is imported. It sits inside
                     .ftd (it reads --ftd-primary and --ftd-accent-soft) and divides by how many steps it is given.
   .ftd-fileline__actions   the import's two controls at the right of the file's line (Choose a different file,
                     Discard import); .ftd-fileline--replace is that line while a different file is being chosen.
   .ftd-runline      the run: what is being imported, with Cancel import at its right, over the sheet's .ftd-bar.
   .ftd-badge--blue  the "Importing" badge on the import's page.
   .ftd-neverran     the import's page for a file that was uploaded and never imported. */
.ftd-track { --ftd-track-done: #3d8a43; --ftd-track-rest: #e3e7ec; display: flex; margin: 6px 0 18px; padding: 0; list-style: none; }
.ftd-track__step { position: relative; flex: 1 1 0; min-width: 0; padding: 44px 6px 0; text-align: center; }
.ftd-track__step::before { content: ""; position: absolute; top: 16px; left: -50%; width: 100%; height: 6px; background: var(--ftd-track-rest); }   /* the line into this step */
.ftd-track__step:first-child::before { display: none; }
.ftd-track__step.is-done::before, .ftd-track__step.is-open::before { background: var(--ftd-track-done); }
.ftd-track__dot { position: absolute; top: 0; left: 50%; transform: translateX(-50%); z-index: 1; display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: 50%; background: #e9edf2; color: #9aa3ae; font-size: 14px; font-weight: 600; }
.ftd-track__step.is-done .ftd-track__dot { background: var(--ftd-track-done); color: #fff; font-size: 20px; }
.ftd-track__step.is-open .ftd-track__dot { background: var(--ftd-primary); color: #fff; box-shadow: 0 0 0 5px var(--ftd-accent-soft); }
.ftd-track__title { display: block; overflow: hidden; color: #9aa3ae; font-size: 11px; font-weight: 600; letter-spacing: .05em; text-overflow: ellipsis; text-transform: uppercase; white-space: nowrap; }
.ftd-track__step.is-done .ftd-track__title { color: var(--ftd-good-ink); }
.ftd-track__step.is-open .ftd-track__title { color: var(--ftd-primary); }
.ftd-track__sum { display: block; margin-top: 2px; overflow: hidden; color: var(--ftd-ink); font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
.ftd-track__step.is-open .ftd-track__sum { color: var(--ftd-primary); font-weight: 500; }
.ftd .ftd-track__change { display: inline-block; margin-top: 2px; padding: 0; border: 0; background: none; color: var(--ftd-accent); font: inherit; font-size: 11.5px; font-weight: 500; text-decoration: underline; cursor: pointer; }
@container ftd (max-width: 520px) { .ftd-track__sum, .ftd .ftd-track__change { display: none; } .ftd-track__title { font-size: 9.5px; letter-spacing: 0; } }
.ftd-fileline__actions { display: inline-flex; flex-wrap: wrap; gap: 6px; margin-left: auto; }
.ftd-fileline--replace { flex-direction: column; align-items: stretch; }
.ftd-fileline--replace .ftd-drop { min-height: 96px; }
.ftd-runline { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin: 4px 0 10px; }
.ftd-runline__what { color: var(--ftd-ink); font-size: 14px; font-weight: 500; }
.ftd-runline__what small { display: block; color: var(--ftd-muted); font-size: 12px; font-weight: 400; }
.ftd-runline > .ftd-btnrow { margin-left: auto; }
.ftd-badge--blue { background: var(--ftd-accent-soft); color: var(--ftd-primary); }
.ftd-neverran { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 22px; padding: 18px 20px; border: 1px solid var(--ftd-line); border-radius: 8px; background: var(--ftd-surface); }
.ftd-neverran__text { flex: 1 1 380px; min-width: 0; }
.ftd .ftd-neverran__title { margin: 0 0 4px; color: var(--ftd-ink); font-size: 15px; font-weight: 600; }
.ftd .ftd-neverran__sub { margin: 0; color: var(--ftd-text); font-size: 13px; }
.ftd-neverran__facts { flex: 0 1 360px; }
