.fko-app {
  --fko-navy: #061b36;
  --fko-navy-2: #082441;
  --fko-navy-3: #153458;
  --fko-orange: #ff7a1a;
  --fko-orange-dark: #dc5d00;
  --fko-white: #f7faff;
  --fko-muted: #a9bad0;
  /* A light INPUT surface (user, 2026-09-12) -- the endpoint name field is a
     white box the customer types into, so its three colours are named here
     rather than written as literals at the rule. Not a light CARD surface:
     the panel is dark navy and stays so. */
  --fko-input-bg: #ffffff;
  --fko-input-ink: #0b1f38;
  --fko-input-placeholder: #6b7a8d;
  --fko-border: #244564;
  --fko-green: #3ed598;
  --fko-amber: #f4bd50;
  --fko-red: #ff6672;
  --fko-blue: #77a7ff;
  /* 🔑 Added 2026-08-19 for the Workflows audit category. Tuned to sit beside
     --fko-blue without reading as the same colour on a navy ground. */
  --fko-purple: #b98cff;
  position: relative;
  width: 100% !important;
  max-width: 1320px !important;
  min-height: 760px;
  margin: 0 auto;
  overflow: hidden;
  border: 1px solid var(--fko-border);
  border-radius: 12px;
  background: var(--fko-navy);
  color: var(--fko-white);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  box-shadow: 0 18px 50px rgba(10, 15, 38, .18);
  box-sizing: border-box;
}

.fko-app *, .fko-app *::before, .fko-app *::after { box-sizing: border-box; }
.fko-app button, .fko-app input, .fko-app select { font: inherit; }
/* 🔴 EVERY SELECT'S OPTION LIST, ONCE (user, 2026-09-11). The OS paints the
   dropdown list on its own white surface, so a select styled dark with white
   text shows an invisible list -- only the highlighted row could be read on
   the Audit page's Activity filter. The nav environment select and the profile
   selects each carried this same rule locally; the Audit filters and the
   members role select did not. One app-wide rule so no future select forgets. */
.fko-app select option { background-color: #ffffff; color: #0a2848; }
.fko-app button, .fko-app a { -webkit-tap-highlight-color: transparent; }
.fko-app h2, .fko-app h3, .fko-app p { margin-top: 0; color: inherit; }
.fko-app h2 { margin-bottom: 0; font-size: clamp(1.55rem, 2.5vw, 2.15rem); line-height: 1.1; letter-spacing: -.035em; }
.fko-app h3 { margin-bottom: 0; font-size: 1.125rem; line-height: 1.35; }
.fko-app a { color: inherit; }
/* Every plain link in the panel: orange on hover, never underlined (user,
   2026-09-11). Buttons rendered as <a> keep their own hover rules -- excluded
   by class, or a solid orange button would turn its label orange on orange. */
.fko-app a:not([class*="button"]):not([class*="fko-nav"]):hover,
.fko-app a:not([class*="button"]):not([class*="fko-nav"]):focus-visible { color: var(--fko-orange); text-decoration: none; }
/* And the selected one stays orange (user, 2026-09-11): any plain link marked
   current -- aria-current or .is-active -- reads orange at rest. */
.fko-app a:not([class*="button"]):not([class*="fko-nav"])[aria-current="true"],
.fko-app a:not([class*="button"]):not([class*="fko-nav"])[aria-current="page"],
.fko-app a:not([class*="button"]):not([class*="fko-nav"]).is-active { color: var(--fko-orange); text-decoration: none; }
.fko-app button:focus-visible, .fko-app a:focus-visible, .fko-app input:focus-visible, .fko-app select:focus-visible { outline: 3px solid var(--fko-orange); outline-offset: 2px; }

/* 🔴 THE SECTION HEADING IS FOCUSED PROGRAMMATICALLY, AND THE THEME OUTLINES IT.
   control-panel.js sets tabindex="-1" on each section's <h2> and calls focus()
   on every tab switch -- correct for screen readers, which then announce the
   section they landed on.

   Twenty Twenty-Five ships `:where(.wp-site-blocks :focus){outline-width:2px;
   outline-style:solid}` with NO colour, so it resolves to currentColor -- a
   white box around the title on our dark panel. Reported 2026-08-16 on
   "Environment overview" and again on "Secrets".

   🔑 :focus, NOT :focus-visible. A programmatic focus() does not match
   :focus-visible, so scoping this to that pseudo-class would leave the theme's
   plain :focus rule to win -- which is exactly what happened the first time I
   tried to fix this.

   ⚠ The rule above still gives a VISIBLE orange outline to everything a
   keyboard user actually tabs to. Headings are not in the tab order; suppressing
   theirs removes decoration, not an accessibility affordance. */
.fko-app h1:focus, .fko-app h2:focus, .fko-app h3:focus,
.fko-app [tabindex="-1"]:focus { outline: none; }
.fko-app [hidden] { display: none !important; }

/* 🔴 VISUALLY HIDDEN, AND IT MUST STAY HIDDEN WHEN FOCUSED.
   The template uses .screen-reader-text on four headings. WordPress core, the
   theme and WooCommerce EACH ship their own copy of this class, and each one
   also ships a `.screen-reader-text:focus` rule that UN-HIDES it with a
   background, padding and width:auto -- correct for skip links, catastrophic
   here.

   control-panel.js sets tabindex="-1" and calls heading.focus() on every
   section switch, so selecting Overview focused "Environment overview" and
   those foreign :focus rules painted it as a white box (reported 3x,
   2026-08-16). Measured on the live page: TWO such rules load AFTER this
   stylesheet, so at equal specificity theirs win.

   🔑 THE FIX IS SPECIFICITY, NOT ORDER. We cannot control enqueue order across
   plugins and themes, so this scopes to the panel's own container and adds a
   :focus override that beats a bare `.screen-reader-text:focus`
   (0,2,1 vs 0,2,0). Scoping to .fko-app also means we do not alter skip-link
   behaviour anywhere else on the site.

   ⚠ NOT display:none -- that removes the heading from the accessibility tree,
   which is the only reason it exists. */
.fko-app .screen-reader-text,
.fko-app .screen-reader-text:focus,
.fko-app .screen-reader-text:focus-visible,
.fko-app .screen-reader-text:active {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
  border: 0 !important;
  background: transparent !important;
  display: block !important;
  font-size: 1px !important;
  line-height: 1 !important;
  color: transparent !important;
  top: auto !important;
  left: -9999px !important;
  z-index: -1 !important;
}

.fko-skip-link { position: absolute; top: -100px; left: 16px; z-index: 20; padding: 10px 14px; background: var(--fko-orange); color: var(--fko-navy); font-weight: 800; }
.fko-skip-link:focus { top: 12px; }

.fko-layout { display: grid; grid-template-columns: 190px minmax(0, 1fr); min-height: 760px; }
.fko-sidebar { display: flex; min-width: 0; flex-direction: column; padding: 18px 13px; border-right: 1px solid #173958; background: #061a34; }
.fko-brand { display: flex; align-items: center; gap: 9px; margin: 0 5px 22px; font-size: 1.205rem; font-weight: 900; letter-spacing: -.03em; }
.fko-brand > span:last-child > span { color: var(--fko-orange); }

/* The environment selector at the head of the nav, 2026-08-22. It scopes
   Workflows, Drift, Audit and Secrets, so it sits above the sections it filters
   rather than beside Fullscreen, where it read as window chrome.
   ⚠ Full width and squared off, unlike the pill buttons: this is a form field
   in a column of nav items, and a pill here would read as a button. */
.fko-nav-environment { margin: 0 5px 14px; }
/* 🔑 UNSCOPED so the ENVIRONMENT label and the ACCOUNT group heading share ONE
   definition. It was `.fko-nav-environment .fko-env-heading` until 2026-08-22,
   which made the second heading impossible to match without copying these five
   declarations -- and a copy is what drifts the first time either is restyled.
   The user asked for a heading "that matches the style of the Environment
   header"; sharing the rule is what makes that true by construction rather
   than by two values that happen to agree today. */
.fko-env-heading {
  display: block; margin-bottom: 6px;
  color: var(--fko-muted); font-size: 0.735rem; font-weight: 800;
  letter-spacing: .08em; text-transform: uppercase;
}
/* ⚠ The header .fko-environment-control rules were REMOVED 2026-08-22 when the
   selector moved into the nav. They set a fixed-width, flex-row pill for a
   header slot that no longer exists, and at equal specificity they fought the
   sidebar rules above. The class name is kept only as the element's hook. */
.fko-nav-environment .fko-environment-control { display: block; }
.fko-nav-environment select {
  width: 100%; margin: 0; padding: 8px 10px;
  /* White on dark (user, 2026-09-11): the environment choice is the one control
     that scopes every section, so it must be the most noticeable thing in the
     sidebar. Dark text on a white field, orange edge on hover. */
  border: 1px solid #ffffff; border-radius: 5px;
  background-color: #ffffff; color: #0a2848;
  font-size: 0.885rem; font-weight: 700; cursor: pointer;
}
.fko-nav-environment select:hover { border-color: var(--fko-orange); }
.fko-nav-environment select:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: 2px; }
/* ⚠ The option list is painted by the OS on its own white surface, so white
   text there is invisible. Dark on white is readable wherever it renders. */
.fko-nav-environment select option { background-color: #ffffff; color: #0a2848; }
/* ⚠ The no-JavaScript submit. control-panel.js hides it once it binds a change
   listener, so it is visible only when the enhancement did not run -- which is
   exactly when the customer needs it. */
.fko-nav-environment form { display: block; }
.fko-nav-environment .fko-env-go { width: 100%; margin-top: 6px; min-height: 32px; padding: 5px 10px; font-size: 0.83rem; }
/* The light rule between the selector and the section list: picking an
   environment is not navigating, so the two must not read as one list. */
.fko-nav-divider {
  height: 0; margin: 0 5px 14px;
  border: 0; border-top: 1px solid var(--fko-border);
}

/* ⚠ The mark is now an <img> of the real logo, not a bordered text box. The
   border/grid centring that framed "FK" must go, or the SVG renders inside an
   orange rectangle that is not part of the brand. */
.fko-brand-mark { width: 34px; height: 34px; flex: 0 0 34px; object-fit: contain; }
.fko-nav-list { display: grid; gap: 5px; }
/* The ACCOUNT group heading sits INSIDE the nav list, so the list's 5px gap
   already separates it from the item below. The space above it, and the rule,
   are what make it read as a new group rather than a stray label.
   ⚠ Inside `.fko-nav-list` on purpose: that container is `display: none` on
   mobile until the menu is opened, so a heading placed outside it would stay
   visible beside a collapsed menu. */
.fko-nav-group-heading {
  /* ⚠ Same specificity as `.fko-env-heading` (both 0,1,0), so this wins ONLY
     because it is later in the file. The `margin` shorthand is deliberate --
     it resets that rule's `margin-bottom: 6px` outright instead of leaving two
     rules half-applying to one element. */
  margin: 16px 5px 2px;
  padding-top: 14px;
  border-top: 1px solid var(--fko-border);
}
.fko-nav-item, .fko-nav-toggle { width: 100%; border: 0; cursor: pointer; text-align: left; }
.fko-nav-item { display: flex; align-items: center; gap: 10px; min-height: 42px; padding: 9px 12px; border-radius: 6px; background: transparent; color: var(--fko-muted); font-size: 1.005rem; font-weight: 720; }
.fko-nav-item:hover { background: var(--fko-navy-3); color: var(--fko-white); }
.fko-nav-item.is-active { box-shadow: inset 3px 0 0 var(--fko-orange); background: var(--fko-navy-3); color: var(--fko-white); }
/* 🔴 A DISABLED NAV ITEM MUST LOOK DISABLED. Without this it renders exactly
   like an enabled one and simply refuses to respond -- which reads as a broken
   panel rather than as a section that is not ready yet. The `not-allowed`
   cursor is what tells a customer the control is inert BEFORE they click it.
   ⚠ The hover rule above must be neutralised too, or a dead item still lights
   up under the pointer and invites the click. */
.fko-nav-item[disabled] { opacity: .45; cursor: not-allowed; }
.fko-nav-item[disabled]:hover { background: transparent; color: var(--fko-muted); }
.fko-nav-icon { width: 22px; flex: 0 0 22px; color: #ccd7e8; font-size: 1.065rem; text-align: center; }
.fko-nav-item.is-active .fko-nav-icon { color: var(--fko-orange); }
.fko-nav-icon::before { content: "•"; }
.fko-nav-icon--overview::before { content: "▦"; }
.fko-nav-icon--workflows::before { content: "⌘"; }
.fko-nav-icon--environments::before { content: "◇"; }
.fko-nav-icon--members::before { content: "♙"; }
.fko-nav-icon--deployments::before { content: "◈"; }
.fko-nav-icon--drift::before { content: "◉"; }
.fko-nav-icon--secrets::before { content: "▢"; }
.fko-nav-icon--audit::before { content: "≣"; }
.fko-nav-icon--backups::before { content: "⛁"; }
/* 🔴 EVERY SECTION KEY NEEDS ITS OWN GLYPH, AND NOTHING CHECKS THAT (user,
   2026-09-12: "icons are not working for the new menus"). The class is BUILT
   from the section key in dashboard.php -- fko-nav-icon--<key> -- so a new
   section renders the `•` fallback above rather than failing: invisible to
   check-orphan-classes, which can only see classes written literally in the
   template. Add the glyph in the SAME change as a section.
   ⚠ Keys, not labels: `inventory` is the key behind the menu item reading
   Inventory, and `applications` is kept because that key still resolves. */
.fko-nav-icon--applications::before { content: "⬡"; }
.fko-nav-icon--inventory::before { content: "⬡"; }
.fko-nav-icon--insights::before { content: "◎"; }
.fko-nav-icon--billing::before { content: "▤"; }
.fko-nav-icon--profile::before { content: "◍"; }
.fko-nav-icon--settings::before { content: "⚙"; }
/* 🔑 A LIFE RING, the icon most products use for support (user, 2026-09-24:
   the "?" glyph read as a stray character). Every other nav icon is a text
   glyph; there is no monochrome life-ring code point, so this one is an
   inline SVG used as a MASK over currentColor -- it takes the same muted /
   orange-when-active colour as the glyphs and needs no image file. `content`
   is still declared so check-nav-icons finds the rule. */
.fko-nav-icon--support::before { content: ""; display: inline-block; width: 1.02em; height: 1.02em; vertical-align: -0.16em; background: currentColor; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Ccircle cx='12' cy='12' r='3.6'/%3E%3Cpath d='M5.6 5.6l3.8 3.8M14.6 14.6l3.8 3.8M5.6 18.4l3.8-3.8M14.6 9.4l3.8-3.8'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Ccircle cx='12' cy='12' r='3.6'/%3E%3Cpath d='M5.6 5.6l3.8 3.8M14.6 14.6l3.8 3.8M5.6 18.4l3.8-3.8M14.6 9.4l3.8-3.8'/%3E%3C/svg%3E") center / contain no-repeat; }
.fko-nav-icon--logout::before { content: "⏻"; }
/* Log out (user, 2026-08-24). ⚠ AN <a>, so it needs the text-decoration and
   box-sizing a <button> got for free -- without these it renders underlined and
   a few pixels narrower than every item above it. */
.fko-nav-logout {
  box-sizing: border-box;
  text-decoration: none;
  /* 🔑 SEPARATED FROM THE SECTIONS ABOVE IT. Every other item switches a section;
     this one leaves the page, and a rule makes that a different kind of thing
     rather than the seventh of six. */
  margin-top: 10px;
  padding-top: 14px;
  border-top: 1px solid var(--fko-border);
  border-radius: 0;
}
.fko-nav-logout:hover { background: var(--fko-navy-3); color: var(--fko-white); }
.fko-nav-toggle { display: none; justify-content: space-between; padding: 10px 12px; background: transparent; color: var(--fko-white); font-weight: 800; }
.fko-sidebar-help { display: grid; gap: 8px; margin-top: auto; padding: 16px 12px 4px; border-top: 1px solid var(--fko-border); font-size: 0.885rem; }
.fko-sidebar-help a { color: var(--fko-muted); text-decoration: none; }
.fko-sidebar-help a:hover { color: var(--fko-orange); }

.fko-main { min-width: 0; padding: 22px 24px 30px; background: #071b32; }
.fko-app-head { display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 20px; }
.fko-app-head h1 { margin: 0 0 3px; color: var(--fko-white); font-size: clamp(1.45rem, 2.4vw, 2rem); line-height: 1.1; letter-spacing: -.035em; }
.fko-app-head p { margin: 0; color: var(--fko-muted); font-size: 0.875rem; }
.fko-app-controls { display: flex; align-items: stretch; gap: 8px; }
/* ⚠ The option list is painted by the OS on its own white surface, so white text there is invisible. Dark text, white background: readable wherever it renders. */
.fko-fullscreen-button { display: flex; min-height: 40px; align-items: center; gap: 7px; padding: 7px 11px; border: 1px solid var(--fko-border); border-radius: 7px; background: #0a2848; color: var(--fko-white); cursor: pointer; font-size: 0.825rem; font-weight: 800; }
.fko-fullscreen-button:hover { border-color: var(--fko-orange); color: var(--fko-orange); }
.fko-fullscreen-icon { font-size: 1.125rem; line-height: 1; }
.fko-app:fullscreen, .fko-app.is-focus-mode { width: 100vw !important; max-width: none !important; height: 100vh; min-height: 100vh; margin: 0; border: 0; border-radius: 0; }
.fko-app:fullscreen .fko-layout, .fko-app.is-focus-mode .fko-layout { height: 100%; min-height: 100vh; }
.fko-app:fullscreen .fko-sidebar, .fko-app:fullscreen .fko-main, .fko-app.is-focus-mode .fko-sidebar, .fko-app.is-focus-mode .fko-main { overflow-y: auto; }
.fko-app.is-focus-mode { position: fixed; inset: 0; z-index: 1000000; }
body.fko-focus-mode-active { overflow: hidden !important; }
.fko-status-dot { width: 7px; height: 7px; border-radius: 50%; }
.fko-status-dot--healthy { background: var(--fko-green); box-shadow: 0 0 0 4px rgba(62, 213, 152, .12); }
.fko-section { animation: fko-section-in .18s ease-out; }
@keyframes fko-section-in { from { opacity: .35; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.fko-section-heading, .fko-panel-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
.fko-section-heading { min-height: 58px; margin-bottom: 20px; }
.fko-eyebrow { display: block; margin-bottom: 6px; color: var(--fko-orange); font-size: 0.805rem; font-weight: 850; letter-spacing: .14em; text-transform: uppercase; }
/* 🔴 THE SECTION NAME WAS PRINTED TWICE ON EVERY PAGE (user, 2026-08-22): once
   as the <h1> in the app header, which control-panel.js keeps in sync with the
   active section, and again as the <h2> immediately below it. "Drift" over
   "Drift".
   🔑 THE <h2> STILL EXISTS — every <section> is named by it via
   aria-labelledby, and control-panel.js focuses it on each section switch, so
   deleting it would strip the accessible name from all nine sections. It is
   .screen-reader-text now: present for assistive tech, gone from the page.
   🔑 The EYEBROW carries the information the <h1> does not ("Desired versus
   runtime" says what Drift means), so it becomes the visible heading and grows
   accordingly -- still orange and uppercase, so the page keeps its rhythm. */
.fko-app .fko-section-eyebrow {
  margin-bottom: 14px;
  font-size: 0.92rem;
  letter-spacing: .1em;
}

/* 🔴 EVERY CP BUTTON IS A PILL (user, 2026-08-22: "secrets page is not
   following the standard"). The outline button became a pill and these did not,
   so a square "Scan workflows" sat beside a rounded Drift badge on the same
   row. Three radii were live at once: 5px here, 6px on .fko-expander, 999px on
   the outline.
   ⚠ SCOPE: the CONTROL PANEL only. wp-admin uses WordPress's own `button`
   classes and the public site has its own system; neither is touched. */
/* 🔑 `.fko-profile-panel button.button` IS LISTED ALONGSIDE `.fko-button` in
   every rule below, rather than given a copy of their declarations.

   WooCommerce hardcodes `class="button"` in form-edit-account.php and
   form-edit-address.php and exposes NO filter to change it, so the panel cannot
   put `.fko-button` on those elements in PHP -- the class has to be joined to
   the standard here instead.

   🔴 JOINED, NOT COPIED. The first attempt (1.26.156) restated the values by
   hand and got two of them wrong: `#10233c` for `var(--fko-navy)`, and weight
   820 where the panel actually renders 400. Sharing the selector means the Save
   buttons cannot drift from "Pull now" again, whatever the standard becomes. */
.fko-button, .fko-secondary-button,
.fko-app .fko-profile-panel button.button { min-height: 39px; padding: 9px 18px; border-radius: 999px; cursor: pointer; font-size: 0.905rem; font-weight: 820; }
.fko-button,
.fko-app .fko-profile-panel button.button { border: 1px solid var(--fko-orange); background: var(--fko-orange); color: var(--fko-navy); }
.fko-button:hover,
.fko-app .fko-profile-panel button.button:hover { border-color: #ff964e; background: #ff964e; }
.fko-secondary-button { border: 1px solid var(--fko-border); background: var(--fko-navy-2); color: var(--fko-white); }
.fko-secondary-button:hover { border-color: var(--fko-orange); color: var(--fko-orange); }
/* 🔴 A LINK WEARING A BUTTON CLASS IS STILL A LINK TO THE CASCADE. `.fko-app a`
   (0,1,1) and the theme's anchor rules out-specify the bare class colours
   above, so `<a class="fko-button">` rendered white-on-orange and the outline
   link white instead of orange (user, 2026-09-24, Support page). The audit
   bar had already patched this for ONE link at its own depth; this states
   the standard once for every link-as-button in the panel: same colours as
   the button, box laid out like a button, no underline.

   🔴 THE FIRST VERSION OF THIS RULE (1.26.552) DID NOT WORK, AND THE REASON
   WAS IN ANOTHER FILE. `flowkeyops-site.css` carries
   `.fko-commerce-main .woocommerce .fko-app a { color: inherit }` -- (0,3,1),
   written to neutralise WooCommerce's orange links inside the panel -- and it
   beat a (0,2,1) rule here whatever the source order. That site rule now
   excludes `a[class*="button"]` (site module 7.16.22). What remains against
   these links is WooCommerce's own `.fko-commerce-main .woocommerce a`
   (0,2,1), which ties with a plain `.fko-app a.fko-button`; the class is
   repeated below to reach (0,3,1) and win regardless of which stylesheet the
   theme prints last. Deliberate, not a typo. */
.fko-app a.fko-button.fko-button, .fko-app a.fko-outline-button.fko-outline-button, .fko-app a.fko-secondary-button.fko-secondary-button { display: inline-flex; align-items: center; justify-content: center; text-decoration: none; }
.fko-app a.fko-button.fko-button, .fko-app a.fko-button.fko-button:hover { color: var(--fko-navy); }
.fko-app a.fko-outline-button.fko-outline-button { color: var(--fko-orange); }
.fko-app a.fko-outline-button.fko-outline-button:hover { color: var(--fko-navy); }
.fko-app a.fko-secondary-button.fko-secondary-button { color: var(--fko-white); }
.fko-app a.fko-secondary-button.fko-secondary-button:hover { color: var(--fko-orange); }
.fko-text-button { padding: 2px; border: 0; background: transparent; color: var(--fko-orange); cursor: pointer; font-size: 0.885rem; font-weight: 800; }

.fko-stat-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; }
.fko-stat, .fko-panel { border: 1px solid #173958; border-radius: 9px; background: var(--fko-navy-2); }
.fko-stat { display: grid; min-height: 132px; align-content: space-between; padding: 16px; border-top: 3px solid var(--fko-border); }
.fko-stat--attention { border-top-color: var(--fko-orange); }
/* The verdict line under a tile's number (user, 2026-09-11): the colour IS the
   message -- green moved/clean, red needs attention, amber unchecked, muted
   nothing to say. The attention tile's NUMBER goes orange too, as in the
   homepage mockup, so the eye lands on the count before the words. */
.fko-stat--attention strong { color: var(--fko-orange); }
.fko-stat .fko-stat-trend { font-weight: 700; }
/* ⚠ `.fko-stat small` (0,1,1) out-specifies a bare `.fko-text-healthy` (0,1,0),
   so the green "changed this week" rendered muted (2026-09-11). Restated at
   the weight the tile needs; .fko-text-danger already wins via `.fko-app`. */
.fko-app .fko-stat small.fko-text-healthy { color: var(--fko-green); }
.fko-app .fko-stat small.fko-text-info { color: var(--fko-blue); }
.fko-app .fko-stat small.fko-text-warning { color: var(--fko-amber); }
.fko-app .fko-stat small.fko-text-accent { color: var(--fko-orange); }
/* The Hourly / Daily / Weekly / Monthly window above the tiles. */
/* Plain text, not pills (user, 2026-09-11): "Hourly | Daily | Weekly | Monthly",
   the current one in orange, the rest white, orange on hover. */
.fko-window-filter { display: flex; flex-wrap: wrap; align-items: center; gap: 0; margin: 0 0 12px; }
.fko-app .fko-window-option { padding: 4px 2px; color: var(--fko-white); font-size: 0.85rem; font-weight: 700; text-decoration: none; }
.fko-app .fko-window-option + .fko-window-option::before { content: "|"; margin: 0 10px; color: var(--fko-white); opacity: .7; font-weight: 400; }
.fko-app .fko-window-option:hover { color: var(--fko-orange); text-decoration: none; }
.fko-app .fko-window-option.is-active { color: var(--fko-orange); }
.fko-stat > span { color: var(--fko-muted); font-size: 0.885rem; font-weight: 700; }
.fko-stat strong { font-size: 2.125rem; line-height: 1; }
.fko-stat small { color: var(--fko-muted); font-size: 0.815rem; }
.fko-two-column { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.fko-panel { padding: 18px; }
/* ⚠ SIBLING PANELS TOUCHED. The grids carry gap: 12px, but panels stacked
   directly in a section had no vertical rhythm, so their 1px borders met and
   read as one tall box (reported 2026-08-16, Environments). Margin on the
   ADJACENT sibling only -- a blanket margin-bottom would also push the last
   panel away from the section end. */
.fko-panel + .fko-panel,
.fko-panel + .fko-section-heading,
.fko-section > .fko-panel + article,
.fko-section > article + .fko-panel { margin-top: 12px; }
/* ⚠ A TWO-COLUMN ROW IS A DIV, so none of the rules above reached it: on
   Settings the Workspace identity / Tenant identifier row sat flush against
   the Secrets vault card above it (user, 2026-09-24, "card spacing"). Same
   12px, both directions, so a row of cards keeps the rhythm of a single one. */
.fko-section > .fko-panel + .fko-two-column,
.fko-section > .fko-two-column + .fko-panel,
.fko-section > .fko-two-column + article { margin-top: 12px; }
/* 🔴 INSIDE a two-column row the sibling rule above is WRONG: the second card
   is a `.fko-panel + .fko-panel` too, so it took the 12px and sat lower than
   the first (user, 2026-09-24: "top align the cards"). The grid's gap is the
   spacing here; the cards share a top edge. (0,3,0) beats the (0,2,0) rule. */
.fko-app .fko-two-column > .fko-panel + .fko-panel { margin-top: 0; }
.fko-panel-heading { margin-bottom: 16px; }
.fko-muted { margin-bottom: 14px !important; color: var(--fko-muted) !important; font-size: 0.905rem; }
.fko-overview-grid { display: grid; grid-template-columns: minmax(0, 1.72fr) minmax(250px, .92fr); gap: 12px; }
.fko-overview-rail { display: grid; gap: 12px; }
/* ROW SEPARATOR STANDARD (user, 2026-09-11): the Overview table sits INSET
   like every other list card. It used `padding: 0` and a hand-padded heading,
   which ran the rows edge to edge -- the retired `fko-table-panel` look under
   another name -- so its hairlines read heavier than the Workflows table beside it. */
.fko-workflow-health { overflow: hidden; }
/* ⚠ The overview grid stretches both columns to the taller one. Without this the
   table sits at its natural height and leaves a dead block beneath it whenever
   the right rail is taller. align-content pushes the rows to fill. */
.fko-workflow-health { display: grid; grid-template-rows: auto minmax(0, 1fr); }
.fko-workflow-health .fko-table-wrap { align-self: stretch; }
.fko-workflow-health .fko-panel-heading { margin: 0 0 12px; }
/* 🔴 SPECIFICITY, NOT SOURCE ORDER. `.fko-table` (line ~893) sets min-width 680px
   at the same weight as this rule and comes LATER, so a bare `.fko-overview-table`
   never won: the Overview's Workflow Health table stayed 680px wide inside a
   ~650px column and grew a horizontal scrollbar (user, 2026-09-12). Scope it
   above the generic rule. Four short columns fit comfortably at 480px. */
.fko-app .fko-table.fko-overview-table { min-width: 480px; }
.fko-overview-table td { padding-top: 13px; padding-bottom: 13px; }
.fko-text-danger { color: #ff6672; }
.fko-donut-row { display: grid; grid-template-columns: 110px minmax(0, 1fr); align-items: center; gap: 8px; }
.fko-donut { position: relative; display: grid; width: 86px; height: 86px; place-items: center; margin: auto; border-radius: 50%; background: conic-gradient(#20bd7e 0 67%, #4e8efa 67% 83%, #ff5662 83% 92%, #ff7a00 92% 100%); }
.fko-donut::after { position: absolute; width: 54px; height: 54px; border-radius: 50%; background: var(--fko-navy-2); content: ""; }
.fko-donut strong { position: relative; z-index: 1; font-size: 1.475rem; }
.fko-legend { display: grid; gap: 7px; color: var(--fko-muted); font-size: 0.815rem; }
.fko-legend span { display: flex; align-items: center; gap: 7px; }
.fko-legend span::before { display: inline-block; width: 7px; height: 7px; margin-right: 6px; border-radius: 50%; background: #20bd7e; content: ""; }
.fko-legend span:nth-child(2)::before { background: #4e8efa; }
.fko-legend span:nth-child(3)::before { background: #ff5662; }
.fko-legend span:nth-child(4)::before { background: #ff7a00; }
.fko-legend b { margin-left: auto; color: var(--fko-white); }
.fko-access-callout { display: flex; align-items: center; justify-content: space-between; gap: 14px; border-left: 3px solid var(--fko-orange); }
.fko-access-callout p { margin: 6px 0 0; color: var(--fko-muted); font-size: 0.825rem; line-height: 1.45; }
.fko-access-callout .fko-secondary-button { flex: 0 0 auto; }

.fko-health-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 8px; }
.fko-health-card { display: grid; min-height: 120px; align-content: center; gap: 8px; padding: 13px; border: 1px solid var(--fko-border); background: var(--fko-navy); text-align: center; }
.fko-health-card--attention { border-color: var(--fko-orange); }
.fko-health-card > span { color: var(--fko-muted); font-size: 0.775rem; font-weight: 850; letter-spacing: .12em; }
.fko-health-card strong { font-size: 0.905rem; }
.fko-text-healthy { color: var(--fko-green); }
.fko-text-warning { color: var(--fko-amber); }
/* 🔑 THE PAGE-GUIDE COLOUR STANDARD NAMES SIX TONES; only two had a text class.
   Added for the Workflows Migrate column, which needs "needs your attention"
   (orange) and "work FlowKeyOps will do" (blue) as distinct from amber.
   ⚠ ACCENT IS ORANGE, NOT AMBER. .fko-text-warning is #f4bd50 and already means
   PARTIAL elsewhere on this page -- reusing it would make two different states
   the same colour in one table, which the guide-card check exists to prevent. */
.fko-text-accent { color: var(--fko-orange); }
.fko-text-info { color: var(--fko-blue); }
/* 🔴 THE OUTCOME COLUMN IS THE ANSWER, so it carries weight (user, 2026-09-01:
   "make the outcome status bolded"). Colour still distinguishes the states;
   the weight only says "this cell is the verdict".
   ⚠ Scoped with .fko-app so it cannot lose to the panel-scoped rules around it:
   the bare .fko-text-* classes are (0,1,0) and anything .fko-app-scoped outranks
   them, which is how a correct-looking colour rule silently does nothing here. */
/* 🔴 MATCH THE SECRETS TABLE (user, 2026-09-01: "make the colored font in the
   table match the size and style of the colored font in the secrets page
   table"). Those cells use .fko-valbadge -- 0.92rem at weight 400 -- while these
   inherited the table's 0.875rem and were forced to 700, so the same kind of
   status word rendered smaller AND heavier one page over.
   ⚠ Metrics restated from .fko-valbadge rather than the class being reused: that
   class also carries padding and border resets tuned for its own column. */
.fko-app .fko-outcome { font-size: 0.92rem; font-weight: 400; }
/* ⚠ .fko-text-danger is UNSCOPED (0,1,0) and sits inside .fko-app. Restated at
   panel scope so the red actually wins on the Outcome column.
   🔑 NOT a colour change -- same #ff6672 as --fko-red, just stated where it can
   hold. The .fko-outline-button override at :1541 is button-only and unaffected. */
.fko-app .fko-text-danger { color: var(--fko-red); }
/* WORKFLOW STATUS VERDICTS (user, 2026-08-21; extended to State 2026-08-22).
   Every state is BOLD except `unknown`, and that exception is the point: bold
   says "this is a verdict we stand behind". `unknown` means we could not
   compare, so it stays muted and unbolded rather than presenting an absence of
   information with the same confidence as a real answer.
   ⚠ The STATE column (Active/Inactive) now follows the same rule for the same
   reason: Active is a positive fact, Inactive is the absence of one, so only
   Active is bold. Both cells that render it -- the dashboard summary table and
   the Workflows table -- must keep the same class or one column silently
   diverges from the other.
   🔑 DEDICATED CLASSES, NOT A CHANGE TO .fko-text-warning / .fko-text-healthy --
   those are used 23+ times in the template and in three JS sites, and
   recolouring them would restyle unrelated messages that are correctly amber.
   Drift is ORANGE because that is the panel's attention colour: amber says "be
   aware", orange says "act". */
/* ⚠ Same as .fko-outcome above, and for the same reason. */
.fko-status-verdict { font-size: 0.92rem; font-weight: 400; }
/* 🔴 EVERY COLOURED CELL IN THIS TABLE, NOT JUST THE TAGGED ONES (user,
   2026-09-01: "Migrate column and Inactive font look smaller than the other
   colored font").
   Active carried .fko-status-verdict and was sized; Inactive is bare
   .fko-muted, and the Migrate column's spans carry only .fko-text-*, so both
   stayed at the table's 0.875rem beside a 0.92rem neighbour.
   🔑 SIZED ON THE CELL, so a new status word inherits it without anyone
   remembering to add a class -- which is exactly how these two were missed. */
.fko-app .fko-workflow-table td .fko-muted,
.fko-app .fko-workflow-table td .fko-text-healthy,
.fko-app .fko-workflow-table td .fko-text-info,
.fko-app .fko-workflow-table td .fko-text-danger {
  font-size: 0.92rem;
}
/* ITEM 3.6 — the drift diff and its anchored actions.
   🔑 THE BAR IS A FLEX SIBLING OF THE SCROLLER, NOT STICKY INSIDE IT.
   `.fko-json-body` is the element with `overflow:auto` (`.fko-json-panel` is a
   flex column with a max-height), so the bar sits OUTSIDE the scrolling region
   and is always in view by construction. `position:sticky` here would do
   nothing at all -- there is no scrolling ancestor for it to stick to -- and
   would read as working while achieving the same result by accident.
   ⚠ `flex: 0 0 auto` so it never shrinks when a long diff fills the body.
   ⚠ A DIFF LINE IS MARKED BY A CHARACTER AS WELL AS A COLOUR (- and +).
   Colour alone fails a colour-blind reader and vanishes in a screenshot. */
.fko-json-resolve { flex: 0 0 auto;
  background: var(--fko-navy, #002048); border-bottom: 1px solid rgba(255,255,255,.14);
  padding: .6rem .9rem; }
/* ⚠ NO `opacity` HERE. It was .8, which is fine for prose but would also fade
   the colour SWATCHES the legend exists to demonstrate -- a muted sample of a
   colour is not that colour. Dim the words instead, and leave the swatches at
   full strength. */
.fko-json-resolve-hint { margin: 0 0 .5rem; font-size: .82rem; }
.fko-json-resolve-hint .fko-diff-legend-row { opacity: .92; }
/* ⚠ NO justify-content HERE, AND THAT IS CHECKED, NOT ASSUMED. `.fko-actions`
   at line ~303 sets `flex-end`, but `.fko-app .fko-actions` (line ~782) has
   higher specificity and omits it, so inside the app the pair already sits
   LEFT -- which is where these belong, beside the diff they act on. Adding
   `flex-start` here would look load-bearing while changing nothing, and would
   silently start fighting the panel-wide rule if that scoping ever changed. */
/* ⚠ The buttons MOVE into the diff's column headers, so this container holds
   no forms while a diff is open. It is hidden by an ATTRIBUTE set in JS, not by
   `:empty` -- the PHP loop leaves newline and tab text nodes inside the div, and
   `:empty` does not match an element containing whitespace. That rule was
   written, tested against the markup, and replaced. */
.fko-json-resolve-actions > form { margin: 0; }
/* The Workflows heading pair: Accept all (secondary) then Pull now (primary).
   ⚠ `.fko-app .fko-actions` omits justify-content, so this sits LEFT unless
   told otherwise -- and here it belongs at the RIGHT of the heading row,
   beside where the single Sync now button used to be. */
.fko-workflow-actions { justify-content: flex-end; margin-top: 0; }
.fko-workflow-actions > form { margin: 0; }
.fko-json-resolve-actions[data-fko-empty] { display: none; }
/* ITEM 3.6 — SIDE-BY-SIDE DIFF, one scrollbar for both panes.
   🔑 ONE GRID, TWO COLUMNS PER ROW -- not two scrolling panes synchronised by
   script. Both halves of a row are children of the same row element, so they
   cannot drift out of step: there is nothing to keep in sync, which is the
   only way this stays correct while the customer scrolls.
   ⚠ THE ROWS ARE ALIGNED BY AN LCS DIFF in JS. Without that, one inserted line
   shifts everything below it and every later row reads as changed.
   ⚠ Horizontal overflow is on the CONTAINER, so long lines scroll both panes
   together rather than letting one wander. */
/* 🔴 NO HORIZONTAL SCROLL (user, 2026-08-21), AND THAT ALSO FIXES THE HEADER
   ALIGNMENT -- they were the same bug. `white-space: pre` let the cells grow
   past the container, so each row's grid was WIDER than the header's grid, and
   the two columns stopped lining up. Wrapping keeps every grid exactly the
   container's width, so the header and every row share one geometry.
   🔑 `pre-wrap` preserves the JSON's indentation while still breaking long
   lines; `overflow-wrap: anywhere` handles the unbroken ones -- a base64 blob
   or a long jmespath expression has no space to break at, and without this it
   would force the scrollbar back on its own. */
/* ⚠ SCOPED TO THE DIFF, NOT THE VIEWER. `overflow-x: hidden` on
   `.fko-json-body` itself would also apply to the PLAIN JSON view, which is
   `white-space: pre` -- long lines there would be CLIPPED instead of scrollable.
   Caught before shipping; the flag is set only while a diff is rendered. */
/* 🔴 BOTH VIEWERS, NOT JUST THE DIALOG'S (user, 2026-09-22: "table title
   columns still not matching row columns").
   This was scoped to `.fko-json-body`, so the ROW's viewer -- which is
   `.fko-json-inline-body` -- never matched it: the diff kept `white-space: pre`
   and horizontal scrolling from the plain JSON view, so the two-column grid
   overflowed its box, the rows slid sideways under a scrollbar and the sticky
   header stayed put. The header looked misaligned; the columns had actually
   moved.
   🔑 A DIFF WRAPS, IT NEVER SCROLLS SIDEWAYS. The columns are a grid sized to
   the box, so any horizontal overflow is a layout fault rather than long
   content -- `overflow-wrap` on the cells already handles a long line. */
/* 🔴 `.fko-app` ON BOTH, AND THAT IS THE WHOLE FIX (user, 2026-09-22: "still
   broken"). `.fko-json-inline-body.fko-json-body--diff` is (0,2,0) -- the SAME
   specificity as `.fko-app .fko-json-inline-body` at the foot of this file, and
   it comes EARLIER, so the later rule's `white-space: pre` won and the diff
   kept scrolling sideways. Scoping this one under `.fko-app` makes it (0,3,0)
   and it wins on specificity rather than on source order, which is the rule
   this stylesheet states: raise specificity, never add !important. */
.fko-app .fko-json-body.fko-json-body--diff,
.fko-app .fko-json-inline-body.fko-json-body--diff { overflow-x: hidden; white-space: normal; }
.fko-diff-split { display: block; width: 100%; }
.fko-diff-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.fko-diff-cell { padding: 0 10px; min-width: 0;
  white-space: pre-wrap; overflow-wrap: anywhere;
  border-left: 1px solid rgba(255,255,255,.07); }
.fko-diff-cell-left { border-left: 0; }
/* 🔑 A BLANK IS VISIBLY A GAP, not an empty line. Without the tint a missing
   counterpart looks like a line the customer simply cannot read. */
.fko-diff-blank { background: rgba(255,255,255,.03); }
.fko-diff-removed { background: rgba(255,122,26,.16); color: var(--fko-orange); }
.fko-diff-added { background: rgba(62,213,152,.14); color: var(--fko-green); }
/* The column headers, sticky so they survive a long scroll. */
/* 🔴 THE STICKY HEADER MUST COVER THE SCROLLER'S OWN PADDING.
   `.fko-json-body` has `padding: 16px 18px`, so `top: 0` pins the header BELOW
   that 16px band -- and code scrolls up through the gap above it, which is
   exactly what a screenshot showed on 2026-08-21.
   🔑 `top: -16px` pins it to the true top of the scrollport, and the negative
   horizontal margins plus matching padding make it span the full width so no
   code is visible beside it either. */
.fko-diff-heads { display: grid; grid-template-columns: 1fr 1fr;
  position: sticky; top: -16px; z-index: 2;
  margin: -16px -18px 0; padding: 16px 18px 0;
  background: var(--fko-navy-2);
  border-bottom: 1px solid rgba(255,255,255,.14);
  /* 🔴 THE COLUMN SPLIT MUST LAND IN THE SAME PLACE AS THE ROWS'.
     The negative margin pulls this grid 18px wider on each side so it can cover
     the scroller's padding -- but that also moves its 1fr|1fr boundary 18px
     left of the rows' boundary, so the header and the code did not line up
     (screenshot, 2026-08-21). `box-sizing: content-box` with the padding
     restored above means the GRID AREA is exactly the rows' width again, while
     the painted background still spans the full scrollport. */
  box-sizing: content-box; }
/* 🔴 THE INLINE VIEWER HAS DIFFERENT PADDING, AND THE HEADER MUST FOLLOW IT
   (user, 2026-09-22: "title columns not aligning with table columns").
   `.fko-diff-heads` above is sized against the DIALOG's `padding: 16px 18px`.
   The row's viewer is `14px 16px`, so the same negative margins overshot by 2px
   on each side -- moving the header's 1fr|1fr boundary off the rows' and
   splitting the titles away from the columns they name.
   🔑 Same technique, this viewer's numbers: the grid area returns to exactly the
   rows' width while the painted background still spans the full scrollport. */
.fko-app .fko-json-inline-body .fko-diff-heads {
  top: -14px;
  margin: -14px -16px 0;
  padding: 14px 16px 0;
}
/* The column header now carries its own action, so it is a row: title and
   consequence on the left, the button that acts on THIS column on the right. */
/* 🔑 THE ACTION SITS ON THE TITLE LINE AND TAKES ONLY THE WIDTH OF ITS TEXT
   (user, 2026-09-22: "autoscaling the buttons based on the button text and
   moving the button to be on the same line as the (Running) title").
   ⚠ A GRID, NOT A FLEX ROW. `auto max-content` is what makes the button size to
   its own label -- `Restore` and `Keep` are different lengths and neither
   should stretch. The title takes what is left, and the consequence line below
   spans both tracks so it reads under the title rather than beside the button.
   ⚠ align-items:start keeps the button on the FIRST line when a long title
   wraps, instead of centring it against a two-line block. */
.fko-diff-head { padding: 8px 10px; font-weight: 700;
  display: grid; grid-template-columns: 1fr max-content;
  align-items: start; column-gap: 10px; row-gap: 2px; }
.fko-diff-head-title { display: block; }
/* ⚠ `flex: 0 0 auto` NOW THE HEADER IS A COLUMN. As `1 1 auto` this block grew
   along the new main axis -- vertically -- so it absorbed the header's spare
   height and pushed the button away from its own title. Width still needs
   `min-width: 0` so a long title wraps instead of widening the column. */
/* The title on line one beside the button; the consequence line beneath both,
   spanning the full header so it is not squeezed into the title's track. */
.fko-diff-head-text { grid-column: 1; grid-row: 1; min-width: 0; }
/* 🔑 RIGHT-ALIGNED IN ITS OWN COLUMN (user, 2026-09-22: "right align the
   button"). The title track is `1fr` so it takes every spare pixel, which
   pushes this `max-content` track hard against the column's right edge -- and
   `justify-self: end` keeps the button there rather than stretched.
   ⚠ STILL max-content, so `Restore` and `Keep` each size to their own label.
   A fixed width would stretch the shorter one. */
.fko-diff-head-action { grid-column: 2; grid-row: 1; justify-self: end; }
.fko-diff-head-action form { margin: 0; }
/* 🔴 NO SIZE OVERRIDE HERE (user, 2026-09-22: "the buttons still do not match
   our standard button size").
   This block previously forced 28px/.8rem so two buttons would not crowd the
   column titles. But Restore and Keep ARE the decision this page exists for,
   and the button standard is explicit that a primary/secondary pair shares one
   set of metrics -- 39px, 0.905rem -- so a shrunk pair here read as a different,
   lesser control than the same pair everywhere else in the panel.
   ⚠ AND REMOVING THE CLASS FROM THE MARKUP COULD NOT FIX IT: this selector is
   (0,3,0) and the standard `.fko-app .fko-outline-button` is (0,2,0), so the
   compact size won on specificity no matter which classes the button carried.
   The header is a grid whose title track is `1fr` and whose action track is
   `max-content`, so a full-size button is absorbed by the title wrapping rather
   than by overflowing -- which is what made the override unnecessary. */
/* The consequence line under each column title: same colour, lighter, so the
   title reads first and the explanation second. */
/* ⚠ ONE RULE FOR THIS CLASS. The placement was briefly split into a second
   block above; two rules for one class at equal specificity resolve by source
   order, which is exactly what this stylesheet's own notes warn is hard to see.
   🔑 Spans BOTH tracks on row 2: the consequence belongs under the title, not
   squeezed into the title's column beside a button. */
.fko-diff-head-sub { display: block; font-weight: 400; opacity: .85;
  font-size: .82rem; grid-column: 1 / -1; grid-row: 2; }
.fko-diff-head-approved { color: var(--fko-orange); }
.fko-diff-head-running { color: var(--fko-green); border-left: 1px solid rgba(255,255,255,.07); }
/* The legend: a swatch shown INLINE beside the words it explains, so the colour
   and its meaning are read together rather than in separate places. */
.fko-diff-legend-row { display: block; }
.fko-diff-legend-swatch { display: inline-block; min-width: 1.4em; padding: 0 .3em;
  border-radius: 3px; font-weight: 700; text-align: center; }
.fko-diff-legend-note { display: block; margin-top: .35rem; opacity: .75; }
/* The row's View JSON when that row is drifted -- the route into the decision. */
.fko-button-attention { border-color: var(--fko-orange); color: var(--fko-orange); }
.fko-drift-verdict { color: var(--fko-orange); font-weight: 700; }
.fko-activity-list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.fko-activity-list li { display: grid; grid-template-columns: 42px 1fr; gap: 10px; padding: 10px 0; border-top: 1px solid var(--fko-border); }
.fko-activity-list li:first-child { border-top: 0; }
.fko-activity-list time { color: var(--fko-orange); font-size: 0.825rem; font-weight: 800; }
.fko-activity-list span { display: grid; gap: 3px; color: var(--fko-muted); font-size: 0.845rem; }
.fko-activity-list strong { color: var(--fko-white); font-size: 0.905rem; }

/* ⚠ The BASE badge is the neutral/"Inactive" state, so it takes the muted grey
   rather than inheriting body text. At near-white it read as emphatic -- an
   inactive workflow looked as prominent as an active one, which inverts the
   signal the column exists to carry. The variants below override the colour. */
.fko-badge { display: inline-flex; width: fit-content; align-items: center; min-height: 23px; padding: 3px 8px; border: 1px solid currentColor; border-radius: 999px; color: var(--fko-muted); font-size: 0.745rem; font-weight: 850; letter-spacing: .03em; text-transform: uppercase; }
.fko-badge--healthy { color: var(--fko-green); }
.fko-badge--warning { color: var(--fko-amber); }
.fko-badge--info { color: var(--fko-blue); }
/* The tone table's "accent": in progress / needs the customer (Support: an
   Active ticket). The badge set had healthy / warning / info only, so Active
   would have fallen to the muted default -- the colour for "no information". */
.fko-badge--accent { color: var(--fko-orange); }
/* The tickets card: the heading's count and the pager's page label are muted
   TEXT beside controls, not paragraphs -- no bottom margin. */
.fko-app .fko-support-tickets .fko-panel-heading .fko-muted,
.fko-app .fko-support-tickets .fko-audit-pager .fko-muted { margin-bottom: 0 !important; align-self: center; }
/* Three children: Newer, the page label, Older. The label sits in the middle;
   with no Newer link it stays left of Older rather than drifting. */
.fko-app .fko-support-tickets .fko-audit-pager { align-items: center; }
/* The row's "what needs attention" list: one item per problem, the fix
   beside it (user, 2026-09-25). Same hairline between items as every list. */
.fko-app .fko-wf-why { margin: 0 0 12px; }
.fko-app .fko-wf-why-list { list-style: none; margin: 8px 0 10px; padding: 0; }
.fko-app .fko-wf-why-item { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 10px 0; border-top: 1px solid var(--fko-border); }
.fko-app .fko-wf-why-item > div { flex: 1 1 auto; min-width: 0; }
.fko-app .fko-wf-why-item > div > strong { margin-right: 8px; }
.fko-app .fko-wf-why-item > div > p { margin: 6px 0 0; font-size: 0.875rem; }
.fko-app .fko-wf-why-item > .fko-row-button { flex: none; margin-top: 2px; }
.fko-app .fko-wf-why-item > p.fko-muted { flex: 0 1 38%; margin: 0 !important; font-size: 0.845rem; }
.fko-app .fko-wf-why > p:last-child { margin: 0; font-size: 0.845rem; }
/* A target-side item hosts the credential box under its sentence, so its
   content column takes the whole row and the box sits beneath the text. */
.fko-app .fko-wf-why-item--target > div { flex: 1 1 100%; }
.fko-app .fko-wf-why-item--target .fko-blocked-cred { margin-top: 10px; }
/* 🔑 TITLE AND ACTION ON ONE ROW, ACTION RIGHT (user, 2026-09-25). The item is
   one grid; its two wrappers are display:contents so their children place on
   it directly: the name and the Set it button share row 1 (button right),
   the explanation and the credential box span the rows below. The button's
   parent is still the box's wrapper, which is how the shared editor finds its
   panel -- so the DOM the script relies on is unchanged. */
.fko-app .fko-wf-why-item.fko-wf-why-grid { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 16px; align-items: center; }
.fko-app .fko-wf-why-grid > div, .fko-app .fko-wf-why-grid > .fko-blocked-cred { display: contents; }
.fko-app .fko-wf-why-grid strong { grid-column: 1; grid-row: 1; margin-right: 0; }
.fko-app .fko-wf-why-grid [data-fko-expand-mapfield] { grid-column: 2; grid-row: 1; justify-self: end; }
.fko-app .fko-wf-why-grid > div > p { grid-column: 1 / -1; grid-row: 2; }
.fko-app .fko-wf-why-grid [data-fko-mapfield-panel] { grid-column: 1 / -1; grid-row: 3; margin-top: 10px; }
/* Create mode: no source column or arrow, so the row is label + field. */
.fko-app .fko-mapfield--create .fko-mapfield-row { grid-template-columns: minmax(120px, 1fr) minmax(200px, 3fr); }
/* The expanded row under a ticket: the conversation, one message per card,
   oldest first. ⚠ NOT display:flex -- this is content, not a control strip,
   so the right-aligned-controls rule for `*-panel-body` does not apply. */
.fko-app .fko-ticket-panel-body { padding: 12px 14px 16px; }
.fko-app .fko-ticket-panel-body .fko-sync-status { align-items: center; }
.fko-app .fko-ticket-panel-body .fko-sync-status p { margin-bottom: 0 !important; font-size: 0.845rem; }
.fko-app .fko-ticket-thread { display: grid; gap: 10px; margin-top: 10px; }
.fko-app .fko-ticket-msg { padding: 12px 14px; border: 1px solid var(--fko-border); border-radius: 6px; background: var(--fko-navy); }
/* A support reply carries the attention colour on its left edge; the
   customer's own messages and system activity stay plain. */
.fko-app .fko-ticket-msg--agent { border-left: 3px solid var(--fko-orange); }
.fko-app .fko-ticket-msg--system { color: var(--fko-muted); font-style: italic; }
.fko-app .fko-ticket-msg-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 6px; color: var(--fko-muted); font-size: 0.825rem; }
.fko-app .fko-ticket-msg-head strong { color: var(--fko-white); }
.fko-app .fko-ticket-msg-head > span:last-child { margin-left: auto; }
.fko-app .fko-ticket-msg-body { font-size: 0.905rem; line-height: 1.55; overflow-wrap: anywhere; }
.fko-app .fko-ticket-msg-body p { margin: 0 0 8px; }
.fko-app .fko-ticket-msg-body p:last-child { margin-bottom: 0; }
.fko-app .fko-ticket-msg-body img { max-width: 100%; height: auto; }
/* Screenshots on a message: thumbnails that open the signed original. */
.fko-app .fko-ticket-attachments { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.fko-app .fko-ticket-shot img { display: block; max-width: 180px; max-height: 130px; border: 1px solid var(--fko-border); border-radius: 4px; background: var(--fko-navy-2); }
.fko-app .fko-ticket-shot--file { display: inline-block; padding: 6px 10px; border: 1px solid var(--fko-border); border-radius: 4px; color: var(--fko-orange); font-size: 0.845rem; text-decoration: none; }
/* The reply box under the conversation: separated by the hairline, laid out
   like the ticket form above it. */
.fko-app .fko-ticket-reply { display: grid; gap: 12px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--fko-border); }
.fko-app .fko-ticket-reply .fko-sync-status { align-items: center; }
.fko-app .fko-ticket-reply .fko-sync-status p { margin-bottom: 0 !important; font-size: 0.845rem; }
/* A file input is a control the customer presses, but it is still a field:
   same box as the text inputs, the button the browser draws inside it. */
.fko-field-row input[type="file"] { padding: 6px 10px; cursor: pointer; }
.fko-field-row input[type="file"]::file-selector-button { margin-right: 10px; padding: 4px 12px; border: 1px solid var(--fko-border); border-radius: 999px; background: var(--fko-navy-2); color: var(--fko-white); font: inherit; font-size: 0.845rem; cursor: pointer; }
.fko-field-row input[type="file"]::file-selector-button:hover { border-color: var(--fko-orange); color: var(--fko-orange); }
/* Screenshots waiting to be sent -- pasted or chosen -- as thumbnails with a
   remove control each. Sits under the file input, inside its label. */
.fko-app .fko-shot-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.fko-app .fko-shot-item { position: relative; display: block; }
.fko-app .fko-shot-item img { display: block; width: 96px; height: 72px; object-fit: cover; border: 1px solid var(--fko-border); border-radius: 4px; background: var(--fko-navy-2); }
.fko-app .fko-shot-item .fko-icon-button { position: absolute; top: -6px; right: -6px; width: 22px; height: 22px; padding: 0; border-radius: 999px; background: var(--fko-navy-2); line-height: 1; }
.fko-app [data-fko-shot-note] { margin: 0 !important; font-size: 0.805rem; }
/* The strip sits between the message and the actions with the form's own
   grid gap; no field of its own any more (user, 2026-09-25). */
.fko-app .fko-shot-list { margin-top: 0; }
/* While an image is dragged over the message box, say it will be taken. */
.fko-app .fko-field-row textarea.is-over { border-color: var(--fko-orange); border-style: dashed; }
.fko-app .fko-support-tickets .fko-audit-pager > .fko-muted:first-child { margin-right: auto; }
.fko-card-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.fko-environment-card { display: flex; min-height: 316px; flex-direction: column; }
.fko-environment-card .fko-secondary-button { width: 100%; margin-top: auto; }
.fko-definition-list { display: grid; gap: 0; margin: 0 0 18px; }
/* The 18px below a list exists to space whatever FOLLOWS it. When nothing does,
   it stacked on the card's own padding and the last row floated 36px above the
   card's edge -- the Workspace identity card looked taller than its content. */
.fko-panel > .fko-definition-list:last-child { margin-bottom: 0; }
.fko-definition-list div { display: flex; justify-content: space-between; gap: 12px; padding: 9px 0; border-top: 1px solid var(--fko-border); }
.fko-definition-list dt { color: var(--fko-muted); font-size: 0.865rem; }
.fko-definition-list dd { margin: 0; color: var(--fko-white); font-size: 0.865rem; font-weight: 750; text-align: right; }

/* Billing -- the two column sub-headings and the postal address block.
   🔑 Only two rules, because everything else on the Billing screen reuses
   .fko-panel / .fko-two-column / .fko-definition-list / .fko-badge. A screen
   that needs its own layout primitives is a screen that will drift from the
   others the first time one of them is restyled. */
/* Subscription actions (user, 2026-08-24). ⚠ SPACED OFF THE FIGURES ABOVE: the
   button sat flush under "Recurring total", reading as part of the list rather
   than as an action on it -- and Cancel is not a control to place ambiguously. */
.fko-sub-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 18px;
}
.fko-billing-subhead { margin: 0 0 10px; color: var(--fko-white); font-size: 0.925rem; font-weight: 820; }
/* ⚠ line-height carries the <br/>-separated address Woo returns; the muted
   colour matches .fko-definition-list dt so the block reads as one record. */
.fko-billing-address { margin: 0; color: var(--fko-muted); font-size: 0.865rem; line-height: 1.7; }

/* Breathing room between the two billing columns and around their contents.
   🔑 SCOPED, not a change to .fko-two-column. That grid is shared with the
   Settings screen (and anything added later), where the columns are separate
   bordered .fko-panel cards that already read as distinct. These two are bare
   divs inside ONE panel with nothing between them, which is why they needed
   the space -- widening the shared rule would push apart screens that did not
   ask for it. */
.fko-billing-columns { gap: 40px; }
/* ⚠ Scoped to the Billing section rather than .fko-panel, which every screen
   uses. This card packs two columns and up to six rows, so it needs more room
   inside and more separation from the next subscription than the shared
   18px/12px gives; a customer with several subscriptions sees a stack. */
.fko-app .fko-billing-card { padding: 24px; }
/* 🔴 SPECIFICITY, not preference: `.fko-section > article + .fko-panel` (~line
   250) sets the shared 12px and OUT-SPECIFIES a bare
   `.fko-billing-card + .fko-billing-card`, so a simpler selector would lose
   silently and the spacing would never change. Matching that shape with the
   file's existing `.fko-app` prefix is what makes this rule actually apply. */
.fko-app .fko-section > article + .fko-billing-card { margin-top: 18px; }
/* ⚠ The divider is a LEFT border on the second column, not a gap trick: it
   stays put when the grid collapses to one column at 760px, where it
   becomes the top border instead. */
.fko-billing-columns > div + div { padding-left: 40px; border-left: 1px solid var(--fko-border); }
/* The last row of a definition list carries no border, so the button below it
   would otherwise sit tight against the final value. */
.fko-billing-columns .fko-definition-list { margin-bottom: 0; }
.fko-billing-columns .fko-secondary-button { margin-top: 18px; }
/* Address sits below the email/phone list; without this it collides with the
   list's last bottom border. */
.fko-billing-columns .fko-billing-address { margin-top: 16px; }

/* The badge and the collapse control share the right-hand side of the heading.
   ⚠ `.fko-panel-heading` is `justify-content: space-between`, so without this
   wrapper the expander would push the badge away from it to the far edges. */
.fko-billing-heading-controls { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }
/* A collapsed card's heading IS the card, so it keeps no bottom margin --
   otherwise a collapsed card carries 16px of empty space under one line. */
.fko-app .fko-billing-card.is-collapsed .fko-panel-heading { margin-bottom: 0; }
/* 🔴 DEFENDS THE `hidden` ATTRIBUTE. `.fko-billing-body` is a bare wrapper the
   toggle shows and hides with `hidden` alone -- but `hidden` is only
   `display: none` from the UA stylesheet, so ANY later rule giving this element
   a `display` would beat it and a "collapsed" card would render fully expanded.
   Stating it here means the collapse cannot be undone by a distant rule. */
.fko-app .fko-billing-body[hidden] { display: none; }

/* A cancelled/expired/suspended subscription is history, not a live record.
   Dimming the whole card to one grey lets a customer with several
   subscriptions find the active one without reading a single status badge.

   🔑 THE BADGE IS UNTOUCHED, and must stay that way. It is the one element
   whose job is to state the status, so dimming it would grey out the very word
   explaining why the card is grey. Nothing here targets it: `.fko-badge` sets
   its own `color`, which beats the inherited one -- so this works by NOT
   naming it, and a future rule that sets a colour on the card's descendants
   generically would break that.

   ⚠ These override colours set on .fko-definition-list dt/dd and
   .fko-billing-subhead, which are plain single-class rules -- so `.fko-app`
   plus the card class is enough to win without !important. */
.fko-app .fko-billing-card.is-inactive { color: var(--fko-muted); }
/* 🔑 THE HEADING DIMS TOO (user, 2026-08-22: "do the first line as well").
   `.fko-app h3` is `color: inherit`, so the card's colour already reaches it --
   but ONLY because nothing else sets one. Naming it explicitly means a future
   `.fko-panel h3 { color: ... }` cannot silently leave the title at full
   strength on a dimmed card. */
.fko-app .fko-billing-card.is-inactive h3 { color: var(--fko-muted); }
.fko-app .fko-billing-card.is-inactive .fko-billing-subhead,
.fko-app .fko-billing-card.is-inactive .fko-definition-list dd { color: var(--fko-muted); }
/* dt and the address are already muted; drop them a step further so the card
   still has internal hierarchy rather than flattening to one grey. */
.fko-app .fko-billing-card.is-inactive .fko-definition-list dt,
.fko-app .fko-billing-card.is-inactive .fko-billing-address { color: #7d8da3; }
/* ⚠ The row separators come from the same border token as the live card; a
   dimmed card with full-strength rules reads as louder than the active one
   above it. */
.fko-app .fko-billing-card.is-inactive .fko-definition-list div { border-top-color: #1b3654; }
/* 🔑 The BUTTON also keeps full contrast, for the same reason as the badge: it
   sets its own colour. That is correct -- "Manage subscription" still works on
   a cancelled record (billing history, resubscribing), and a dimmed control
   would read as disabled when it is not. */

/* PROFILE -- WooCommerce's own edit-account form, restyled for the dark panel.

   🔴 THE THEME OUT-SPECIFIED THE FIRST VERSION OF THIS BLOCK (fixed
   2026-08-23). Every rule was `.fko-profile-panel <thing>` (0,2,0 / 0,2,1),
   and the labels rendered near-black on navy while the inputs stayed white
   boxes overflowing their column -- the CSS looked undeployed. This is the
   SAME trap already documented at `.fko-app p.fko-orphan-note` and at the
   `.screen-reader-text:focus` block: theme and WooCommerce stylesheets load
   AFTER this one, so at equal specificity theirs win.

   🔑 THE FIX IS SPECIFICITY, NOT ORDER -- we cannot control enqueue order
   across plugins and themes. Two changes, both deliberate:
     1. `.fko-app` prefix everywhere, the same scope the rest of this file uses
        to win these fights.
     2. Target WOO'S OWN CLASSES (`.input-text`, `.woocommerce-Input`) rather
        than only `input[type=...]`. The theme styles `.input-text`; a bare
        attribute selector ties with it and loses on source order.

   ⚠ RAISE SPECIFICITY, NEVER ADD !important -- two !important rules resolve by
   source order, which is the thing that is hard to see in a file this size.

   ⚠ Still scoped to the profile panel so none of this reaches the Woo forms on
   the rest of the site -- checkout, addresses and login share these class
   names. */
.fko-app .fko-profile-panel .woocommerce-EditAccountForm {
  margin-top: 16px;
  /* Woo uses `.form-row-first`/`.form-row-last` floats for the two-up name row;
     a grid reproduces that without the clearfix divs the template also emits. */
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 20px;
}
/* ⚠ THE ADDRESS FORM NEEDS THE SAME GRID. Its wrapper is
   `.woocommerce-address-fields__field-wrapper`, not `.woocommerce-EditAccountForm`,
   and its fields carry the SAME `form-row-first`/`-last`/`-wide` classes (set in
   WC_Countries::get_address_fields). Without this the two-up rows -- first/last
   name, and several country layouts -- stack full width instead of pairing. */
.fko-app .fko-profile-panel .woocommerce-address-fields__field-wrapper {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 20px;
}
.fko-app .fko-profile-panel .form-row { margin: 0 0 16px; padding: 0; }
.fko-app .fko-profile-panel .form-row-first { grid-column: 1; }
.fko-app .fko-profile-panel .form-row-last { grid-column: 2; }
/* Everything else spans both columns. ⚠ `fieldset` and the hook-injected rows
   are included so a field another plugin adds through
   woocommerce_edit_account_form_fields lands full width rather than squeezing
   into half a row. */
.fko-app .fko-profile-panel .form-row-wide,
.fko-app .fko-profile-panel .woocommerce-EditAccountForm > fieldset,
.fko-app .fko-profile-panel .woocommerce-EditAccountForm > p { grid-column: 1 / -1; }
/* ⚠ The submit row is the ONE <p> that is not a .form-row, so it carries no
   bottom margin of its own and sat tight under the password box. Selected by
   :not(.form-row) rather than by position, so a field another plugin appends
   through woocommerce_edit_account_form does not silently inherit it. */
.fko-app .fko-profile-panel .woocommerce-EditAccountForm > p:not(.form-row) { margin: 6px 0 0; }
/* ⚠ Woo emits `<div class="clear">` between float rows; with a grid they would
   occupy cells and open gaps. */
.fko-app .fko-profile-panel .woocommerce-EditAccountForm > .clear { display: none; }
/* The address form's submit sits outside the field grid, in
   `.woocommerce-address-fields`; give it the same breathing room as the
   account form's button row. */
.fko-app .fko-profile-panel .woocommerce-address-fields > p { margin: 6px 0 0; }

/* 🔴 THE LABELS. These are what rendered black-on-navy. */
.fko-app .fko-profile-panel .form-row label,
.fko-app .fko-profile-panel .woocommerce-EditAccountForm label {
  display: block; margin-bottom: 4px;
  color: var(--fko-muted); font-size: 0.825rem; font-weight: 700;
}
.fko-app .fko-profile-panel .form-row .required { color: var(--fko-orange); border: 0; text-decoration: none; }

/* 🔴 THE INPUTS. `.input-text` and `.woocommerce-Input` are named because the
   theme styles those classes; matching only on `input[type=...]` loses to them.
   ⚠ `box-sizing` is stated explicitly -- a padded 100%-wide input otherwise
   overflows its grid column, which is exactly what shipped. */
/* 🔴 SCOPED TO THE PANEL, NOT TO `.woocommerce-EditAccountForm` (2026-08-23).
   The billing-address tab renders Woo's ADDRESS form, whose wrapper is
   `.woocommerce-address-fields` -- it has no EditAccountForm class, so a rule
   keyed to that one styles the details tab and silently skips the address tab.
   Keying to `.fko-profile-panel` covers both forms with ONE definition, and any
   form a later tab adds, instead of a second block to keep in sync.
   ⚠ `select` is included for the country/state dropdowns, which only exist on
   the address form. */
.fko-app .fko-profile-panel input.input-text,
.fko-app .fko-profile-panel input.woocommerce-Input,
.fko-app .fko-profile-panel input[type="text"],
.fko-app .fko-profile-panel input[type="email"],
.fko-app .fko-profile-panel input[type="password"],
.fko-app .fko-profile-panel input[type="tel"],
.fko-app .fko-profile-panel select {
  box-sizing: border-box; width: 100%; max-width: 100%; margin: 0; padding: 8px 10px;
  border: 1px solid var(--fko-border); border-radius: 5px;
  background: var(--fko-navy); color: var(--fko-white);
  font-family: inherit; font-size: 0.905rem; line-height: 1.4;
}
/* ⚠ The option list is painted by the OS on its own white surface, so white
   text there is invisible -- the same trap the environment selector documents. */
.fko-app .fko-profile-panel select option { background-color: #ffffff; color: #0a2848; }
/* ⚠ The placeholder inherits the theme's colour otherwise, which is tuned for
   a white field. */
.fko-app .fko-profile-panel input::placeholder { color: var(--fko-muted); opacity: 1; }
.fko-app .fko-profile-panel input:focus-visible,
.fko-app .fko-profile-panel select:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: 1px; }
/* 🔴 CHROME'S AUTOFILL PAINTS ITS OWN WHITE BACKGROUND and ignores `background`.
   An inset shadow is the only thing that reliably covers it, and the text
   colour needs its own property -- without this the saved email renders black
   on white the moment the browser fills it. */
.fko-app .fko-profile-panel input:-webkit-autofill {
  -webkit-text-fill-color: var(--fko-white);
  -webkit-box-shadow: 0 0 0 1000px var(--fko-navy) inset;
  caret-color: var(--fko-white);
}

/* The "how your name will be displayed" hint under Display name. */
.fko-app .fko-profile-panel .form-row span em,
.fko-app .fko-profile-panel #account_display_name_description,
.fko-app .fko-profile-panel #account_display_name_description em {
  display: block; margin-top: 5px;
  color: var(--fko-muted); font-size: 0.8rem; font-style: normal;
}
/* 🔑 THE PASSWORD GROUP IS SEPARATED FROM THE IDENTITY FIELDS. Changing your
   name and changing your password are two different intentions, and the form
   ran them together as one column of inputs. A rule and real space above the
   Password change box say "new section" before the legend has to.

   🔴 THE RULE IS THE EMAIL ROW'S OWN `border-bottom`, not a positioned
   pseudo-element and not a border on anything the fieldset owns.

   ⚠ The ::before version (1.26.161) rendered MIS-CENTRED. An absolutely
   positioned pseudo-element resolves its offsets against the fieldset's
   PADDING box, which EXCLUDES the element's own 1px borders -- so the line and
   the box below it cannot share an edge by construction. Chasing that with
   nudged offsets is how a 1px discrepancy becomes a magic number nobody can
   later justify.

   ⚠ And there is NO `<div class="clear">` between the email row and the
   fieldset to hang it on -- Woo emits three, but they fall after the name row,
   after Display name, and after the fieldset. A selector keyed to one there
   would have matched nothing and the divider would have silently disappeared.
   (Checked against the rendered markup, not assumed.)

   🔑 A border on a full-width GRID ITEM is exactly the width of the column, so
   the line, the fields above it and the box below it all share one edge at any
   form width, with no second box to keep in sync.

   ⚠ Keyed to `#account_email`'s row via :has() rather than to a position, so
   a field another plugin inserts through woocommerce_edit_account_form_fields
   does not inherit the divider or push it somewhere arbitrary. */
.fko-app .fko-profile-panel .woocommerce-EditAccountForm > .form-row:has(#account_email) {
  margin-bottom: 34px; padding-bottom: 34px;
  border-bottom: 1px solid var(--fko-border);
}
.fko-app .fko-profile-panel .woocommerce-EditAccountForm fieldset {
  margin: 0 0 16px; padding: 20px 18px 6px;
  border: 1px solid var(--fko-border); border-radius: 8px;
}
.fko-app .fko-profile-panel .woocommerce-EditAccountForm legend {
  padding: 0 6px; color: var(--fko-white); font-size: 0.925rem; font-weight: 820;
}
/* The submit button, matched to the panel's primary action.
   ⚠ `.woocommerce-Button` is named for the same reason as `.input-text`. */
/* ⚠ The account form's button also carries `woocommerce-Button`; the address
   form's carries only `button`. Both are covered by the `button.button` selector
   joined to `.fko-button` above -- nothing further is needed here, and adding a
   second definition is exactly what drifted last time. */
/* Woo's notices, printed by the section when a save fails validation.
   ⚠ Woo's own list markup carries bullets and left padding from the theme. */
/* ⚠ `.fko-app` prefixed for the same reason as the form above: Woo's own
   `.woocommerce-error`/`.woocommerce-message` rules load after this stylesheet
   and would otherwise repaint these for a light theme. These carry validation
   text the customer MUST be able to read -- a failed save is the one moment
   this section has something urgent to say. */
.fko-app .fko-woo-notices ul { margin: 0 0 14px; padding: 0; list-style: none; }
.fko-app .fko-woo-notices li { margin: 0 0 8px; padding: 10px 12px; border-left: 3px solid var(--fko-red); border-radius: 4px; background: rgba(255, 102, 114, 0.13); color: var(--fko-white); font-size: 0.885rem; }
.fko-app .fko-woo-notices .woocommerce-message li,
.fko-app .fko-woo-notices li.woocommerce-message { border-left-color: var(--fko-green); background: rgba(74, 222, 128, 0.10); }
/* Woo wraps notices in its own <ul class="woocommerce-error"> etc.; strip the
   theme's list styling from the wrapper too. */
.fko-app .fko-woo-notices .woocommerce-error,
.fko-app .fko-woo-notices .woocommerce-message,
.fko-app .fko-woo-notices .woocommerce-info { margin: 0 0 14px; padding: 0; border: 0; background: none; list-style: none; }

@media (max-width: 760px) {
  /* One column: the two-up name row has nowhere to go on a phone. */
  /* ⚠ Must carry the SAME `.fko-app` prefix as the desktop rules above, or
     these lose to them and the two-up name row stays side by side on a phone. */
  .fko-app .fko-profile-panel .woocommerce-EditAccountForm,
  .fko-app .fko-profile-panel .woocommerce-address-fields__field-wrapper { grid-template-columns: 1fr; }
  .fko-app .fko-profile-panel .form-row-first,
  .fko-app .fko-profile-panel .form-row-last { grid-column: 1; }
}

/* 🔴 ROW SEPARATOR STANDARD (user, 2026-09-11). A list of records is ONE
   `.fko-panel` card with a `.fko-panel-heading`, and rows are separated by the
   single hairline below -- `1px solid var(--fko-border)` on `.fko-table th/td`,
   last row bare. `.fko-table-panel` (padding:0, edge-to-edge) and
   `.fko-table-toolbar` (a boxed header bar) were REMOVED: together they made a
   table read as three stacked boxes. Do not reintroduce them; do not draw a
   second rule on `tr`; do not give a row its own card. Lists that are not
   tables (`.fko-activity-list li`, `.fko-audit-list li`,
   `.fko-definition-list div`) use the SAME token at the same weight.
   tools/check-row-separators.php enforces all of this. */
.fko-table-wrap { overflow-x: auto; }
.fko-table { width: 100%; min-width: 680px; margin: 0; border: 0; border-collapse: collapse; color: var(--fko-white); }
.fko-table th, .fko-table td { padding: 13px 15px; border-width: 0 0 1px; border-style: solid; border-color: var(--fko-border); background: transparent; text-align: left; vertical-align: middle; }
.fko-table th { color: var(--fko-muted); font-size: 0.775rem; font-weight: 850; letter-spacing: .08em; text-transform: uppercase; }
/* 🔑 0.92rem, THE STATUS TEXT SIZE (user, 2026-09-26: "Source is smaller"). Once
   status words were raised to 0.92rem, plain cell text beside them at 0.875rem
   read as a mismatch in the same row. All table body text is one size. */
.fko-table td { font-size: 0.92rem; }
.fko-table td strong, .fko-table td small { display: block; }
.fko-table td small { margin-top: 3px; color: var(--fko-muted); font-size: 0.785rem; }
.fko-table tbody tr:last-child td { border-bottom: 0; }
.fko-table tbody tr:hover td { background: var(--fko-navy-3); }

/* 🔴 EVERY EXPANDED ROW SHARES ONE BACKGROUND (user, 2026-09-01: "let's make all
   expand areas background this colour" -- pointing at the Secrets expander).
   Cells are `transparent` by default, so an expanded panel showed whatever was
   behind it and the three expanders did not agree with each other.
   🔑 KEYED ON THE data-* ATTRIBUTES the panels already carry, not on new classes:
   these three ARE the set of expandable rows, so a fourth added later inherits
   the treatment only if it follows the same convention -- which is the intent. */
/* 🔴 EVERY EXPANDED ROW SHARES ONE BACKGROUND (user, 2026-09-01), and it is
   LIGHTER than the table -- neither of the first two attempts was.
   navy-2 was IDENTICAL to the .fko-panel card behind it, so the rule applied,
   won on specificity, and changed nothing visible. navy went the wrong way and
   darkened it. A rule can be correct and still be a no-op.
   🔑 navy-3 (#153458) is this panel's existing "raised" shade -- the same one
   row hover uses -- so an open expander reads as lifted off the table.
   ⚠ Keyed on the data-* attributes the panels already carry, not on new classes:
   these three ARE the expandable rows, so a fourth inherits the treatment only
   by following the same convention. */
.fko-app tr[data-fko-workflow-panel] > td,
.fko-app tr[data-fko-credential-panel] > td,
.fko-app tr[data-fko-mapfield-panel] > td {
  background: var(--fko-navy-3);
}
/* 🔑 AN OPEN ROW IS ONE SURFACE WITH ITS PANEL, INSIDE A 1px GREY FRAME (user,
   2026-09-12, final: "remove all orange borders and leave as grey"). The summary row takes the panel's navy-3, the hairline between
   row and panel goes, and the frame wraps both as one block.
   🔴 DRAWN WITH THE CELLS' OWN BORDERS, NOT box-shadow. The first attempt used
   inset box-shadows on the <td>s and the user saw no frame at all: a
   border-collapse table does not reliably paint cell shadows. Every cell already
   has `border-style: solid` from the base hairline rule, so only WIDTHS and
   COLOUR change here -- top/left/right on the row, left/right/bottom on the
   panel, nothing at the join, so the frame never doubles.
   🔑 KEYED ON THE EXPANDER'S OWN aria-expanded STATE via :has(), so no row class
   and no script change: the workflow, credential and mapping-field expanders all
   inherit it. ⚠ aria-expanded, not .is-open: the collapse-marker check treats a
   `.fko-expander.is-open` selector as a minus-bar painter. ⚠ Selectors omit
   `.fko-table` on purpose: the row-separator check inspects `.fko-table tr/td`
   rules for competing `border:` shorthands, and these set widths only. */
.fko-app tr:has(> td > .fko-expander[aria-expanded="true"]) > td,
.fko-app tr:has(> td > .fko-expander[aria-expanded="true"]):hover > td {
  background: var(--fko-navy-3);
  border-color: var(--fko-border);
  border-width: 1px 0 0;
}
.fko-app tr:has(> td > .fko-expander[aria-expanded="true"]) > td:first-child { border-left-width: 1px; }
.fko-app tr:has(> td > .fko-expander[aria-expanded="true"]) > td:last-child { border-right-width: 1px; }
.fko-app tr:has(> td > .fko-expander[aria-expanded="true"]) + tr[data-fko-workflow-panel] > td,
.fko-app tr:has(> td > .fko-expander[aria-expanded="true"]) + tr[data-fko-credential-panel] > td,
.fko-app tr:has(> td > .fko-expander[aria-expanded="true"]) + tr[data-fko-mapfield-panel] > td {
  border-color: var(--fko-border);
  border-width: 0 1px 1px;
}
/* ⚠ HOVER MUST NOT REPAINT AN OPEN PANEL. `.fko-table tbody tr:hover td` also
   sets navy-3, so these agree today -- but stating it keeps the panel stable if
   the hover shade ever changes. A hover cue belongs on a row you can click, not
   on the panel it revealed. */
.fko-app .fko-table tbody tr[data-fko-workflow-panel]:hover > td,
.fko-app .fko-table tbody tr[data-fko-credential-panel]:hover > td,
.fko-app .fko-table tbody tr[data-fko-mapfield-panel]:hover > td {
  background: var(--fko-navy-3);
}

.fko-deployment { max-width: 850px; }
.fko-pipeline { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 24px 0; padding: 0; list-style: none; }
.fko-pipeline li { position: relative; display: grid; justify-items: center; gap: 7px; padding: 18px 8px; border: 1px solid var(--fko-border); text-align: center; }
.fko-pipeline li > span { display: grid; width: 28px; height: 28px; place-items: center; border: 1px solid var(--fko-border); color: var(--fko-muted); font-size: 0.825rem; font-weight: 850; }
.fko-pipeline li.is-complete > span { border-color: var(--fko-green); color: var(--fko-green); }
.fko-pipeline li.is-current { border-color: var(--fko-orange); }
.fko-pipeline li.is-current > span { border-color: var(--fko-orange); background: var(--fko-orange); color: var(--fko-navy); }
.fko-pipeline small { color: var(--fko-muted); font-size: 0.805rem; }
.fko-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }

.fko-drift-card { max-width: 850px; border-color: var(--fko-orange); }
.fko-drift-alert { display: flex; gap: 12px; align-items: center; padding-bottom: 18px; border-bottom: 1px solid var(--fko-border); }
.fko-drift-alert > span { display: grid; width: 36px; height: 36px; flex: 0 0 36px; place-items: center; border: 2px solid var(--fko-orange); color: var(--fko-orange); font-weight: 900; }
.fko-drift-alert p { margin: 4px 0 0; color: var(--fko-muted); font-size: 0.885rem; }
.fko-comparison { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 18px 0; }
.fko-comparison > div { display: grid; gap: 8px; padding: 18px; border: 1px solid var(--fko-border); background: var(--fko-navy); }
.fko-comparison span, .fko-comparison small { color: var(--fko-muted); font-size: 0.825rem; }

/* Audit filters and pagination (item 3.8).
   ⚠ Scoped to .fko-audit-* only. The dashboard shares generic classes across
   nine sections, so a bare `select`/`input` rule here would restyle every other
   screen -- and a change that looks correct in one tab breaks three others. */
/* The filter bar heads the ONE audit card (user, 2026-09-11); the hairline
   under it separates the filters from the legend and calendar/list. */
.fko-audit-filters { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; margin: 0 0 14px; padding-bottom: 16px; border-bottom: 1px solid var(--fko-border); }
.fko-audit-state { padding: 4px 0; }
.fko-audit-list-card { margin-top: 4px; }
/* Calendar | List, left of Activity (user, 2026-09-11): the same pill group as
   the Overview's window switch. */
/* Plain text, not pills (user, 2026-09-11): "Calendar | List", same look as the
   Overview's window switch. */
.fko-view-toggle { display: flex; align-items: center; gap: 0; align-self: flex-end; padding-bottom: 8px; }
.fko-app .fko-view-option { padding: 4px 2px; color: var(--fko-white); font-size: 0.85rem; font-weight: 700; text-decoration: none; }
.fko-app .fko-view-option + .fko-view-option::before { content: "|"; margin: 0 10px; color: var(--fko-white); opacity: .7; font-weight: 400; }
.fko-app .fko-view-option:hover { color: var(--fko-orange); text-decoration: none; }
.fko-app .fko-view-option.is-active { color: var(--fko-orange); }
/* ⚠ The Clear link rendered white: an anchor inherits the theme's link colour at
   a weight the outline rule did not beat. Named here at the bar's own depth. */
.fko-app .fko-audit-filters .fko-audit-filter-actions a.fko-outline-button { color: var(--fko-orange); }
.fko-app .fko-audit-filters .fko-audit-filter-actions a.fko-outline-button:hover { color: var(--fko-navy); }
/* The audit calendar: one card, day cells in the page's card colours, the
   legend's icons and colours for the counts. */
.fko-audit-calendar-card { margin-bottom: 14px; }
/* Setup wizard (user, 2026-09-11): three steps in one card. */
.fko-setup-steps { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
/* 🔴 DIRECT CHILDREN ONLY. Step 2 nests the install-steps partial, whose own <ol>
   <li>s were caught by a descendant `li` rule here and turned into 36px/1fr grids:
   every instruction wrapped one word per line under a stray hairline (2026-09-11). */
.fko-setup-steps > li { display: grid; grid-template-columns: 36px 1fr; gap: 14px; padding: 16px 0; border-top: 1px solid var(--fko-border); }
.fko-setup-steps > li:first-child { border-top: 0; }
.fko-setup-steps > li > div > p { margin: 4px 0 10px; color: var(--fko-muted); font-size: 0.885rem; }
.fko-setup-steps > li > div > strong { font-size: 0.95rem; }
.fko-setup-num { display: grid; width: 32px; height: 32px; place-items: center; border: 2px solid var(--fko-border); border-radius: 50%; color: var(--fko-muted); font-weight: 800; }
.fko-setup-steps > li.is-current .fko-setup-num { border-color: var(--fko-orange); color: var(--fko-orange); }
.fko-setup-steps > li.is-done .fko-setup-num { border-color: var(--fko-green); color: var(--fko-green); }
.fko-setup-steps > li.is-todo { opacity: .55; }
.fko-app .fko-notice.fko-setup-wait { margin: 12px 0 0; font-size: 0.885rem; }
.fko-app .fko-notice.fko-setup-wait > p { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0; }
/* The refresh promise shows only while the poll actually runs. */
.fko-setup-auto { display: none; color: var(--fko-muted); }
.fko-setup-wait.is-setup-polling .fko-setup-auto { display: inline; }
/* Guided tour: overlay, spotlight, tooltip. Appended INSIDE .fko-app so it
   renders in fullscreen too. */
.fko-tour-overlay { position: fixed; inset: 0; z-index: 9999; }
/* 🔴 SHADOW ORDER IS PAINT ORDER: the first shadow is on top. With the 9999px
   dimming overlay listed first it painted OVER the 2px orange ring, which read
   as a pale grey outline (reported 2026-09-11). Ring first, overlay beneath. */
.fko-tour-spot { position: fixed; border-radius: 8px; box-shadow: 0 0 0 2px var(--fko-orange), 0 0 0 9999px rgba(3, 12, 29, 0.72); transition: top .2s, left .2s, width .2s, height .2s; pointer-events: none; }
/* "Take a tour" reads as an invitation, not a nav link: bold italic (user, 2026-09-12). */
.fko-app .fko-tour-link { font-weight: 700; font-style: italic; }
/* A page tour has no "don't show again": that box belongs to the auto-offered workspace tour. */
/* The page does not scroll under a running tour; the tour scrolls each stop into view itself. */
html.fko-tour-lock { overflow: hidden; }
.fko-tour-overlay.is-page-tour .fko-tour-never { display: none; }
.fko-tour-tip { position: fixed; z-index: 10000; width: min(360px, calc(100vw - 24px)); padding: 16px 18px; border: 1px solid var(--fko-border); border-radius: 9px; background: var(--fko-navy-2); color: var(--fko-white); box-shadow: 0 12px 40px rgba(0, 0, 0, .5); }
.fko-tour-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.fko-tour-count { color: var(--fko-muted); font-size: 0.78rem; }
.fko-tour-tip h3 { margin: 0 0 6px; font-size: 1.05rem; }
.fko-tour-tip p { margin: 0 0 12px; color: var(--fko-muted); font-size: 0.885rem; line-height: 1.45; }
.fko-tour-never { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; font-size: 0.82rem; color: var(--fko-muted); }
.fko-tour-actions { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
.fko-tour-nav { display: flex; gap: 8px; }
.fko-audit-body.is-loading { opacity: .55; pointer-events: none; transition: opacity .15s; }
.fko-cal-nav { align-items: center; }
.fko-cal-month { margin: 0; font-size: 1.45rem; }
.fko-cal-nav .fko-text-button { font-size: 1rem; }
.fko-cal-month small { margin-left: 6px; font-size: 0.72rem; font-weight: 600; }
.fko-cal-month .fko-cal-narrow { display: block; margin: 2px 0 0; color: var(--fko-orange); font-size: 0.78rem; font-weight: 400; }
.fko-cal-wrap { overflow-x: auto; }
.fko-audit-calendar { display: grid; min-width: 640px; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.fko-cal-head { padding: 4px 8px; color: var(--fko-muted); font-size: 0.72rem; font-weight: 850; letter-spacing: .06em; text-transform: uppercase; }
.fko-cal-day { min-height: 92px; padding: 8px; border: 1px solid var(--fko-border); border-radius: 8px; background: var(--fko-navy); scroll-margin-top: 12px; }
.fko-cal-day.is-blank { border-color: transparent; background: transparent; }
.fko-cal-day.is-empty { opacity: .6; }
.fko-cal-day.is-future { opacity: .35; }
.fko-cal-day.is-today { border-color: var(--fko-blue); }
.fko-cal-day.is-selected { border-color: var(--fko-orange); box-shadow: inset 0 0 0 1px var(--fko-orange); }
/* Normal weight on the day number and ALL (user, 2026-09-11); the counts list
   is a left-aligned column, one category per line. */
.fko-cal-daynum { display: flex; justify-content: space-between; align-items: baseline; font-weight: 400; font-size: 0.9rem; text-align: left; }
.fko-app .fko-cal-all { color: var(--fko-orange); font-size: 0.7rem; font-weight: 400; letter-spacing: .04em; text-decoration: none; }
.fko-app .fko-cal-all:hover { text-decoration: none; opacity: .8; }
.fko-cal-counts { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; margin-top: 8px; text-align: left; }
/* ⚠ Named at the calendar's own depth: an <a> inherits the theme's link colour
   at a weight `.fko-app .fko-cal-count` did not beat (same as Clear). */
.fko-app .fko-audit-calendar a.fko-cal-count, .fko-app .fko-audit-calendar .fko-cal-count { display: inline-flex; align-items: center; gap: 6px; color: var(--fko-white); font-size: 0.8rem; font-weight: 400; text-decoration: none; }
.fko-app .fko-audit-calendar a.fko-cal-count:hover { color: var(--fko-orange); text-decoration: none; }
.fko-app .fko-audit-calendar a.fko-cal-all { color: var(--fko-orange); }
/* ⚠ flex: 0 0 auto, or the flex row squeezes the circle into an ellipse -- the
   icons rendered visibly skewed next to four-digit counts (2026-09-11). */
.fko-cal-count .fko-audit-icon { flex: 0 0 20px; width: 20px; height: 20px; min-width: 20px; font-size: 0.58rem; line-height: 1; }
.fko-cal-selected { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin: 0 0 10px; font-size: 0.95rem; }
.fko-audit-filters label { display: grid; gap: 5px; }
/* The selects sit CENTRED between the view switch (left) and the buttons
   (right) (user, 2026-09-11): auto margins on both sides of the label group
   split the free space equally. */
.fko-audit-filters label:first-of-type { margin-left: auto; }
.fko-audit-filters label span { color: var(--fko-muted); font-size: 0.785rem; text-transform: uppercase; letter-spacing: .04em; }
/* ⚠ The date input needs MORE than the shared rule to match the selects.
   A native date field applies its own user-agent padding, height and font, and
   ignores `font: inherit` for its internal spin/indicator parts -- so without
   an explicit height and appearance reset it renders visibly taller and in a
   different typeface than the dropdowns beside it. */
.fko-audit-filters select, .fko-audit-filters input[type="date"] { box-sizing: border-box; height: 34px; padding: 7px 10px; border: 1px solid var(--fko-border); border-radius: 0; background: transparent; color: inherit; font-family: inherit; font-size: 0.885rem; line-height: 1.2; }
.fko-audit-filters select { -webkit-appearance: none; appearance: none; padding-right: 26px; background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%); background-position: calc(100% - 14px) 15px, calc(100% - 9px) 15px; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; }
/* The calendar picker icon inherits the panel's colour instead of the browser
   default, which is near-black and invisible on a dark panel. */
.fko-audit-filters input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1) opacity(.55); cursor: pointer; }
.fko-audit-filters input[type="date"]:hover::-webkit-calendar-picker-indicator { filter: invert(1) opacity(1); }
.fko-audit-filters select:focus-visible, .fko-audit-filters input[type="date"]:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: 1px; }
.fko-audit-filter-actions { display: flex; gap: 8px; margin-left: auto; }
.fko-audit-pager { display: flex; justify-content: space-between; gap: 10px; margin-top: 14px; }
/* ⚠ When only "Older" is present it must sit right, not left -- otherwise the
   control jumps across the panel between page one and page two. */
.fko-audit-pager > :only-child { margin-left: auto; }

.fko-audit-list { display: grid; margin: 0; padding: 0; list-style: none; }
/* 🔑 ICON FIRST, THEN TIME, THEN THE ENTRY (user, 2026-08-22). The category
   icon is what a customer scans down the column for, so it leads the row.
   ⚠ REORDERED IN CSS, NOT IN THE MARKUP. `order` on the two leading cells
   leaves the DOM as time-then-icon, which is the order that reads correctly to
   a screen reader: "15:30, workflow, Workflow discovered".
   ⚠ align-items: start, not center -- an expanded entry is tall, and centring
   would float the icon and timestamp down beside its middle. */
.fko-audit-list li { display: grid; grid-template-columns: 34px 54px 1fr; gap: 18px; align-items: start; padding: 15px 0; border-top: 1px solid var(--fko-border); }
.fko-audit-list li > time { order: 2; }
.fko-audit-list li > .fko-audit-icon { order: 1; }
/* ⚠ Named explicitly rather than :last-child. Each row has exactly three
   children -- time, icon, details -- and naming the one that moves means a
   fourth child added later does not silently inherit an order it should not. */
.fko-audit-list li > .fko-audit-detail { order: 3; }
.fko-audit-list li:first-child { border-top: 0; }
.fko-audit-list time { color: var(--fko-muted); font-size: 0.825rem; }
.fko-audit-icon { display: grid; width: 30px; height: 30px; place-items: center; border: 1px solid var(--fko-orange); color: var(--fko-orange); font-size: 0.705rem; font-weight: 900; }
.fko-audit-list div { display: grid; gap: 4px; }
.fko-audit-list strong { font-size: 0.885rem; }
.fko-audit-list small { color: var(--fko-muted); font-size: 0.805rem; }
.fko-copy-field { display: flex; gap: 8px; }
.fko-copy-field code { flex: 1; min-width: 0; padding: 10px; overflow: hidden; border: 1px solid var(--fko-border); background: var(--fko-navy); color: var(--fko-orange); text-overflow: ellipsis; }
/* A labelled copy row (Support: tenant identifier, cell, namespace). One
   shape for every identifier -- the label sits above the field at the
   definition-list's label size and colour, so three rows read as one list. */
.fko-copy-row + .fko-copy-row { margin-top: 14px; }
.fko-copy-label { display: block; margin-bottom: 6px; color: var(--fko-muted); font-size: 0.865rem; }
.fko-members-summary { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; }
.fko-members-summary .fko-stat { min-height: 148px; }
.fko-domain-value { overflow-wrap: anywhere; font-size: clamp(1rem, 2vw, 1.45rem) !important; }
.fko-plan-notice { margin: 12px 0 0; padding: 13px 16px; border-left: 3px solid var(--fko-orange); background: var(--fko-navy-2); color: var(--fko-muted); font-size: 0.905rem; }
.fko-integration-notice { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; padding: 12px 15px; border: 1px solid rgba(244, 189, 80, .55); border-radius: 8px; background: rgba(244, 189, 80, .08); }
.fko-integration-notice p { margin: 0; color: #e2d4b5; font-size: 0.865rem; line-height: 1.5; }
.fko-role-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-top: 12px; }
.fko-role-card { min-height: 170px; padding: 16px; border: 1px solid var(--fko-border); border-radius: 8px; background: var(--fko-navy-2); }
.fko-role-card.is-primary { border-top: 3px solid var(--fko-orange); }
.fko-role-card > span { display: block; margin-bottom: 14px; color: var(--fko-orange); font-size: 0.805rem; font-weight: 850; letter-spacing: .11em; text-transform: uppercase; }
.fko-role-card strong { display: block; min-height: 42px; font-size: 1.005rem; line-height: 1.35; }
.fko-role-card p { margin: 12px 0 0; color: var(--fko-muted); font-size: 0.825rem; line-height: 1.55; }

.fko-toast { position: fixed; right: 24px; bottom: 24px; z-index: 9999; max-width: min(390px, calc(100vw - 32px)); padding: 13px 16px; border: 1px solid var(--fko-orange); border-radius: 6px; background: var(--fko-navy); color: var(--fko-white); box-shadow: 0 10px 35px rgba(0, 0, 0, .32); font-size: 0.905rem; font-weight: 700; }

@media (max-width: 1050px) {
  .fko-stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fko-card-grid { grid-template-columns: 1fr; }
  .fko-environment-card { min-height: auto; }
  .fko-overview-grid { grid-template-columns: 1fr; }
  .fko-overview-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fko-access-callout { grid-column: 1 / -1; }
  .fko-role-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 760px) {
  .fko-layout { grid-template-columns: 1fr; }
  .fko-sidebar { padding: 10px; border-right: 0; border-bottom: 1px solid var(--fko-border); }
  .fko-brand { margin: 3px 2px 10px; }
  .fko-nav-toggle { display: flex; }
  .fko-nav-list { display: none; padding-top: 6px; }
  .fko-nav-list.is-open { display: grid; }
  .fko-sidebar-help { display: none; }
  .fko-main { padding: 18px 12px; }
  .fko-app-head { align-items: flex-start; flex-direction: column; }
  .fko-app-controls { width: 100%; }
  .fko-fullscreen-button { flex: 0 0 auto; }
  .fko-two-column, .fko-comparison, .fko-members-summary { grid-template-columns: 1fr; }
  /* Stacked: the divider becomes a horizontal rule above the second column,
     so the two blocks stay visually separated without a left border floating
     beside full-width content. */
  .fko-billing-columns { gap: 28px; }
  .fko-billing-columns > div + div { padding-left: 0; padding-top: 28px; border-left: 0; border-top: 1px solid var(--fko-border); }
  .fko-overview-rail, .fko-role-grid { grid-template-columns: 1fr; }
  .fko-section-heading { align-items: flex-start; flex-direction: column; }
  .fko-section-heading > .fko-button, .fko-section-heading > .fko-secondary-button { width: 100%; }
}

@media (max-width: 480px) {
  .fko-stat-grid { grid-template-columns: 1fr; }
  .fko-stat { min-height: 112px; }
  .fko-health-grid, .fko-pipeline { grid-template-columns: 1fr; }
  .fko-health-card { min-height: 92px; }
  .fko-actions > * { width: 100%; }
  .fko-access-callout, .fko-integration-notice { align-items: flex-start; flex-direction: column; }
  .fko-access-callout .fko-secondary-button { width: 100%; }
  .fko-fullscreen-button [data-fko-fullscreen-label], .fko-fullscreen-button [data-fko-refresh-label] { position: absolute; width: 1px; height: 1px; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
}

/* Sync-in-progress banner.
   🔑 The spinner is the ONLY moving thing on the page, so it reads as "work is
   happening here" rather than decoration. It runs while the browser polls for a
   newer inventory, and the row disappears on reload -- so its lifetime is
   exactly the sync's. */
.fko-sync-status { display: flex; align-items: flex-start; gap: 10px; }
.fko-sync-spinner { flex: 0 0 16px; width: 16px; height: 16px; margin-top: 2px; border: 2px solid var(--fko-border); border-top-color: var(--fko-orange); border-radius: 50%; animation: fko-spin .8s linear infinite; }
.fko-sync-status p { margin: 0; }
.fko-sync-status .fko-sync-note { margin-top: 3px; font-size: 0.845rem; }
/* ⚠ The reload promise is only true when JS can keep it. Hidden by default and
   revealed by the script, so a no-JS reader is never told the page will reload
   itself when nothing will. */
.fko-sync-status .fko-sync-note { display: none; }
.fko-app.is-sync-polling .fko-sync-status .fko-sync-note { display: block; }
/* Item 3.6 — the resolve banner promises an automatic refresh ONLY when the
   script that keeps that promise is running.
   🔑 Default state is the MANUAL instruction, so a customer with JS disabled
   reads something true. `is-resolve-polling` is added by the poller and swaps
   them -- the same rule the sync banner follows, for the same reason: a promise
   the page cannot keep is worse than no promise. */
/* Item 3.6 — the two resolve states.
   🔑 BLUE while committing, GREEN when done. Blue is the panel's neutral
   "in progress" and does not claim an outcome; green is reserved for a result
   that actually happened.
   ⚠ ONLY THE SUCCESS NOTICE AUTO-DISMISSES. The panel's standing rule is that
   a warning or an error must never vanish on a timer -- a customer who looks
   away must not lose the one message telling them something needs attention. */
/* ⚠ `.fko-notice--progress` IS DEFINED WITH THE OTHER NOTICE TYPES, not here.
   It sat at this point in the file, unscoped and with a 3px border, so the
   standard's own `.fko-app .fko-notice { border-left: 4px }` (higher
   specificity AND later) overrode it -- the accent colour never rendered.
   Only the spinner spacing belongs to this section. */
.fko-notice--progress .fko-sync-spinner { margin-right: .5rem; vertical-align: -2px;
  display: inline-block; }
/* ⚠ No `.is-fading` rule: the standard dismisser removes the node outright
   rather than fading it, so a transition here would never be seen. */
.fko-resolve-done { border-left: 3px solid var(--fko-green); }
.fko-resolve-auto { display: none; }
.fko-app.is-resolve-polling .fko-resolve-auto { display: inline; }
.fko-app.is-resolve-polling .fko-resolve-manual { display: none; }
/* The credential-scan banner (user, 2026-08-23), same contract as the two above:
   the "will refresh automatically" line is HIDDEN by default and shown only once
   the script adds `is-scan-polling`.
   🔑 A promise the page cannot keep is worse than no promise -- with JS off the
   customer is told to refresh instead. */
.fko-scan-auto { display: none; }
.fko-app.is-scan-polling .fko-scan-auto { display: inline; }
.fko-app.is-scan-polling .fko-scan-manual { display: none; }
/* ⚠ Stacked, not inline: "how long this takes" is a separate fact from "what is
   happening", and running them together buries the one that sets the customer's
   expectation. */
.fko-app .fko-scan-banner p { margin: 0 0 6px; }
.fko-app .fko-scan-banner p:last-child { margin-bottom: 0; }
/* Migration and credential-write banners (user, 2026-08-23) -- same contract as
   every other async banner: the auto-refresh promise is hidden until the script
   that keeps it is running, and the manual fallback shows instead. */
.fko-promote-auto { display: none; }
.fko-app.is-promote-polling .fko-promote-auto { display: inline; }
.fko-app.is-promote-polling .fko-promote-manual { display: none; }
.fko-credsync-auto { display: none; }
/* A finished banner leaves without a reload: a short fade, then removed. */
.fko-app .fko-notice.is-leaving { opacity: 0; transition: opacity 0.4s ease; }
.fko-app.is-credsync-polling .fko-credsync-auto { display: inline; }
.fko-app.is-credsync-polling .fko-credsync-manual { display: none; }
.fko-app .fko-promote-banner p,
.fko-app .fko-credsync-banner p { margin: 0; }
@keyframes fko-spin { to { transform: rotate(360deg); } }

/* Workflow JSON viewer.
   🔑 A dialog over the table, not a new page -- the customer is comparing
   against the rows behind it. */
.fko-json-viewer { position: fixed; inset: 0; z-index: 999; display: flex; align-items: center; justify-content: center; padding: 24px; background: rgba(2, 12, 26, .72); }
.fko-json-viewer[hidden] { display: none; }
/* 🔑 THE PANEL GROWS WITH THE VIEWPORT, UP TO A CAP (user, 2026-08-21). It was
   a flat `min(900px, 100%)`, which ignores what is inside: a side-by-side diff
   has to fit TWO columns of JSON in the space one column used to have, so on a
   wide screen the code wrapped hard while the viewport sat mostly empty.
   ⚠ `width: fit-content` WAS TRIED AND IS WRONG HERE. Its children are sized at
   `100%` of the panel, so there is no intrinsic max-content width to measure
   against and the panel would resolve to its min-width and never grow -- the
   same 900px hardcode wearing a different name. `width: 100%` plus a `max-width`
   cap is what actually tracks the viewport.
   🔑 The diff opts into the WIDER cap via a modifier; the plain single-column
   view keeps the narrower one, because a lone column of JSON stretched across
   1600px is harder to read, not easier. */
.fko-json-panel { display: flex; flex-direction: column;
  width: 100%; max-width: min(900px, 100%);
  max-height: 82vh; border: 1px solid var(--fko-border); border-radius: 10px;
  background: var(--fko-navy-2); box-shadow: 0 18px 60px rgba(0,0,0,.5); }
/* A side-by-side diff earns the extra width; a single-column view does not. */
.fko-json-panel.fko-json-panel--diff { max-width: min(1600px, 100%); }
.fko-json-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 16px 18px; border-bottom: 1px solid var(--fko-border); }
.fko-json-head h3 { margin: 0 0 3px; font-size: 1.075rem; }
.fko-json-head p { margin: 0; font-size: 0.845rem; word-break: break-all; }
.fko-json-actions { display: flex; gap: 8px; flex: 0 0 auto; }
/* ⚠ The body scrolls INDEPENDENTLY. A 500-line workflow must not push the
   header and Copy button off screen. */
.fko-json-body { flex: 1 1 auto; overflow: auto; margin: 0; padding: 16px 18px; background: transparent; color: var(--fko-white); font-size: 0.845rem; line-height: 1.5; white-space: pre; tab-size: 2; }
/* ⚠ The viewer reuses .fko-sync-spinner, so it inherits the reduced-motion
   handling already defined for the sync banner. */
.fko-json-head .fko-sync-status { margin-top: 4px; }
.fko-json-head .fko-sync-spinner[hidden] { display: none; }
/* ⚠ A row button must not be full-height: .fko-secondary-button sets a 39px
   min-height sized for a section header, which would make every table row
   noticeably taller. */
/* ⚠ SCOPED, or the outline role wins. `.fko-app .fko-outline-button` is (0,2,0)
   against this rule's (0,1,0), so once View JSON became an outline button its
   39px min-height and 18px padding outranked these -- making one table row
/* Agent settings form (item 3.17). ⚠ Wraps rather than scrolling: this sits
   inside a panel that is already narrow on a laptop, and a horizontally
   scrolling form is one whose Save button goes missing. */
.fko-agent-form { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--fko-border); }
.fko-field-row { display: flex; flex-wrap: wrap; gap: 12px; }
.fko-field-row label { display: flex; flex: 1 1 190px; flex-direction: column; gap: 4px; }
.fko-field-row label span { color: var(--fko-muted); font-size: 0.825rem; font-weight: 700; }
.fko-field-row input, .fko-field-row select, .fko-field-row textarea { width: 100%; margin: 0; padding: 8px 10px; border: 1px solid var(--fko-border); border-radius: 5px; background: var(--fko-navy); color: var(--fko-white); font-size: 0.905rem; }
/* The support message: a field, not a control (5px like the inputs), resizable
   downwards only so it cannot break the two-column row sideways. */
.fko-field-row textarea { min-height: 132px; resize: vertical; font-family: inherit; line-height: 1.45; }
.fko-app .fko-support-form { display: grid; gap: 12px; }
.fko-agent-form .fko-actions { margin-top: 12px; }
.fko-row-actions { text-align: right; white-space: nowrap; }
/* 🔴 THE EXPANDER IS COLUMN ONE (user, 2026-09-01: "all expand buttons on all
   pages should be in column 1"). It used to be the LAST column while the panel
   it reveals starts at the far left, so the eye crossed the whole row to find
   what it had just opened.
   ⚠ THE COLUMN MOVED, not just the text. An earlier attempt only set
   text-align:left, which slides the glyph inside a cell that is still last --
   visibly no different, which is exactly what the user reported back.
   ⚠ Header AND body cell move together in every table, or every column right of
   it shifts by one and the panel row's colspan stops matching. */
.fko-app .fko-row-expander { text-align: left; width: 1%; }
/* Item 3.6 -- Restore/Accept each need their OWN form (own direction, own
   nonce), and a <form> is block-level by default, so each would break onto a
   line of its own. inline-block keeps the pair side by side in the JSON
   viewer's action bar.
   ⚠ These moved OUT of the workflow row (user, 2026-08-21): the decision
   belongs beside the diff that justifies it, not beside a row where the
   customer cannot yet see what changed. */
.fko-inline-form { display: inline-block; margin: 0; }

.fko-row-note { font-size: 0.805rem; }

@media (prefers-reduced-motion: reduce) {
  .fko-section { animation: none; }
  /* ⚠ Do not just disable the spin -- that leaves a static ring reading as a
     stalled control. Keep it visible and still; the text carries the meaning. */
  .fko-sync-spinner { animation: none; border-top-color: var(--fko-orange); opacity: .85; }
  .fko-app * { scroll-behavior: auto !important; }
}

/* The "used by" dialog (item 3.9 phase A).
   ⚠ <dialog> defaults to the browser's light chrome, which would be a white box
   on this dark panel -- the same class of bug as the focus outline. */
/* 🔴 POSITIONED EXPLICITLY, NOT LEFT TO THE BROWSER DEFAULT (user, 2026-08-24).
   A <dialog> normally centres in the VIEWPORT -- but `.fko-section` animates a
   `transform` (fko-section-in), and a transformed ancestor becomes the
   containing block for its fixed-position descendants. Every modal here sits
   three sections deep, so they centred inside the SECTION instead: scrolled
   down the page, the dialog opened above the fold and was cut off at the top.
   ⚠ THE ANIMATION IS NOT THE THING TO REMOVE. `transform: none` at the end of a
   keyframe still establishes the containing block while it runs, and dropping
   the animation to fix positioning would trade one visible problem for another.
   Pinning the dialog to the viewport is the fix that does not depend on what
   any ancestor does. */
.fko-modal {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  background: var(--fko-navy-2);
  color: inherit;
  border: 1px solid #173958;
  border-radius: 9px;
  padding: 22px;
  max-width: 520px;
  width: calc(100% - 32px);
  /* ⚠ A LONG LIST MUST SCROLL INSIDE THE DIALOG, not push it off screen: the
     unused-credential modal can hold dozens of rows. */
  max-height: calc(100vh - 48px);
  overflow-y: auto;
}
.fko-modal::backdrop { background: rgba(0, 12, 29, .72); }
/* The shared confirm dialog (user, 2026-08-24). ⚠ NARROWER AND CENTRED, unlike
   the list modals it shares .fko-modal with: this holds one sentence and two
   buttons, and full width would spread them apart. */
.fko-confirm-modal { max-width: 460px; text-align: center; }
.fko-confirm-body { padding: 8px 4px; }
.fko-confirm-mark { margin: 0 0 14px; }
.fko-confirm-modal p { margin: 0 0 20px; line-height: 1.5; }
/* The optional short list under the question (data-fko-confirm-detail). Left-aligned: three lines of prose read badly centred. */
.fko-confirm-modal .fko-confirm-detail { margin: -8px 0 20px; padding: 0; list-style: none; text-align: left; line-height: 1.45; }
.fko-confirm-modal .fko-confirm-detail li { margin: 0 0 8px; }
.fko-confirm-modal .fko-confirm-detail strong { font-weight: 600; }
/* The optional typed confirmation (data-fko-confirm-type). A field, so 5px -- pills are for things you press. */
.fko-confirm-modal .fko-confirm-typed { margin: -4px 0 20px; text-align: left; }
.fko-confirm-modal .fko-confirm-typed label { display: block; margin: 0 0 6px; }
.fko-confirm-modal .fko-confirm-typed-input { width: 100%; box-sizing: border-box; padding: 9px 12px; border: 1px solid var(--fko-border); border-radius: 5px; background: var(--fko-input-bg); color: var(--fko-input-ink); }
.fko-confirm-modal .fko-confirm-typed-msg { display: block; min-height: 1.4em; margin-top: 6px; font-size: 0.92rem; font-weight: 400; }
.fko-confirm-modal [data-fko-confirm-yes]:disabled { opacity: .45; cursor: not-allowed; }
/* 🔑 STACKED, WITH THE DESTRUCTIVE ACTION FIRST AND THE ESCAPE BELOW IT — the
   shape YITH uses, and the one that makes "no" the easy target. */
.fko-confirm-actions { display: grid; gap: 10px; justify-items: center; }
.fko-confirm-actions .fko-button { min-width: 220px; }
/* ⚠ THE ESCAPE IS THE NEUTRAL ROLE, and lighter than the action above it: two
   controls of equal weight on a destructive confirm make the safe choice as easy
   to misread as the dangerous one. Sized down rather than restyled, so it stays
   the standard's button and not a variant of it. */
.fko-confirm-actions .fko-secondary-button { border-color: transparent; }
.fko-confirm-actions .fko-secondary-button:hover { border-color: var(--fko-border); }
.fko-modal h3 { margin: 0 0 6px; }
.fko-modal ul { margin: 12px 0 0; padding-left: 20px; display: grid; gap: 6px; }

/* The +/- credential expander (item 3.9 phase B).
   🔑 ORANGE WHEN OPEN, per the requested behaviour: the state must be readable
   at a glance in a table of 39 rows. */
/* 🔴 THE SAME DRAWN +/- AS EVERY OTHER DISCLOSURE (user, 2026-08-22: "make sure
   +|- button on secrets match all the rest"). This was the last one still
   TYPING a '+' character inside a grey box: the glyph sat high, because in Inter
   a maths symbol is centred on the maths axis rather than the baseline, and the
   border never turned orange when open.
   🔑 The bars are two background gradients, exactly as the shared marker rule
   does it -- geometry, not a font. WHITE while collapsed (the action is
   "expand"), ORANGE when open (the action is "collapse").
   ⚠ THE JS STILL WRITES '+' AND '−' INTO THE SPAN. That span is hidden rather
   than removed, because control-panel.js queries it on every toggle and would
   throw if it vanished. */
.fko-expander {
  width: 22px; height: 22px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid var(--fko-white);
  border-radius: 999px;
  background-color: transparent;
  background-image:
    linear-gradient(var(--fko-white), var(--fko-white)),
    linear-gradient(var(--fko-white), var(--fko-white));
  background-size: 10px 2px, 2px 10px;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
  cursor: pointer;
}
/* ⚠ Hidden, not removed -- the JS reads and writes this node. */
.fko-expander [data-fko-expander-glyph] {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* Open: orange, and the vertical bar simply is not painted. */
.fko-expander.is-open {
  border-color: var(--fko-orange);
  background-image: linear-gradient(var(--fko-orange), var(--fko-orange));
  background-size: 10px 2px;
  background-position: center;
  background-repeat: no-repeat;
}
/* 🔑 HOVER = ORANGE INK, NEVER A FILL (user, 2026-09-11, for every expand /
   collapse control on every page). The border AND both bars turn orange; the
   background is untouched, so a hovered control cannot be mistaken for a
   pressed button. ⚠ background-image only, never the `background` shorthand:
   the shorthand resets the layers and erased the minus once already. */
.fko-expander:hover {
  border-color: var(--fko-orange);
  background-image:
    linear-gradient(var(--fko-orange), var(--fko-orange)),
    linear-gradient(var(--fko-orange), var(--fko-orange));
}
.fko-expander.is-open:hover { background-image: linear-gradient(var(--fko-orange), var(--fko-orange)); }
/* 🔴 A SECOND .is-open RULE USED TO SIT HERE, AND IT ERASED THE MINUS.
   It set `background: var(--fko-orange)` -- the SHORTHAND, which resets
   background-image -- so the orange horizontal bar painted by the rule above was
   wiped and replaced with a solid orange disc. The glyph span is visually hidden
   (it exists only because control-panel.js reads and writes it), so nothing was
   left to see: the open state rendered as a filled circle with no minus at all.
   ⚠ It also carried `color` and `font-weight`, leftovers from the older
   font-glyph approach that have no effect once the mark is geometry.
   🔑 NEVER put a `background` SHORTHAND on this element. Use
   background-color if a fill is ever wanted, so the bar gradients survive. */

/* ⚠ A field whose KEY is fixed must LOOK fixed. A disabled input that renders
   like an editable one invites the customer to try typing in it. */
.fko-field-row input:disabled {
  opacity: .65;
  cursor: not-allowed;
  background: rgba(255, 255, 255, .04);
}

/* Loading rows (built by fkoLoadingRow).
   🔴 STANDING REQUIREMENT (user, 2026-08-17): every loading state shows a
   spinner. ⚠ Reuses .fko-sync-spinner deliberately, so the reduced-motion rule
   above covers this too -- a second spinner class would need that rule copied,
   and the copy that got forgotten would animate for someone who asked the OS for
   no motion. */
.fko-loading-row { margin: 8px 0; }
.fko-loading-row p { font-size: 0.945rem; }

/* Disabled selects must ABSORB their own clicks.
   🔴 Reported 2026-08-17: "clicking the greyed credential type scrolls to top".
   A disabled control receives no click, so the event falls through to the
   wrapping <label>, which then hunts for something focusable -- and the nearest
   candidate is the skip link, whose href="#fko-main" jumps the page.
   ⚠ pointer-events:none on the SELECT would make it worse (the click would pass
   even further up), so the wrapper takes the hit instead: the label stops being
   a click target while the control inside it is disabled. */
.fko-field-row label:has(select:disabled) { pointer-events: none; }
/* ⚠ :has() is unsupported in older engines, where the symptom simply returns --
   it does not break anything. The cursor makes "not available yet" visible
   regardless of :has() support. */
.fko-field-row select:disabled { cursor: not-allowed; opacity: .7; }

/* Credential field: value box and n8n's hint.
   🔑 The "Value" caption is gone (user 2026-08-17) -- the field's own label
   already names the box. .fko-field-value has no <span>, so it needs its own
   flex-basis: without it the value column would size differently from the label
   column beside it and the rows would not align. */
.fko-field-value { flex: 1 1 220px; }
/* ⚠ Long hints render BELOW the input, short ones as a placeholder inside it.
   Muted and small so guidance never competes with the value the customer typed. */
.fko-field-hint { display: block; margin-top: 4px; color: var(--fko-muted); font-size: 0.805rem; line-height: 1.35; font-weight: 400; }
/* ⚠ A placeholder must read as a HINT, not as a value already entered --
   otherwise a customer sees "name@email.com" and thinks the field is filled. */
.fko-field-row input::placeholder { color: var(--fko-muted); opacity: .75; }
/* A required field with no stored value says so in its own box: orange, normal
   weight (user, 2026-09-26). Scoped under .fko-app so it outranks the muted
   placeholder above whatever row the input sits in. */
.fko-app input.fko-input-missing::placeholder { color: var(--fko-orange); opacity: 1; font-weight: 400; }

/* Orphaned-module link on a workflow row.
   🔴 ORANGE ON PURPOSE (user, 2026-08-17): a workflow carrying nodes that never
   run is a real defect, and it must read as a warning rather than as decoration
   beside the workflow name. ⚠ Not red -- the workflow still WORKS; the orphaned
   part of it simply does nothing. */
/* ⚠ .fko-orphan-link was removed 2026-08-22 with the asterisk link on the
   Workflows table.
   🔴 .fko-orphan-note IS NOW UNUSED (2026-08-23). Its last consumer was the
   Secrets page's Drift explanation, which moved INTO the credentials table so it
   sits beside the button and the Delete it describes. The rules are kept rather
   than deleted because check-orphan-classes only catches a class USED with no
   rule, never a rule with no user -- so removing them is a judgement call, and
   the note above them is the record. Delete them if nothing claims them by the
   next sweep. */
/* The asterisk note, in the workflows heading beside the title.
   ⚠ Small and muted: it explains a mark, it is not itself a warning banner.
   🔑 Width-capped and right-aligned so it sits in the heading's empty second
   slot instead of stretching the row -- the heading is a flex container and an
   uncapped paragraph would push the title hard left. */
/* 🔑 ORANGE AND HALF THE WIDTH (user, 2026-08-17). Muted grey read as
   boilerplate; the note is telling the customer something is wrong with their
   workflows, so it carries the same orange as the asterisks it explains.
   ⚠ 50% of the heading row, which spans the table -- wide enough to read in two
   or three lines instead of a narrow column beside the title. */
/* 🔴 `.fko-app p` (0,2,0) SETS `color: inherit` AND OUT-SPECIFIES A LONE CLASS
   (0,1,0). A single-class rule here loses and the note renders white -- which is
   exactly what shipped in 1.19.79 and looked like the CSS had not deployed.
   ⚠ Raise specificity, never add !important: two !important rules resolve by
   source order, which is the thing that is hard to see in a file this size. */
.fko-app p.fko-orphan-note { margin: 0; flex: 0 1 50%; color: var(--fko-orange); font-size: 0.845rem; line-height: 1.45; text-align: left; }
/* ⚠ COLOUR THE SPANS, NOT ONLY THE <p>. A bare text node inside the paragraph
   rendered blue while the asterisk beside it rendered orange -- so the theme
   colours loose text in a section heading more specifically than a rule on the
   <p>. Targeting the spans directly is what actually holds. */
.fko-app .fko-orphan-note-mark,
.fko-app .fko-orphan-note-text { color: var(--fko-orange); }
@media (max-width: 760px) {
  /* ⚠ Stacked on narrow screens: half a width beside a title is unreadable there. */
  .fko-app p.fko-orphan-note { flex: 1 1 auto; text-align: left; margin-top: 8px; }
}

/* Modal close: one large orange × in the top right, on every dialog.
   🔑 ONE RULE FOR ALL THREE (user, 2026-08-17). They had three different close
   treatments -- a "Close" text button, and two × glyphs at body font size that
   were hard to hit. A dismiss control that is hard to find is how a modal comes
   to feel like a trap.
   ⚠ ABSOLUTE, so it sits in the corner regardless of what the head contains --
   the dialog carries the padding, so the offset matches it. */
/* 🔴 .fko-modal IS DELIBERATELY NOT LISTED HERE (2026-08-24). It is
   `position: fixed` above so it centres on the VIEWPORT rather than inside a
   transformed section, and this rule sits LATER in the file -- listing it would
   silently override that and put the dialog back where it was cut off.
   ⚠ fixed ALSO establishes a containing block, so the absolutely positioned
   close button still lands in the dialog's corner either way. */
.fko-json-panel { position: relative; }
/* ⚠ SCOPED TO A POSITIONED ANCESTOR. The JSON viewer is a plain div, not a
   <dialog>, so without position:relative on .fko-json-panel the absolute × would
   escape to the page and float over unrelated content. */
/* 🔴 `.fko-app button` (0,2,0) SETS `font: inherit` AND OUT-SPECIFIES A LONE
   CLASS (0,1,0) -- the shorthand resets font-size, so a single-class rule here
   silently loses and the × stays body-sized. That is what shipped in 1.19.79 and
   read as "the CSS did not deploy".
   ⚠ Raise specificity; do NOT reach for !important. */
.fko-app button.fko-modal-close {
  position: absolute; top: 10px; right: 12px;
  padding: 2px 10px; border: 0; background: none; cursor: pointer;
  color: var(--fko-orange); font-size: 2.025rem; line-height: 1; font-weight: 400;
}
.fko-app button.fko-modal-close:hover { color: var(--fko-white); }
/* ⚠ A visible focus ring: this is often the only way out by keyboard, and the
   panel's global reset removes outlines. */
.fko-app button.fko-modal-close:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: 2px; border-radius: 4px; }
/* ⚠ Keep the heading clear of the button so a long title cannot slide under it. */
/* ⚠ Grows with the glyph. At 2.4rem the button is ~46px wide, and a title that
   slid under it would look like a rendering fault rather than a tight fit. */
.fko-modal-head, .fko-json-head { padding-right: 48px; }
.fko-modal-head { display: flex; align-items: flex-start; gap: 12px; }

/* ---------------------------------------------------------------------------
   AGENT INSTALL INSTRUCTIONS  (Environments -> an environment with no agent)
   -------------------------------------------------------------------------
   A native <details>, open until the agent connects. The open/closed state is
   NOT stored: once an agent connects the template renders the connected branch
   instead, so the instructions disappear rather than being collapsed. Nothing
   can drift out of step with whether an agent is actually there.

   🔴 THIS PANEL IS DARK NAVY. The first version of this block used invented
   fallbacks (--fko-surface-alt, --fko-code-bg) that do not exist in :root, so
   they resolved to near-white -- a white card with white text, and the summary
   heading was invisible. Use the REAL tokens above (--fko-navy-*, --fko-white,
   --fko-muted) and never introduce a light surface here.

   ⚠ SPECIFICITY. `.fko-app button { font: inherit }` and `.fko-app p { color:
   inherit }` are (0,2,1) and beat a lone class (0,1,0) -- the same trap that
   made an orange note render grey twice. Every rule here is prefixed .fko-app
   so it can win, and colours are set EXPLICITLY rather than inherited.
   ------------------------------------------------------------------------ */
.fko-app .fko-install-steps { margin-top: 14px; border: 1px solid var(--fko-border); border-radius: 8px; padding: 12px 14px; background: var(--fko-navy-2); }
/* 🔑 +/- GLYPH, matching the dashboard menu toggle (.fko-nav-toggle), which
   renders a literal + and swaps it to - in JS. Here it is done in CSS off
   [open], so it needs no JavaScript and cannot fall out of step with the
   actual open state.
   ⚠ The default disclosure triangle is removed on BOTH engines -- `list-style`
   for most, `::-webkit-details-marker` for Safari -- or the row shows a
   triangle AND a +. */
.fko-app .fko-install-steps > summary { display: flex; align-items: center; justify-content: flex-start; gap: 10px; cursor: pointer; font-weight: 600; color: var(--fko-white); list-style: none; }
.fko-app .fko-install-steps > summary::-webkit-details-marker { display: none; }
/* ⚠ ::before, and the summary is flex-start -- the glyph leads the label
   rather than being pushed to the far edge by space-between. A fixed width
   keeps the text from shifting sideways as + swaps to the wider minus. */
.fko-app .fko-install-steps[open] > summary { margin-bottom: 10px; }

/* ⚠ SCOPED to environment cards. `.fko-actions` is right-aligned panel-wide;
   changing that rule would move buttons in every other section too. */
.fko-app .fko-env-actions { justify-content: flex-start; align-items: center; }
/* ⚠ Each button is wrapped in its own <form> (a POST with a nonce -- a GET
   would be prefetchable and would burn the single-use token). The form is the
   flex item, so aligning the BUTTONS means the forms must stop stretching:
   align-items:center above, and the form itself becomes a flex box so its
   button centres inside it. Without this the two buttons sit on different
   baselines because their padding differs. */
.fko-app .fko-env-actions > form { display: flex; align-items: center; margin: 0; }
/* ⚠ Set explicitly: `.fko-app p { color: inherit }` would otherwise pull these
   from whatever encloses the block. */
.fko-app .fko-install-steps p { color: var(--fko-muted); }
.fko-app .fko-install-list { margin: 0 0 10px; padding-left: 22px; color: var(--fko-white); }
.fko-app .fko-install-list li { margin-bottom: 8px; line-height: 1.5; color: var(--fko-white); }
/* ⚠ Commands must wrap, not overflow: a panel column is narrow and a customer
   copying a truncated command gets an error that looks like ours. */
.fko-app .fko-install-list code { display: inline-block; max-width: 100%; overflow-wrap: anywhere; padding: 3px 8px; border-radius: 4px; background: var(--fko-navy); border: 1px solid var(--fko-border); color: var(--fko-orange); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.92em; }

/* Kubernetes: the two "where do you run kubectl from" routes. ⚠ Headings and
   list text set explicitly -- `.fko-app p { color: inherit }` at (0,2,1) beats
   a lone class, the trap that rendered a note grey twice. */
.fko-app .fko-install-routes { margin: 4px 0 12px; padding-top: 10px; border-top: 1px solid var(--fko-border); }
.fko-app .fko-install-routes h4 { margin: 12px 0 4px; color: var(--fko-white); font-size: 1.045rem; font-weight: 700; }
.fko-app .fko-install-routes h4:first-child { margin-top: 0; }

/* Table group headings — Workflows split into Active / Inactive (2026-08-19).
   🔑 Uses EXISTING tokens only. A previous change invented --fko-surface-alt
   and --fko-code-bg, which do not exist, and rendered a white card on a dark
   panel. */
.fko-app .fko-table-group-title {
  margin: 18px 0 8px;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--fko-muted);
}
.fko-app .fko-table-group-title:first-of-type { margin-top: 4px; }
/* ⚠ The count must not shout louder than the label it qualifies. */
.fko-app .fko-table-group-title .fko-muted { font-weight: 600; }


/* A disabled control must not look pressable. `fko-secondary-button` already
   matches "Scan workflows"; this removes the hover affordance so it does not
   invite a click it will ignore (user, 2026-08-19). */
.fko-app .fko-secondary-button:disabled,
.fko-app .fko-button:disabled,
.fko-app .fko-profile-panel button.button:disabled { opacity: 0.55; cursor: not-allowed; }
.fko-app .fko-secondary-button:disabled:hover { border-color: var(--fko-border); color: var(--fko-white); }

/* In-table instruction — a CALLOUT, not a footnote (user, 2026-08-19).
   It is the one action the Secrets page asks for, so it gets a bordered panel
   inside the table rather than a line of muted text under the intro.

   🔴 THE TEXT IS WRAPPED IN ITS OWN <span> AND COLOURED THERE. A bare text node
   inside the <p> rendered BLUE while the rule targeted the <p> -- the theme
   colours loose text and out-specifies it. Exactly the trap already documented
   on .fko-orphan-note; a span the stylesheet targets directly ends it. */
.fko-app .fko-table-note-row td {
  padding: 0 0 18px;
  border-bottom: none;
}
/* 🔑 MATCHES THE REFERENCE SCREENSHOT: a quiet bordered panel that sits inside
   the table, NOT an alert. Slightly inset from the row so it reads as a block
   of guidance rather than another data row.
   🔑 NOTIFICATION COLOR STANDARD (user, 2026-08-18/19): this is an INSTRUCTION,
   so it carries the BLUE left band + blue font. The earlier decision stands --
   never orange: orange is the attention colour for a WARNING/finding, and an
   always-present instruction coloured like an alert made the whole table look
   like it had a problem. */
.fko-app .fko-table-note {
  margin: 0;
  padding: 20px 24px;
  border: 1px solid var(--fko-border);
  border-left: 4px solid var(--fko-blue);
  border-radius: 0 4px 4px 0;
  /* Blue-shaded background — the reference look for every message under the
     refined standard (severity colour at low alpha over the panel ground). */
  background: rgba(119, 167, 255, 0.08);
}
/* ⚠ NORMAL WEIGHT AND THE PANEL'S LINK BLUE. The bolded orange version read as
   a warning about the table below it; this is standing guidance. */
.fko-app .fko-table-note,
.fko-app .fko-table-note-text {
  /* 🔑 REFINED (user, 2026-08-18): WHITE font like every message; the blue band
     and blue-shaded background carry the "instruction" meaning. */
  color: var(--fko-white);
  /* 🔑 EXACTLY .fko-table td (user, 2026-08-19). The note sits INSIDE the table,
     so a different size made it read as a separate widget dropped on top of the
     rows rather than as guidance belonging to them. */
  font-size: 0.875rem;
  line-height: 1.7;
  font-weight: 400;
}

/* Outlined button — the destructive option beside a filled primary
   (user, 2026-08-19). Delete must not compete with Save for the eye: an
   outline reads as available-but-secondary. Same metrics as .fko-button so
   the pair sits on one line without shifting. */
/* 🔑 ORANGE AND NOT BOLD (user, 2026-08-19). Red read as an error state on a
   button the customer may legitimately want; orange is this panel's action
   colour, and normal weight keeps it clearly secondary to Save. */
/* 🔑 SHAPE MATCHES "Save credential" (user, 2026-08-19): same radius, same
   metrics -- a pair of buttons on one row should differ in EMPHASIS, not in
   silhouette. 1px border and normal weight keep it clearly secondary. */
/* 🔑 SAME WEIGHT AS `.fko-button` (820), not 400. The two are a PAIR -- the
   standard puts them side by side in every `.fko-actions` row -- and a 400
   against an 820 reads as two different kinds of control, which is exactly
   what the outline/solid distinction is already saying. Corrected panel-wide
   2026-08-21 (user): the canonical Disconnect agent / Save agent settings pair
   had the same mismatch, so this fixes the reference, not just one caller.
   ⚠ Size and box metrics were ALREADY identical (0.905rem, 39px, 9px 14px) --
   weight was the only difference, which is why it read as a size problem. */
/* 🔴 THE PAIR'S FONT, SET IN ONE PLACE AND AT A SPECIFICITY THAT ACTUALLY WINS.
   Found 2026-08-21 from a screenshot: Restore rendered heavier and larger than
   Accept, which is the OPPOSITE of what the source suggests.
   Why: `.fko-app button, ... { font: inherit }` (line ~33, specificity 0,1,1)
   OUTRANKS `.fko-button, .fko-secondary-button { font-size/-weight }` (line
   ~164, specificity 0,1,0). So `.fko-button` silently inherits the THEME font,
   while `.fko-outline-button` (0,2,0) keeps its own -- and the pair mismatches.
   ⚠ SOURCE ORDER IS NOT THE STORY. Line 164 comes long after line 33 and still
   loses; only specificity decides. The same trap as `.fko-actions`.
   🔑 Both classes are named here at 0,2,0 so they cannot diverge again, and so
   a future edit to either base rule cannot silently desynchronise them. */
.fko-app .fko-button,
.fko-app .fko-outline-button,
.fko-app .fko-secondary-button,
.fko-app .fko-profile-panel button.button {
  font-size: 0.905rem;
  /* ⚠ NORMAL, NOT BOLD (user, 2026-08-21). The base `.fko-button` rule says
     820, but it never won -- the theme reset outranked it -- so the panel's
     solid buttons have always rendered at normal weight and that is the look
     being matched. Setting 820 here would have made every button in the panel
     bold for the first time, which is a change nobody asked for. */
  font-weight: 400;
  line-height: 1.2;
}

/* 🔴 THE OUTLINE BUTTON IS A PILL. THIS IS THE STANDARD (user, 2026-08-22:
   "make the buttons look like this, which should already be a required
   standard").
   The shape was already defined for the Drift badge as
   `.fko-outline-button--accent` and used exactly ONCE, while four real outline
   buttons kept a 5px radius -- so the panel had two outline shapes and the rare
   one was the intended look. The base class now IS the pill and the modifier
   only changes scale.
   ⚠ WHAT NOT TO DO: this is the shape for a SECONDARY or DESTRUCTIVE action.
   The solid `.fko-button` remains the primary; making everything a pill would
   remove the distinction the outline exists to draw. */
.fko-app .fko-outline-button {
  min-height: 39px;
  padding: 9px 18px;
  /* Fully rounded at this height. Matches --accent, which is the same shape at
     a smaller scale, so the two can never drift apart visually. */
  border-radius: 999px;
  cursor: pointer;
  font-size: 0.905rem;
  background: transparent;
  border: 2px solid var(--fko-orange);
  color: var(--fko-orange);
}
   noticeably taller than its neighbours. Same specificity, later in source. */
.fko-app .fko-row-button { min-height: 28px; padding: 5px 11px; font-size: 0.825rem; }
.fko-app .fko-outline-button:hover {
  background: var(--fko-orange);
  color: var(--fko-navy);
}
/* ⚠ .fko-text-danger sets a red colour and is applied alongside this class on
   the delete button; this must win, hence the double-class selector. */
.fko-app .fko-outline-button.fko-text-danger { color: var(--fko-orange); }
.fko-app .fko-outline-button.fko-text-danger:hover { color: var(--fko-navy); }
/* ⚠ Delete on the LEFT, Save on the right: the actions row is flex, so order
   follows the DOM, and the delete button is appended first.
   🔴 THE MISSING justify-content IS DELIBERATE -- see the note at ~line 384,
   which relies on this rule leaving actions LEFT inside the app so the diff's
   buttons sit beside the diff they act on. I added flex-end here on 2026-08-30
   reading the omission as an accidental override, and reverted it: that note
   documents the intent, and honouring one expander's alignment by moving every
   actions row in the panel is the wrong trade. */
.fko-app .fko-actions { display: flex; gap: 10px; align-items: center; }

/* Orange outlined variant — the Drift control (user, 2026-08-19).
   🔑 A control that opens an explanation should look pressable; a coloured word
   in a table cell reads as decoration. Transparent with a 2px orange border and
   orange text, so it reads as a control without competing with a filled action. */
/* ⚠ The SMALL pill: same shape as the base outline button, at badge scale. It
   restates background/border/colour because .fko-drift-sample is a <span>, not
   a button, and inherits none of the base rule. */
.fko-app .fko-outline-button--accent,
.fko-app .fko-drift-sample {
  background: transparent;
  border: 2px solid var(--fko-orange);
  color: var(--fko-orange);
  /* 🔑 45px, i.e. fully rounded at this height (user, 2026-08-19). Kept on the
     SHARED rule so the sample in the note stays identical to the real button --
     a picture that does not match what is in the table defeats its purpose. */
  border-radius: 999px;
  min-height: 30px;
  padding: 3px 12px;
  font-size: 0.845rem;
  font-weight: 820;
  line-height: 1.3;
}
.fko-app .fko-outline-button--accent:hover {
  background: var(--fko-orange);
  color: var(--fko-navy);
}

/* ⚠ THE SAMPLE IN THE NOTE IS A PICTURE OF THE BUTTON, NOT A BUTTON. It shows
   the customer what to look for in the table; making it pressable would offer
   an action with no row to act on. Rendered as a <span>, so there is nothing to
   click and nothing for a screen reader to announce as actionable. */
.fko-app .fko-drift-sample {
  display: inline-block;
  cursor: default;
  vertical-align: baseline;
  margin: 0 2px;
}

/* ⚠ The drift modal's body carries a blank line before CAUTION. textContent
   preserves the newline in the DOM, but CSS collapses it by default -- so the
   two paragraphs ran together into one block. pre-line honours the break and
   still wraps normally (user copy, 2026-08-19). */

/* Audit event categories (user, 2026-08-19).
   🔑 FIVE CATEGORIES, NOT 21 COLOURS. One colour per action is not a legend
   anyone learns; these answer the question a customer actually asks — is this
   about my environment, my secrets, my agents, my access, or my billing?

   ⚠ Every colour is an EXISTING token. A previous change invented
   --fko-surface-alt and --fko-code-bg, which do not exist, and rendered a white
   card on a dark panel. */
/* ⚠ Round like every other bordered control in the CP. It is a decorative
   category badge (a span, aria-hidden), not a button -- but a 4px square beside
   round markers and round pills is the inconsistency the standard exists to
   remove. */
.fko-app .fko-audit-icon { font-size: 0.85rem; font-weight: 700; border-radius: 999px; }
.fko-app .fko-audit-icon--env      { border-color: var(--fko-purple); color: var(--fko-purple); }
.fko-app .fko-audit-icon--secret   { border-color: var(--fko-orange); color: var(--fko-orange); }
.fko-app .fko-audit-icon--workflow { border-color: var(--fko-blue);   color: var(--fko-blue); }
/* ⚠ Agents and Control panel SWAPPED (user, 2026-08-19): agents take the grey
   that access used, and the control-panel category takes amber. */
.fko-app .fko-audit-icon--agent    { border-color: var(--fko-muted);  color: var(--fko-muted); }
.fko-app .fko-audit-icon--access   { border-color: var(--fko-amber);  color: var(--fko-amber); }
.fko-app .fko-audit-icon--billing  { border-color: var(--fko-green);  color: var(--fko-green); }
.fko-app .fko-audit-icon--other    { border-color: var(--fko-border); color: var(--fko-muted); }
/* ⚠ RESULT OUTRANKS CATEGORY. A failed event is red whatever it was about --
   "did this work" is the more urgent question than "what was it about". */
.fko-app .fko-audit-icon--failed   { border-color: #ff6672; color: #ff6672; }

/* Audit legend — derived from the same map the rows use, so it cannot disagree
   with the icons beside it. */
.fko-app .fko-audit-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  align-items: center;
  margin: 0 0 12px;
  color: var(--fko-muted);
  font-size: 0.8rem;
}
.fko-app .fko-audit-legend-item { display: inline-flex; align-items: center; gap: 7px; }
/* ⚠ The legend swatch is a SAMPLE, not a control: smaller than the timeline
   marker and never a click target. */
.fko-app .fko-audit-legend .fko-audit-icon { width: 22px; height: 22px; font-size: 0.75rem; }

/* Audit rows use +|- like the rest of the panel (user, 2026-08-19), replacing
   the browser's default ▶ disclosure triangle.

   🔑 CSS ONLY, NO JAVASCRIPT. These are native <details> elements, chosen so a
   row expands with keyboard and screen-reader semantics for free and cannot be
   left broken by a script that fails to load. Restyling the marker keeps all of
   that; scripting a toggle would throw it away for a glyph. */
/* 🔴 FLEX, so the marker sits ON THE SAME LINE as the text (user, 2026-08-22).
   The shared disclosure rule gives every marker `display: block`, which is
   correct inside the flex summaries used elsewhere -- but this summary was not
   a flex row, so the block-level marker took a line of its own and pushed the
   entry text beneath it. A regression from unifying the four disclosures.
   ⚠ align-items:center, not baseline: the marker is a drawn box with no text
   baseline of its own. */
.fko-app .fko-audit-detail > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  list-style: none;
  cursor: pointer;
}
/* ⚠ Safari needs the WebKit pseudo-element as well; without it the triangle
   remains and the row shows both markers. */
.fko-app .fko-audit-detail > summary::-webkit-details-marker { display: none; }
/* ⚠ Spacing comes from the summary's flex `gap` now, not a margin here. */
/* ⚠ The BORDER stays the neutral one (user, 2026-08-19); only the glyph turns
   orange. An orange box and an orange glyph made the open row read as a
   warning rather than as the row being read. */


/* Diff counts in the audit trail (user, 2026-08-19). Green for added, red for
   removed — the convention every diff view uses, so it needs no legend.
   ⚠ Counts only. The diff CONTENT is never stored in an audit row, because
   those rows are exportable and a workflow diff carries the customer's node
   names, URLs and parameters. */
/* The reason under a migration outcome -- why it failed, or why parity was
   overridden. ⚠ Its own line: a failure reason is a sentence, not a label, and
   inline it would push the table's Outcome column wide. */
/* The Migrate control in a workflow row: a select and a button on one line when
   there are several steps, a lone button when there is one.
   ⚠ align-items:center so the select and button share a midline whichever
   layout applies. */
/* 🔑 THE MIGRATION OUTCOME, IN THE ROW (user, 2026-09-22). The tab's action area
   is a flex row, so a notice dropped into it shrink-wraps to its text and sits
   beside the button instead of above it -- the same trap the blocked-credential
   block hit at 2393. flex-basis:100% makes it take its own line. */
.fko-app .fko-wf-promote-note {
  flex: 1 0 100%;
  margin: 12px 0 0;
  text-align: left;
}
.fko-app .fko-migrate-form { display: inline-flex; align-items: center; gap: 6px; }
.fko-app .fko-migrate-form select {
  margin: 0; padding: 6px 8px;
  border: 1px solid var(--fko-border); border-radius: 5px;
  background: var(--fko-navy); color: var(--fko-white); font-size: 0.845rem;
}
.fko-app .fko-promo-reason { display: block; margin-top: 3px; font-size: .78rem; font-weight: 400; line-height: 1.35; }
.fko-app .fko-diff-add { color: var(--fko-green); font-weight: 700; }
.fko-app .fko-diff-del { color: #ff6672; font-weight: 700; }

/* The Export form sits beside the filter's View button rather than in the
   section heading (user, 2026-08-19), so the two controls read as a pair:
   view this slice, then export what you are looking at. */
.fko-app .fko-audit-export-form { display: inline-block; margin: 0 0 12px; }

/* ─────────────────────────────────────────────────────────────────────────────
   NOTICE STANDARD (user, 2026-08-20). One vocabulary for every message the
   panel shows about something that just happened.

   🔑 THE SAME COLOURS AS THE AUDIT LEGEND, deliberately. A customer who learns
   that green is success and red is failure in the timeline should not have to
   learn a second scheme for notices. One meaning per colour, panel-wide.

     success  green   it worked, and here is what happened
     warning  amber   it worked partly, or needs your attention
     error    red     it did not work
     info     blue    context, no outcome

   ⚠ THE LEFT BORDER CARRIES THE MEANING, not a background wash: these panels
   sit on a dark ground where a tinted fill turns text unreadable, and colour
   alone would exclude anyone who cannot distinguish it -- the notices also say
   what happened in words.
   ───────────────────────────────────────────────────────────────────────────── */
.fko-app .fko-notice {
  margin: 0 0 14px;
  padding: 14px 18px;
  border: 1px solid var(--fko-border);
  border-left: 4px solid var(--fko-border);
  border-radius: 6px;
  background: var(--fko-navy-2);
}
.fko-app .fko-notice p { margin: 0; }
.fko-app .fko-notice p + p { margin-top: 8px; }

/* ─────────────────────────────────────────────────────────────────────────────
   NOTIFICATION COLOR STANDARD (user, 2026-08-18) — left band + matching font:
     info/instruction = BLUE · notification = YELLOW (amber on navy) ·
     warning = ORANGE · failure/error = RED
   Drift is a WARNING (orange) everywhere — a state to resolve, not a failure.
   Documented in DOCS/technical/Standards/NOTIFICATION-COLOR-STANDARD.md.
   ───────────────────────────────────────────────────────────────────────────── */
/* 🔑 REFINED (user, 2026-08-18): the FONT is WHITE on every message — severity is
   carried by the left band and a background SHADED in the severity colour (the
   credentials instruction panel is the reference look). */
.fko-app .fko-notice p { color: var(--fko-white); }
.fko-app .fko-notice > p:first-child { font-weight: 600; }

/* 🔴 THE HEADER UPGRADE BANNER (user, 2026-08-20: "we should not rely on a user
   scrolling to the bottom to find and become aware they need to upgrade").

   Text on the left, the action on the right, so the reason and the remedy are
   read as one thing. ⚠ It sits ABOVE every section, so it must not be tall --
   two short lines and a button, never a paragraph.

   ⚠ NO `padding-right` RESERVED FOR A DISMISS BUTTON. This banner is
   deliberately not dismissible (a dismissed one is how an agent ends up three
   versions behind), so the base rule's 42px gutter would be dead space here. */
.fko-app .fko-notice.fko-upgrade-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-right: 18px;
}
.fko-app .fko-upgrade-banner > div { min-width: 0; }
/* The n8n outage banner (user, 2026-08-23), sharing the upgrade banner's layout
   because it sits in the same place and is also not dismissible.
   🔑 IT CARRIES NO BUTTON, and that is honest: the fix is on the customer's own
   n8n, so there is nothing this panel can do about it. A button here would have
   to either lie or no-op.
   ⚠ NO `padding-right` OVERRIDE -- it inherits the flex rule above, and the base
   notice's 42px dismiss gutter would be dead space on a banner with no control. */
.fko-app .fko-notice.fko-outage-banner {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-right: 18px;
}
.fko-app .fko-outage-banner > div { min-width: 0; }
/* ⚠ Paragraphs stack: two outages (n8n down AND an agent offline) are separate
   facts and must not run together on one line. */
.fko-app .fko-outage-banner p { margin: 0 0 4px; }
.fko-app .fko-outage-banner p:last-child { margin-bottom: 0; }
/* ⚠ The button must not shrink under a long message -- a wrapped two-character
   button is unreadable and stops looking like an action. */
.fko-app .fko-upgrade-banner .fko-button { flex: 0 0 auto; }

@media (max-width: 700px) {
  .fko-app .fko-notice.fko-upgrade-banner {
    align-items: flex-start;
    flex-direction: column;
    gap: 12px;
  }
  /* Full width once stacked, so it reads as the section's action rather than a
     stray control floating under the text. */
  .fko-app .fko-upgrade-banner .fko-button { width: 100%; }
}

.fko-app .fko-notice--info { border-left-color: var(--fko-blue); background: rgba(119, 167, 255, 0.08); }
/* 🔑 PROGRESS IS BLUE, THE SAME BLUE AS `info` (user, 2026-08-21). It is the
   standard's fifth state and it borrows info's meaning deliberately: work is
   under way, no outcome is being claimed yet. Green would assert success
   before the commit exists.
   ⚠ Defined HERE, with the other types, so it inherits the standard's 4px
   border and `.fko-app` scoping rather than fighting them. */
.fko-app .fko-notice--progress { border-left-color: var(--fko-blue); background: rgba(119, 167, 255, 0.08); }
/* ── Item 3.11: promotion paths and credential parity ────────────────────
   🔑 Each path is its own panel: a customer reads ONE route at a time
   ("can I promote DEV to QA yet?"), and stacking them into a single table
   would force them to filter by eye. */
/* 🔴 ONE GAP RULE, NOT AN ADJACENT-SIBLING ONE. `+ .fko-promotion-path` spaced
   paths from EACH OTHER but left the last one flush against the "Add a
   promotion path" panel below, so the gaps were visibly uneven (reported
   2026-08-22). The list owns the rhythm now, and the panel after it gets the
   same gap as the panels within it. */
/* 🔑 STRUCTURAL CLASSES WITH NO VISUAL RULES OF THEIR OWN. Declared so the
   orphan-class lint stays green and so the dependency is written down: each is
   either a JS/selector hook or pairs with a styled class. An undeclared class
   renders fine and styles nothing, which is how four bugs shipped on
   2026-08-22. */
.fko-app .fko-env-key,          /* pairs with .fko-env-rename */
.fko-app .fko-agent-id,         /* pairs with .fko-muted */
.fko-app .fko-path-form,        /* the add-step form wrapper */
/* 🔴 A TWO-COLUMN TABLE, LEFT-ALIGNED WITH THE ENTRY TITLE (user, 2026-08-22).
   Each row is already <div><dt>label</dt><dd>value</dd></div>, but nothing reset
   the browser's default 40px <dd> indent, so every value sat on its own line
   indented under its label -- a stack, not a table.
   🔑 `grid-template-columns: max-content 1fr` on the ROW makes the label column
   size to the longest label and every value start at the same x. `auto` would
   let each row size independently and the values would not line up.
   ⚠ The whole block is flush with the summary text, NOT indented under the
   marker: the entry title is the reference edge the customer reads down. */
.fko-app .fko-audit-detail-body {
  display: grid;
  gap: 0;
  /* 🔑 LEFT-ALIGNED WITH THE ENTRY TITLE, not with the marker. The summary is a
     flex row of [22px marker][8px gap][title], so the title starts 30px in and
     the detail block must too -- otherwise the table hangs left of the heading
     it belongs to.
     ⚠ Derived from the two values above, not typed as "30px": change the marker
     size or the gap and this follows. */
  margin: 8px 0 0 calc(22px + 8px);
}
.fko-app .fko-audit-detail-body > div {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 4px 16px;
  align-items: baseline;
  padding: 5px 0;
  border-top: 1px solid var(--fko-border);
}
.fko-app .fko-audit-detail-body > div:first-child { border-top: 0; }
.fko-app .fko-audit-detail-body dt {
  color: var(--fko-muted);
  font-size: 0.845rem;
  white-space: nowrap;
}
.fko-app .fko-audit-detail-body dd {
  /* ⚠ THE INDENT LIVES HERE. A <dd> carries `margin-inline-start: 40px` by UA
     default; without this the value column starts 40px right of where the grid
     puts it and the "table" is a stack again. */
  margin: 0;
  color: var(--fko-white);
  font-size: 0.845rem;
  overflow-wrap: anywhere;
}
/* ⚠ The Changes row nests its own list, so it must not be forced onto one line. */
.fko-app .fko-audit-detail-body dd > ul,
.fko-app .fko-audit-detail-body dd > ol { margin: 0; padding-left: 18px; }
  { }
.fko-app .fko-path-list { display: flex; flex-direction: column; gap: 14px; }
.fko-app .fko-path-list + .fko-panel { margin-top: 14px; }
/* ⚠ Feedback while the reorder is in flight, and if it fails. A silent failure
   would leave the customer's arrangement on screen while the server still has
   the old order -- the screen and the truth disagreeing, which is the shape
   this project keeps getting caught by. */
.fko-app .fko-path-list.is-saving { opacity: .7; }
.fko-app .fko-path-list.has-error { outline: 1px solid var(--fko-orange); outline-offset: 6px; border-radius: 9px; }
.fko-app .fko-promotion-route { margin: 0 0 10px; font-size: 1rem;
  display: flex; align-items: center; gap: 10px; }
/* Collapsed promotion paths, 2026-08-22 (user: "make the steps collapsed by
   default"). A path's parity table can run to dozens of rows; the usual
   question is just "what routes exist?".
   🔴 THE ROUTE IS THE SUMMARY and carries the Blocked flag, so the one fact
   worth seeing without opening anything stays visible. A blocked path also
   renders `open` -- hiding the reason a promotion cannot run behind a click is
   the "empty success" shape this project keeps getting caught by. */
.fko-app .fko-promotion-path > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  list-style: none;
}
.fko-app .fko-promotion-path > summary::-webkit-details-marker { display: none; }
/* 🔴 THE +/- IS DRAWN, NOT TYPED. Three attempts to centre a '+' / '−'
   CHARACTER failed, because a glyph's position is decided by the FONT, not the
   box: in Inter both are maths symbols sitting well above the baseline, so the
   box centred perfectly while the ink inside it did not. line-height:0 centres
   the collapsed LINE BOX, which still is not the ink.
   🔑 So the box is empty and the bars are two background gradients, both
   centred by `background-position: center`. No font, no positioning, no nested
   pseudo-element -- geometry we control, so the cross cannot sit off-centre.
   ⚠ WHITE while collapsed (the action is "expand"), ORANGE when open (the
   action is "collapse"): the colour says what the control WILL do. */
.fko-app .fko-audit-detail > summary::before,
.fko-app .fko-install-steps > summary::before,
.fko-app .fko-env-card > summary::before,
.fko-app .fko-promotion-path > summary::before,
.fko-app .fko-mapping-step > summary::before {
  content: '';
  flex: none;
  display: block;
  width: 22px;
  height: 22px;
  /* 🔴 THE PILL STANDARD, not a square with a grey border. This shipped as
     `1px var(--fko-border)` / `4px` radius -- an invented look that matched
     nothing else in the panel (reported 2026-08-22, the second time this
     control was wrong). Round and 2px orange, like every other bordered
     control here. */
  /* ⚠ WHITE WHILE COLLAPSED, ORANGE ONCE OPEN (user, 2026-08-19 for the audit
     rows, 2026-08-22 for these): the colour says what the control WILL do --
     white offers "expand", orange offers "collapse" -- and the orange also
     marks the one row you are currently reading. */
  border: 2px solid var(--fko-white);
  border-radius: 999px;
  background-image:
    linear-gradient(var(--fko-white), var(--fko-white)),
    linear-gradient(var(--fko-white), var(--fko-white));
  background-size: 10px 2px, 2px 10px;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
}
/* ⚠ ONE gradient now, so the vertical bar is simply not painted: the + and the
   − are the same geometry with one layer removed, which is why they cannot
   drift apart. */
.fko-app .fko-audit-detail[open] > summary::before,
.fko-app .fko-install-steps[open] > summary::before,
.fko-app .fko-env-card[open] > summary::before,
.fko-app .fko-promotion-path[open] > summary::before,
.fko-app .fko-job-detail[open] > summary::before,
.fko-app .fko-mapping-step[open] > summary::before {
  border-color: var(--fko-orange);
  background-image: linear-gradient(var(--fko-orange), var(--fko-orange));
  background-size: 10px 2px;
  background-position: center;
  background-repeat: no-repeat;
}
.fko-app .fko-promotion-path[open] > summary { margin-bottom: 14px; }
.fko-app .fko-promotion-path > summary:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: 3px; border-radius: 4px; }
/* ⚠ Pushed to the right so it reads as a verdict on the route, not part of the
   environment name. */
.fko-app .fko-route-flag { margin-left: auto; font-size: .8rem; font-weight: 700; }
/* A named step: the customer's label leads, the route sits beneath it.
   🔴 THE ROUTE IS NEVER DROPPED. A name is what they call it; the route is what
   it DOES, and that is the one fact a rename must not hide. */
.fko-app .fko-route-name { font-weight: 700; }
.fko-app .fko-route-sub { display: block; font-size: .82rem; font-weight: 400; }
.fko-app .fko-route-note { margin: 0 0 12px; font-size: .85rem; }
/* ⚠ The summary is a flex row; a named step stacks two lines, so let the text
   column grow and keep the +/- and the Blocked flag on the first line. */
.fko-app .fko-promotion-path > summary > .fko-route-name { flex: 1 1 auto; }
.fko-app .fko-path-labels { margin-bottom: 12px; }
/* Reordering promotion paths, 2026-08-22. Display order only -- nothing runs
   these as a sequence (migration 0051).
   🔴 THE HANDLE IS DECORATION. The move up/down buttons are the real controls
   and work without JavaScript, with a keyboard and with a screen reader; drag
   is layered on top. Hence the handle is aria-hidden and not focusable. */
/* ⚠ A wrapper the drag JS needs (it queries [data-fko-reorder] and reparents
   panels inside it). Declared here so it is not an invented class with no
   rules -- three of those shipped today and each one styled nothing. */
.fko-app .fko-reorder-hint { margin: 0 0 10px; font-size: .82rem; }
.fko-app .fko-drag-handle {
  flex: none;
  color: var(--fko-muted);
  font-size: 1.05rem;
  line-height: 1;
  cursor: grab;
  user-select: none;
  /* ⚠ Focusable now that the move buttons are gone -- it is the keyboard path,
     so it must show focus. */
}
.fko-app .fko-drag-handle:active { cursor: grabbing; }
.fko-app .fko-drag-handle:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: 2px; border-radius: 4px; }
.fko-app .fko-drag-handle:hover { color: var(--fko-white); }
.fko-app .fko-promotion-path.is-dragging { opacity: .55; outline: 2px dashed var(--fko-orange); outline-offset: 2px; }
/* ⚠ The move form sits beside Remove; keep the two visually distinct so an
   arrow is never mistaken for part of a destructive control. */
.fko-app .fko-path-move { display: inline-flex; gap: 4px; margin-right: 8px; }
/* ⚠ The move arrows are a MUTED control, not an orange outline button: they sit
   beside "Remove path" and an orange pill there would compete with a
   destructive action for attention. Rounded to match the pill family, but grey
   until hovered. */
.fko-app .fko-icon-button {
  min-width: 30px;
  padding: 4px 8px;
  border: 1px solid var(--fko-border);
  border-radius: 999px;
  background: transparent;
  color: var(--fko-muted);
  font-size: .95rem;
  line-height: 1.2;
  cursor: pointer;
}
.fko-app .fko-icon-button:hover { color: var(--fko-white); border-color: var(--fko-orange); }
.fko-app .fko-icon-button:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: 2px; }
/* ⚠ The arrow is aria-hidden and paired with a screen-reader "promotes to",
   because a bare → is read as "right arrow" or skipped entirely -- and the
   DIRECTION is the whole meaning of a path. */
.fko-app .fko-promotion-arrow { color: var(--fko-orange); font-weight: 700; }
.fko-app .fko-parity-table td:first-child { white-space: nowrap; }
/* Tabs, 2026-08-22 -- Promotion folded into Deployments as Steps / Deploy.
   🔑 RADIO INPUTS + :checked, NO JAVASCRIPT. The panel is server-rendered and
   had no tab component; this works with JS disabled and a radio group is
   keyboard-navigable with the arrow keys for free.
   ⚠ The inputs must stay in the DOM and merely be invisible -- display:none
   removes them from the accessibility tree AND from keyboard focus, which
   would make the tabs unreachable without a mouse. */
.fko-app .fko-tab-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
/* ⚠ The tab wrapper. It carries no visual rules of its own -- the tablist and
   panels do the work -- but it MUST exist as a selector, because every
   `#fko-tab-x:checked ~ .fko-tabpanel` rule depends on the inputs and panels
   being siblings inside this element. Declared so the orphan-class lint does
   not report it, and so the dependency is written down. */
.fko-app .fko-tabs { display: block; }
.fko-app .fko-tablist {
  display: flex;
  gap: 4px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--fko-border);
}
.fko-app .fko-tab {
  padding: 9px 16px;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--fko-muted);
  font-size: .92rem;
  font-weight: 600;
  cursor: pointer;
  user-select: none;
}
.fko-app .fko-tab:hover { color: var(--fko-white); }
/* 🔴 SOURCE ORDER IS LOAD-BEARING HERE. `~` only reaches FORWARD, so both
   inputs must precede .fko-tablist and both panels in the markup. Moving an
   input after them silently breaks the tab it drives -- with no error. */
.fko-app .fko-tabpanel { display: none; }

/* 🔴 POSITIONAL CLASSES, NOT IDS (2026-09-01). These rules were keyed to literal
   ids -- #fko-tab-steps, #fko-tab-mappings, #fko-tab-details -- which works only
   while each exists ONCE on the page. The Workflows and Secrets row expanders
   put a tab set inside EVERY ROW, so an id-keyed rule would need 37 copies and,
   worse, 37 elements would share one id: `<label for>` binds to the FIRST match
   in the document, so clicking row 12's tab would toggle row 1's. That is the
   same cross-write CLAUDE.md documents for the `form=` attribute.

   🔑 ONE RULE SERVES EVERY INSTANCE. --a/--b/--c are POSITIONAL, deliberately
   not semantic: the same three rules drive the Migrations wizard (Route /
   Mappings / Jobs), Profile (Details / Billing) and the two-tab row expanders
   (This environment / Migration). A semantic name would fit one and lie about
   the others.

   ⚠ THE ids MUST STILL BE UNIQUE per instance -- they are what `<label for>`
   binds to. The class selects the rule; the id does the binding. A row that
   reuses another row's id is still broken, and no CSS rule can save it.

   ⚠ ADDING A FOURTH TAB means adding a --d pair here. A label with no matching
   :checked rule renders as a tab that never highlights, and one with no
   :focus-visible rule is invisible to keyboard users. */
.fko-app .fko-tab-input--a:checked ~ .fko-tablist label.fko-tab--a,
.fko-app .fko-tab-input--b:checked ~ .fko-tablist label.fko-tab--b,
.fko-app .fko-tab-input--c:checked ~ .fko-tablist label.fko-tab--c,
.fko-app .fko-tab-input--d:checked ~ .fko-tablist label.fko-tab--d,
.fko-app .fko-tab-input--e:checked ~ .fko-tablist label.fko-tab--e,
.fko-app .fko-tab-input--f:checked ~ .fko-tablist label.fko-tab--f,
.fko-app .fko-tab-input--g:checked ~ .fko-tablist label.fko-tab--g {
  color: var(--fko-white);
  border-bottom-color: var(--fko-orange);
}
.fko-app .fko-tab-input--a:checked ~ .fko-tabpanel--a,
.fko-app .fko-tab-input--b:checked ~ .fko-tabpanel--b,
.fko-app .fko-tab-input--c:checked ~ .fko-tabpanel--c,
.fko-app .fko-tab-input--d:checked ~ .fko-tabpanel--d,
.fko-app .fko-tab-input--e:checked ~ .fko-tabpanel--e,
.fko-app .fko-tab-input--f:checked ~ .fko-tabpanel--f,
.fko-app .fko-tab-input--g:checked ~ .fko-tabpanel--g { display: block; }
.fko-app .fko-tab-input--a:focus-visible ~ .fko-tablist label.fko-tab--a,
.fko-app .fko-tab-input--b:focus-visible ~ .fko-tablist label.fko-tab--b,
.fko-app .fko-tab-input--c:focus-visible ~ .fko-tablist label.fko-tab--c,
.fko-app .fko-tab-input--d:focus-visible ~ .fko-tablist label.fko-tab--d,
.fko-app .fko-tab-input--e:focus-visible ~ .fko-tablist label.fko-tab--e,
.fko-app .fko-tab-input--f:focus-visible ~ .fko-tablist label.fko-tab--f,
.fko-app .fko-tab-input--g:focus-visible ~ .fko-tablist label.fko-tab--g {
  outline: 2px solid var(--fko-orange);
  outline-offset: -2px;
  border-radius: 4px 4px 0 0;
}
/* One migration step's rewrites. 🔑 The two forms are separated by their
   headings rather than a border: they are two halves of one step's rules, not
   two unrelated panels. */
/* The workflows table's expanded actions (user, 2026-08-23).
   🔑 The controls that used to crowd the row -- View JSON, Migrate, Blocked --
   plus room for a per-workflow credential form, which could never fit a cell. */
/* ⚠ ONE BLOCK, NOT TWO. A second rule on this same selector is how the collapse
   control lost its minus earlier today: the later block's `background`
   shorthand silently reset what the first had painted. */
/* 🔴 THE CELL STAYS A TABLE-CELL. MEASURED 2026-09-01: with `display:flex` it
   rendered 498px inside a 1078px row -- setting `display` on a <td> REPLACES
   table-cell layout, so colspan="6" no longer governs its width and the cell
   shrink-wraps to its content. Five rounds of styling its CHILDREN were all
   downstream of this, and none could have worked.
   🔑 THE SECRETS PANEL CELL IS THE PROOF: no class, no display override, stays
   table-cell, and has always spanned correctly. Matching it means REMOVING the
   override, not adding more width rules.
   ⚠ Only padding belongs here now. The flex row moves to .fko-workflow-panel-row
   below, which is a plain div and can be flex without affecting table geometry. */
.fko-app .fko-workflow-panel-body {
  padding: 14px 16px;
}
/* The control strip, formerly the cell itself.
   ⚠ WRAPS. Three controls plus a migrate dropdown overflow a narrow viewport, and
   a row that scrolls sideways hides the button on its right.
   🔴 RIGHT-ALIGNED, SO A CONTROL SITS UNDER THE COLUMN THAT OWNS IT (user,
   2026-08-30): left-aligned controls put "View JSON" beneath the first column
   while the + that revealed it is at the far right.
   ⚠ `flex-end` and not a margin: the row WRAPS, and a margin on the first item
   would strand the wrapped line at the left. */
.fko-app .fko-workflow-panel-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

/* 🔴 THE PANEL MUST FILL THE ROW (user, 2026-09-01: "make the expanded view span
   entire width of all columns"). The cell already carries colspan="6", but a
   table cell only takes the width its CONTENT claims -- and with every child
   sized to its text, the flex line ended partway across, leaving the panel
   looking like it belonged to the first two columns.
   ⚠ width:100% on the CELL, not the row: the <td> is the element the colspan is
   on, and box-sizing keeps the padding inside it. */
/* 🔴 THE BLOCKED PANEL IS A DIRECT CHILD OF THE FLEX CELL (user, 2026-09-01,
   after four failed CSS attempts: "still wrong, why is this so difficult").
   It used to sit inside form.fko-migrate-form, which is display:inline-flex and
   so shrink-wraps to its content -- and no rule on a CHILD can widen a parent
   that sizes itself that way. The template now closes the form around it, which
   is what actually fixed the width; these two declarations are all that is left
   to do here.
   ⚠ align-self:stretch WAS THE WRONG TOOL and has been removed: in a ROW flex
   container the cross axis is VERTICAL, so it never affected width at all.
   Width comes from flex-basis:100% plus an explicit width; min-width:0 stops a
   long unbroken credential name from pushing the box wider than the cell. */
.fko-app .fko-blocked-block { flex: 1 1 100%; width: 100%; max-width: 100%; min-width: 0; box-sizing: border-box; }
/* ⚠ The notice sets its own left border and background; only spacing belongs
   here, or this would fork the component it is meant to reuse. */
.fko-app .fko-blocked-block .fko-notice { margin: 0 0 12px; width: 100%; box-sizing: border-box; text-align: left; }
.fko-app .fko-blocked-block .fko-notice p { margin: 6px 0 0; }
/* ⚠ .fko-actions does NOT right-align inside the panel -- `.fko-app .fko-actions`
   omits justify-content and outranks the base rule -- so the modifier states it,
   matching where the Secrets panel puts Save. */
/* Inline credential fixing inside a blocked workflow row (2026-09-01).
   ⚠ Sits between the notice and the actions, so it needs its own top margin;
   the notice already carries the bottom one. */
.fko-app .fko-blocked-fix { margin: 0 0 12px; }
/* What a migration will rewrite for ONE workflow (2026-09-01).
   ⚠ Full width inside the panel's flex row, so it stacks under the controls
   rather than competing with them for a line. */
/* 🔑 A DISCLOSURE THAT LOOKS LIKE A BUTTON (user, 2026-09-01), the same shape
   the environment actions use: the summary carries the outline role and the
   contents drop below it.
   ⚠ Full width of the panel's flex row, so opening it pushes nothing sideways. */
.fko-app .fko-wf-rewrites { flex: 1 0 100%; text-align: left; margin: 12px 0 0; }
/* 🔑 A HEADING, NOT A CONTROL (user, 2026-09-22: "remove the expand | collapse
   button"). This was a <summary> with the shared +/- marker; the tab is already
   behind a row expander and a tab, so a third disclosure hid the only content
   the tab exists to show. The marker rules went with it -- a ::before on an
   element that is no longer a summary would draw a square nothing toggles. */
.fko-app .fko-wf-rewrites-title {
  margin: 0 0 10px;
  font-size: .875rem;
  font-weight: 600;
  color: inherit;
}
.fko-app .fko-wf-rewrites ul { margin: 0 0 8px; padding-left: 18px; }
.fko-app .fko-wf-rewrites li { font-size: .85rem; line-height: 1.6; }
/* ⚠ The label and the value need separating, or "Address api.x.com" reads as
   one string. */
.fko-app .fko-wf-rewrites li > .fko-muted { margin-right: 8px; }
.fko-app .fko-blocked-fix > p { margin: 0 0 10px; font-size: .85rem; }
/* One credential: name, expander, and the panel the expander reveals.
   🔑 The panel is a GRID ITEM spanning both columns, so it drops onto its own
   full-width line under the name rather than squeezing beside it. */
.fko-app .fko-blocked-cred {
  display: grid;
  /* ⚠ auto FIRST: the expander leads, the name takes the rest. */
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  border-top: 1px solid var(--fko-border);
}
/* 🔴 THE SAME BUTTON, SMALLER AND SQUARE (user, 2026-09-01: "use the same type
   of buttons as the main expand buttons, only smaller, 1px and square").
   These sit INSIDE an already-expanded panel, so matching the 22px pill of the
   control that opened them made two levels of one thing look like one level.

   🔑 CHANGE ONLY WHAT MUST CHANGE. Everything else -- the gradients, their
   centring, the open-state colour, the hover -- is inherited from .fko-expander,
   so the two can never drift apart. An earlier attempt restated box-sizing and
   min-width here and the glyphs stopped agreeing with each other.

   ⚠ EVEN NUMBERS ONLY. The +/- is two background gradients centred in the box:
   at 18px with a 2px bar the bar lands on a whole pixel, at 1px on a half one,
   and a half-pixel bar renders a different width per button -- which is exactly
   what "uneven and unmatching" looked like. 18/2 keeps the same 9:1 proportion
   the 22px/2px original has. */
/* 🔴 THE SAME SMALL SQUARE AS THE SECRETS ROW (user, 2026-09-02). This marker
   inherits the shared 22px pill; the Secrets migration control is 18px with a
   1px square border, and the two Migration tabs sit one nav item apart.
   ⚠ background-size shrinks with the box, and the bar stays 2px: at 18px with
   a 1px border the content box is 16px, so a 2px bar centres on a whole pixel
   and a 1px bar lands on 7.5 -- which renders a different width per button. */
.fko-app .fko-blocked-cred > .fko-expander {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  border-width: 1px;
  border-radius: 1px;
  background-size: 8px 2px, 2px 8px;
}
.fko-app .fko-blocked-cred-name { font-size: .875rem; overflow-wrap: anywhere; }
/* ⚠ A host rewrite is read-only -- no expander -- so it has no first column to
   fill. One column, not the two .fko-blocked-cred lays out for a credential. */
.fko-app .fko-wf-rw-host { grid-template-columns: 1fr; font-size: .85rem; }
/* The Endpoints / Credentials halves of a workflow's rewrites (2026-09-22): a
   small heading, then either the mapping grid or the credential expanders.
   ⚠ min-width:0 so the five-column mapping grid inside can shrink with the
   row instead of widening the Workflows table. */
.fko-app .fko-wf-rw-section { min-width: 0; margin: 0 0 12px; }
.fko-app .fko-wf-rw-section > strong { display: block; font-size: .8rem; letter-spacing: .04em; text-transform: uppercase; color: var(--fko-orange); margin-bottom: 4px; }
.fko-app .fko-wf-rw-section > p.fko-muted { margin: 0 0 6px; font-size: .85rem; }
.fko-app .fko-blocked-cred > [data-fko-mapfield-panel] { grid-column: 1 / -1; }
/* ⚠ `hidden` must still win: a grid item with `display:grid` would override the
   attribute's own `display:none` and render every panel open. */
.fko-app .fko-blocked-cred > [data-fko-mapfield-panel][hidden] { display: none; }

.fko-app .fko-blocked-actions { justify-content: flex-end; }
/* 🔴 A ROW BUTTON MUST NOT SHRINK (user, 2026-09-01 -- "Set them up" rendered as
   a CIRCLE). As a flex item its default min-width is auto, but flex-shrink is 1,
   so once the credential-name list claimed the line the button was compressed
   below its own text and the label wrapped one character per line.
   ⚠ Fixes the cause, not the symptom: a min-width would break at a longer label. */
.fko-app .fko-workflow-panel-row .fko-row-button { flex: 0 0 auto; }
/* The approved JSON on a workflow's first tab (user, 2026-09-21).
   🔑 60vh: "approximately 60% of the height of the page". A FIXED height rather
   than a max: the box exists before its content arrives, so the row does not
   jump when the body lands, and a long workflow scrolls INSIDE it instead of
   pushing the Copy button a thousand lines down the page.
   🔴 width:0 + min-width:100% IS LOAD-BEARING. This sits in a table cell, and
   `white-space: pre` gives the block an intrinsic width of its LONGEST LINE --
   overflow:auto does not change that. Without this pair one long line in one
   workflow would widen the whole Workflows table past the page. Zero intrinsic
   width, then stretched to the cell. */
.fko-app .fko-json-inline { width: 0; min-width: 100%; margin: 0 0 12px; border: 1px solid var(--fko-border); border-radius: 10px; background: var(--fko-navy-2); overflow: hidden; }
/* 🔑 THE HEADER: status on the left, Copy JSON on the right (user, 2026-09-21:
   "move the copy button to the top"). Same shape as the dialog viewer's head.
   ⚠ The status block SHRINKS and the button does not (min-width:0 / flex none):
   the path is break-all text, and without this a long path would push the
   button out of the box instead of wrapping. */
.fko-app .fko-json-inline-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 14px; border-bottom: 1px solid var(--fko-border); }
.fko-app .fko-json-inline-head > .fko-sync-status { flex: 1 1 auto; min-width: 0; align-items: center; }
.fko-app .fko-json-inline-head > .fko-sync-status p { font-size: 0.845rem; word-break: break-all; }
.fko-app .fko-json-inline-head > .fko-row-button { flex: none; }
/* The add-a-route form on a workflow's Migration tab, shown only when no route
   leads out of its environment (2026-09-21).
   🔴 flex: 1 1 100% -- it sits inside .fko-workflow-panel-row, a RIGHT-ALIGNED
   flex strip sized for a couple of buttons. Without this the form would
   shrink-wrap against the right edge; with it the block takes the whole line
   and reads left to right like the same form on the Migrations page. */
.fko-app .fko-wf-add-route { flex: 1 1 100%; min-width: 0; text-align: left; }
.fko-app .fko-wf-add-route > strong { display: block; margin-bottom: 4px; font-size: 0.95rem; }
/* 🔑 THE ROW'S OWN Restore/Keep PAIR. It is `hidden` in the markup and revealed
   by renderDiff only after the forms have MOVED into the column headers -- so
   these rules cover the brief moment before that, and the case where a diff
   never renders. Left visible and unmoved they would read as a second,
   unexplained pair of buttons under the JSON. */
/* 🔑 THE ROW'S PROGRESS LINE. It REPLACES the buttons rather than sitting
   beside them, so the row never shows a control and a "working" message at
   once -- which is what invites a second click on work already running. */
.fko-app .fko-inline-resolve-status {
  display: flex; align-items: center; gap: 8px;
  margin: 10px 0 0; color: var(--fko-blue); font-size: .875rem;
}
.fko-app .fko-json-inline-resolve { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 0; }
.fko-app .fko-json-inline-resolve form { margin: 0; }
.fko-app .fko-json-inline-body { height: 60vh; overflow: auto; margin: 0; padding: 14px 16px; border: 0; background: transparent; color: var(--fko-white); font-size: 0.845rem; line-height: 1.5; white-space: pre; tab-size: 2; text-align: left; }
/* The names list yields space instead of forcing the button to. */
.fko-app .fko-workflow-panel-row .fko-row-note { flex: 0 1 auto; min-width: 0; }
/* The direction selector (user, 2026-08-23): pick a route, see one card. */
.fko-app .fko-map-filter {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}
.fko-app .fko-map-filter label { font-size: .85rem; color: var(--fko-muted); }
/* ⚠ Bounded, so a long "Environment A → Environment B" label does not stretch
   the control across the panel. */
.fko-app .fko-map-filter select { max-width: 320px; }
/* A migration step's rewrites, collapsed by default (user, 2026-08-23): 3 hosts
   and 53 credentials is 56 rows per step, and two steps put 112 rows of forms
   between the customer and the one they came to edit. */
.fko-app .fko-mapping-step > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  list-style: none;
}
.fko-app .fko-mapping-step > summary::-webkit-details-marker { display: none; }
/* ⚠ Pushed right so the count reads as a property of the step, not part of its
   name. */
.fko-app .fko-mapping-count { margin-left: auto; font-size: .85rem; }
/* ⚠ Sits between the route and the count, so it must not push either: it names
   WHICH card this is, not a value. */
/* 🔴 WHAT THE CARD IS, NOT AN AFTERTHOUGHT (user, 2026-09-01: "I'd like
   Endpoints and Credentials to be more obvious"). Two cards sit under one tab
   with the SAME route in bold, so the kind is the only thing telling them
   apart -- and it rendered smaller and muted, the least prominent thing in the
   header. It now reads as a label: uppercase, tracked, and in the accent
   colour the panel uses for "this is the subject". */
.fko-app .fko-mapping-kind {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--fko-orange);
  /* ⚠ Separated from the route rather than run together: "Source → Test
     Endpoints" read as one phrase. */
  margin-left: .6rem;
  padding-left: .6rem;
  border-left: 1px solid var(--fko-border);
}
/* Numbered wizard steps (user, 2026-08-23). The tabs stay independently
   clickable -- someone returning to change one endpoint should not walk through
   step 1 again -- so the number is a HINT about order, not a lock. */
.fko-app .fko-wizard .fko-tab { display: inline-flex; align-items: center; gap: 8px; }
/* ⚠ Fixed size, so "1" and "3" occupy the same space and the labels line up. */
.fko-app .fko-wizard-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: 1px solid var(--fko-border);
  border-radius: 999px;
  font-size: .72rem;
  color: var(--fko-muted);
}
/* 🔑 The ACTIVE step's number follows its label, so the two read as one thing.
   ⚠ Positional classes since 2026-09-01, matching the shared tab rules. Only a
   wizard emits .fko-wizard-n, so the two-tab row expanders are unaffected -- the
   selector simply finds nothing there. */
.fko-app .fko-tab-input--a:checked ~ .fko-tablist label.fko-tab--a .fko-wizard-n,
.fko-app .fko-tab-input--b:checked ~ .fko-tablist label.fko-tab--b .fko-wizard-n,
.fko-app .fko-tab-input--c:checked ~ .fko-tablist label.fko-tab--c .fko-wizard-n,
.fko-app .fko-tab-input--d:checked ~ .fko-tablist label.fko-tab--d .fko-wizard-n,
.fko-app .fko-tab-input--e:checked ~ .fko-tablist label.fko-tab--e .fko-wizard-n {
  border-color: var(--fko-orange);
  color: var(--fko-white);
}
/* The Secrets intro: three notes in one card, above the table (user,
   2026-08-23). ⚠ Paragraphs are SPACED, not run together: they answer three
   different questions and a single block reads as one long warning. */
.fko-app .fko-secrets-intro p { margin: 0 0 10px; }
.fko-app .fko-secrets-intro p:last-child { margin-bottom: 0; }
/* ⚠ The usage column must not steal width from the values being mapped: those
   are what the customer is reading and editing. */
.fko-app .fko-map-usage { white-space: nowrap; font-size: .875rem; }
.fko-app .fko-mapping-step h4 { margin: 18px 0 4px; }
.fko-app .fko-mapping-step h4:first-of-type { margin-top: 8px; }
/* ⚠ WRAPS. Two hostnames plus an arrow and a button overflow a narrow column,
   and a form that scrolls sideways is a form with an invisible submit button. */
/* 🔴 A GRID, NOT FLEX (user, 2026-08-23). Flex sized each row to its own
   content, so eleven rows of hostnames of different lengths put the arrow, the
   workflow count and the Save button in eleven different places -- and nothing
   lined up under the headers. A grid gives every row the same columns.
   ⚠ THE COLUMN LIST IS SHARED WITH .fko-mapping-head, which is what makes the
   headers sit over the fields they name. Change one and the other must follow. */
.fko-app .fko-mapping-form,
.fko-app .fko-mapping-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) 9rem auto;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
}
/* ⚠ min-width:0 on the inputs, or a long hostname forces the grid wider than
   its container and the Save button leaves the card. */
.fko-app .fko-mapping-form input[type="text"] { min-width: 0; width: 100%; }
/* The header row: labels only, no controls. */
.fko-app .fko-mapping-head {
  margin-top: 14px;
  padding-bottom: 2px;
  border-bottom: 1px solid var(--fko-border);
  font-size: .75rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--fko-muted);
}
/* 🔑 FIXED WIDTH FOR THE COUNT so "26 workflows" and "1 workflow" occupy the
   same space and the Save buttons form a straight edge. */
.fko-app .fko-map-usage-inline { text-align: right; }
@media (max-width: 782px) {
  /* ⚠ Stacks rather than shrinking: five columns at 320px gives each about
     50px, which cannot hold a hostname. The header is hidden because a stacked
     row is self-evident and the labels would double every row's height. */
  .fko-app .fko-mapping-form { grid-template-columns: 1fr; }
  .fko-app .fko-mapping-head { display: none; }
}
/* 🔑 A READ-ONLY SOURCE MUST LOOK READ-ONLY. The value is discovered, not
   typed, and an input that renders like an editable one invites a customer to
   change it and wonder why nothing saved. */
.fko-app .fko-map-readonly {
  background-color: var(--fko-navy);
  color: var(--fko-muted);
  cursor: default;
}
/* ⚠ Does not grow: the usage note is context, not a field, and letting it take
   flex space would squeeze the input the customer is actually filling in. */
.fko-app .fko-map-usage-inline { flex: 0 0 auto; font-size: .8rem; }
/* One credential's fields, inside the Mappings expander (user, 2026-08-23).
   🔑 A GRID, not a flex row: the label, source and target columns must line up
   DOWN the list, or a credential with six fields reads as six unrelated rows. */
.fko-app .fko-mapfield-row {
  display: grid;
  grid-template-columns: minmax(120px, 1fr) minmax(140px, 1.4fr) auto minmax(200px, 2fr);
  align-items: center;
  gap: 10px;
  padding: 6px 0;
}
.fko-app .fko-mapfield-label { font-size: .875rem; }
/* ⚠ The arrow does not grow: it is a separator, and letting it take flex space
   would squeeze the target box the customer is actually filling in. */
.fko-app .fko-mapfield-arrow { color: var(--fko-muted); }
/* ⚠ Sits beside the credential NAME, so it must not look like part of it. */
.fko-app .fko-map-have { font-size: .8rem; margin-left: 8px; white-space: nowrap; }
/* ⚠ NOW AN INPUT, NOT A SPAN -- so it needs min-width:0. An <input> carries an
   intrinsic minimum width (~20 chars) that a grid track honours OVER its own
   minmax(), so a long URL blew the column out and pushed the target field off
   the row. Wrapping no longer applies; the value scrolls inside the field. */
.fko-app .fko-mapfield-src {
  font-size: .875rem;
  min-width: 0;
  width: 100%;
  text-overflow: ellipsis;
}
/* The line explaining that fields arrive prefilled from the source. Spans the
   whole grid so it reads as an instruction for the list, not a row in it. */
.fko-app .fko-mapfield-howto {
  font-size: .8rem;
  margin: 0 0 10px;
}
/* ⚠ The per-field <form> is gone (2026-09-01): the panel now builds ONE form
   for the whole credential, the way the Secrets editor does, so the rows are
   .fko-field-row and the form is .fko-agent-form -- both already styled. */
/* The "Start a job" card on the Jobs tab (2026-09-01). */
.fko-app .fko-job-new h3 { margin: 0 0 6px; }
/* The two-button explanation (user, 2026-09-11): muted like the intro, one
   entry per control, the control's name in white so the eye can match it to
   the button on the right. */
.fko-app .fko-job-new > p { margin: 0 0 14px; }
.fko-app .fko-job-help { margin: 0 0 14px; padding: 0 0 0 18px; color: var(--fko-muted); font-size: .9rem; }
.fko-app .fko-job-help li { margin: 0 0 8px; }
.fko-app .fko-job-help:last-child { margin-bottom: 0; }
.fko-app .fko-job-help:last-child li:last-child { margin-bottom: 0; }
.fko-app .fko-job-help strong { color: var(--fko-white); }
/* One route, its name and its two actions.
   ⚠ WRAPS rather than scrolls: two buttons plus a route name overflow a narrow
   viewport, and a row that scrolls sideways hides the button on its right. */
.fko-app .fko-job-route {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 0;
  border-top: 1px solid var(--fko-border);
}
/* The route name on a job row reads in orange (user, 2026-09-11). */
.fko-app .fko-job-route > strong { color: var(--fko-orange); }
/* ⚠ .fko-actions does not right-align inside the panel -- `.fko-app .fko-actions`
   omits justify-content and outranks the base rule -- so state it here. */
.fko-app .fko-job-actions { justify-content: flex-end; }
/* Separates the history from the card above it. */
.fko-app .fko-job-history-title { margin: 22px 0 10px; }

.fko-app .fko-mapfield-actions { justify-content: flex-end; margin-top: 12px; }
@media (max-width: 782px) {
  /* ⚠ Stacks on a narrow screen: four columns at 320px would give each about
     70px, which is unusable for a URL. */
  .fko-app .fko-mapfield-row { grid-template-columns: 1fr; }
}
/* Profile tabs (user, 2026-08-23). Same mechanism, its own radio group.
   🔑 NO RULES OF ITS OWN ANY MORE (2026-09-01). These were per-id and are now
   served by the shared .fko-tab-input--a/--b rules above, so Profile, the
   Migrations wizard and the row expanders cannot drift apart. The markup keeps
   its own unique ids and its own `name`, which is what isolates the radio
   group -- only the STYLING is shared. */
/* ⚠ Woo's own form markup, rendered inside the panel: give it the panel's
   spacing rather than leaving theme defaults to decide. */
.fko-app .fko-tabpanel--billing-address .woocommerce-address-fields__field-wrapper { margin-bottom: 12px; }
/* ⚠ Focus must be visible on the LABEL, since the input itself is clipped --
   now handled once by the shared .fko-tab-input--a/--b/--c:focus-visible rules.
   🔴 THE RULE THAT WAS HERE WAS SUBTLY WRONG: its first selector was
   `.fko-tab-input:focus-visible ~ … label[for="fko-tab-steps"]` -- ANY tab input
   receiving focus outlined the STEPS label, so focusing Mappings or Jobs drew
   the ring on the wrong tab. Positional classes remove the class of bug where a
   selector pairs one tab's state with another tab's label. */

/* The add-path form: From → To on one line where there is room.
   ⚠ `align-items: end` so the two selects sit on a shared baseline with the
   arrow between them; without it the labels above push them out of line. */
/* 🔴 STRETCH, NOT END. `align-items: end` bottom-aligns the three children,
   which made the arrow impossible to centre against the SELECTS: every attempt
   had to reconstruct the select's height, and the only reliable inputs for that
   are unknowable here -- `.fko-app select { font: inherit }` (line 33) resets
   line-height to whatever the THEME sets, so any calc() is a guess.
   🔑 Stretching makes each child full-height; the label stacks push their select
   to the bottom with margin-top:auto, and the arrow centres in the same box the
   selects end up sharing. Nothing needs to know a pixel height. */
.fko-app .fko-path-fields { display: flex; flex-wrap: wrap; gap: 12px;
  align-items: stretch; margin-bottom: 12px; }
/* 🔴 .fko-field HAD NO CSS AT ALL. These two labels were the only users of the
   class and nothing defined it, so they rendered as inline boxes -- the label
   and the select on one line -- which is why the arrow could never be aligned
   by adjusting the arrow. Style them like every other field in the panel
   (.fko-field-row label) and the row becomes two equal stacks with a gap. */
.fko-app .fko-path-fields .fko-field { display: flex; flex: 0 1 200px; flex-direction: column; gap: 4px; }
/* ⚠ Pins the select to the bottom of a stretched column, so two columns with
   different label wrapping still align their selects. */
.fko-app .fko-path-fields .fko-field > span { color: var(--fko-muted); font-size: 0.825rem; font-weight: 700; line-height: 1.4; }
.fko-app .fko-path-fields .fko-field > select {
  width: 100%;
  /* ⚠ margin-top:auto, NOT margin:0. A separate `margin-top: auto` rule above
     this one was silently cancelled by the shorthand -- later rule, equal
     specificity. Pins the select to the bottom of its stretched column so both
     selects share a baseline whatever the labels do. */
  margin: auto 0 0;
  padding: 8px 10px;
  border: 1px solid var(--fko-border); border-radius: 5px;
  background: var(--fko-navy); color: var(--fko-white); font-size: 0.905rem;
}
/* 🔑 THE ARROW CARRIES THE MEANING (user, 2026-08-22: "make the arrow in the
   steps more obvious and centered"). A path is one-way, and the direction is
   the single most important thing on this form -- a small → between two
   look-alike selects was easy to miss entirely.
   ⚠ Centred against the SELECTS, not the label+select stack: the labels sit
   above, so a plain `align-items: center` would ride the arrow up between
   them. Matching the select's own height and centring inside it keeps it on
   the selects' midline whatever the labels do. */
/* 🔑 THE ARROW IS A COLUMN THAT MATCHES ITS NEIGHBOURS -- an empty label line,
   then the glyph on the selects' row. It inherits .fko-field, so it is the same
   flex column as the two labels and needs NO height of its own.
   🔴 DO NOT REINTRODUCE HEIGHT MATHS. Four attempts tried to compute the
   select's height (38px, then calc(font-size x 1.4), then align-self tricks)
   and all were wrong: `.fko-app select { font: inherit }` (line 33) takes
   line-height from the THEME, so that height is not knowable from this file.
   ⚠ flex:none so the arrow column does not stretch like the 200px selects. */
.fko-app .fko-path-fields .fko-promotion-arrow-col { flex: none; align-items: center; }
.fko-app .fko-path-fields .fko-promotion-arrow-col > span:first-child {
  /* The invisible twin of a field label: same metrics, no text. */
  font-size: 0.825rem;
  font-weight: 700;
  line-height: 1.4;
}
.fko-app .fko-path-fields .fko-promotion-arrow-col .fko-promotion-arrow {
  display: flex;
  align-items: center;
  /* Fills the row the selects occupy, whatever height that turns out to be. */
  flex: 1 1 auto;
  padding: 0 4px;
  /* ⚠ RESTATED. The base .fko-promotion-arrow rule sets orange, but this
     selector is more specific and sits later, so anything it does not repeat
     falls back to the inherited colour -- which is how the arrow rendered grey
     in the add form (reported 2026-08-22). */
  color: var(--fko-orange);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1;
}
.fko-app .fko-path-actions { justify-content: flex-end; margin: 4px 0 12px; }
/* ⚠ The remove control sits at the TOP-RIGHT of its path panel, away from the
   findings table -- a destructive-looking control directly beneath a list of
   problems reads as "remove these problems". */
/* The remove form is simply the first item of .fko-path-actions now; the row's
   gap spaces it from the primary. */
.fko-app .fko-path-remove { margin: 0; }
/* The read-only n8n instance line under the agent settings.
   🔑 It sits with the fields that do NOT change it, because "why didn't the
   Instance column change when I renamed the agent?" is otherwise unanswerable
   from this panel -- the value was not shown here at all. */
/* The n8n instance line + its explanation, under the agent settings.
   🔑 It sits with the fields that do NOT change it, because "why didn't the
   Instance column change when I renamed the agent?" is otherwise unanswerable
   from this panel -- the value was not shown here at all. */
.fko-app .fko-agent-instances { margin-top: 10px; padding-top: 10px;
  border-top: 1px solid rgba(255,255,255,.08); font-size: .85rem; }
.fko-app .fko-agent-instances p { margin: 0 0 6px; }
.fko-app .fko-agent-instances p:last-child { margin-bottom: 0; }
/* The environment card header, restored 2026-08-21.
   🔑 Without it a card with no agent yet rendered as an empty box with a
   Remove button -- no name, no URL, nothing to tell two environments apart. */
/* Collapsible environment cards, 2026-08-22 (user: "let's also make each
   environment collapsable").
   🔑 THE HEADER IS THE SUMMARY, so a collapsed card still shows name, type,
   n8n URL and "no agent connected yet" -- enough to tell environments apart
   without opening them.
   ⚠ The native marker is replaced by the same +/- affordance the install
   steps use, so the two disclosures on one card behave alike. */
.fko-app .fko-env-card > summary {
  display: flex;
  align-items: flex-start;
  /* ⚠ 18px, not 10 (user, 2026-09-01: "add some more space between the expand
     button and environment name"). The +/- is a circle, so its visual edge sits
     inside its box -- 10px of box gap read as about 4px of daylight. */
  gap: 18px;
  cursor: pointer;
  list-style: none;
}
.fko-app .fko-env-card > summary::-webkit-details-marker { display: none; }
/* 🔑 The header carries its own bottom margin; only add separation once the
   card is actually open, or a collapsed row sits in too much space. */
.fko-app .fko-env-card > summary .fko-env-header { margin-bottom: 0; flex: 1 1 auto; }
.fko-app .fko-env-card[open] > summary { margin-bottom: 12px; }
.fko-app .fko-env-card > summary:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: 3px; border-radius: 4px; }

/* A stale agent -- one that has stopped heartbeating, 2026-08-22.
   🔴 IT MUST NOT LOOK LIKE A HEALTHY ROW. A dead pod's card was previously
   indistinguishable from a live one, so the wrong card got read and a working
   agent looked broken. The tint is deliberately mild: the agent being gone is
   usually EXPECTED (it was replaced), so this is information, not an alarm. */
.fko-app .fko-row-warning { background: rgba(255, 122, 26, 0.06); border-radius: 4px; }
.fko-app .fko-stale-note { display: block; margin-top: 2px; font-size: .78rem; font-weight: 400; line-height: 1.4; }

.fko-app .fko-env-header { margin-bottom: 12px; }
.fko-app .fko-env-name { margin: 0 0 2px; font-size: 1.05rem; }
/* The two environment actions, side by side and right-aligned (user,
   2026-09-01). Stacked, they read as list items; on one line beside the name
   they read as what they are -- the two things you can do to this environment.
   ⚠ They stay OUTSIDE the <summary>: a <details> nested in a summary would
   toggle the whole card when clicked, so this is a row in the card BODY
   aligned to the same edge, not a move into the header. */
.fko-app .fko-env-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 16px;
  margin-bottom: 12px;
}
/* ⚠ Each keeps its own disclosure behaviour; only the row is new. */
.fko-app .fko-env-actions > details { margin: 0; }
.fko-app .fko-env-meta { margin: 0; font-size: .85rem; }
/* Rename, 2026-08-21 (user: "Need a way to change the environment name").
   🔑 COLLAPSED BY DEFAULT. A card is normally read, not edited; an always-open
   text input would dominate it. The summary carries the affordance.
   ⚠ The marker is removed and replaced by a text-link look because the native
   triangle reads as an expandable *section* -- this is a control. */
.fko-app .fko-env-rename { margin: 0 0 12px; }
/* 🔴 THESE ARE BUTTONS, SO THEY LOOK LIKE ONE (user, 2026-09-01: "buttons
   should have borders and follow our button standards"). Bare orange text read
   as a link -- and next to Download agent, which IS bordered, the pair implied
   two different kinds of control for two identical actions.
   🔑 THE OUTLINE ROLE, matching .fko-outline-button exactly: 2px orange border,
   999px radius, transparent fill. These are secondary actions on the card, and
   the standard reserves the solid fill for the primary one.
   ⚠ Metrics copied from the real rule rather than approximated, or the two
   would drift the moment either changed. */
.fko-app .fko-env-rename > summary {
  display: inline-block;
  /* 🔴 CHROMIUM BLOCKIFIES <summary>: `inline-block` is honoured by Firefox but
     Chrome lays the summary out as a block-level box regardless, so once the
     disclosure opens and its paragraph widens the <details>, the pill stretched
     to the full card width (reported 2026-09-11). `fit-content` sizes it to its
     label in every engine, open or closed. */
  width: fit-content;
  max-width: 100%;
  min-height: 39px;
  padding: 9px 18px;
  box-sizing: border-box;
  border: 2px solid var(--fko-orange);
  border-radius: 999px;
  background: transparent;
  color: var(--fko-orange);
  cursor: pointer;
  font-size: 0.905rem;
  font-weight: 400;
  line-height: 1.35;
  list-style: none;
}
/* ⚠ Hover fills, exactly as .fko-outline-button does -- an underline on a
   bordered control reads as a link inside a button. */
.fko-app .fko-env-rename > summary:hover {
  background: var(--fko-orange);
  color: var(--fko-navy);
}
.fko-app .fko-env-rename > summary::-webkit-details-marker { display: none; }
.fko-app .fko-env-rename > summary:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: 2px; }
.fko-app .fko-env-rename[open] > summary { margin-bottom: 8px; }
/* 🔴 SPECIFICITY, NOT SOURCE ORDER, decides this. .fko-inline-form is
   `display: inline-block` for the one-button forms it was written for; the
   rename form stacks a field over a button, so it needs its own rule at equal
   or greater weight -- which the compound selector gives it. */
.fko-app .fko-env-rename .fko-inline-form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  max-width: 340px;
}
.fko-app .fko-env-rename .fko-field-row { width: 100%; }
.fko-app .fko-env-rename > p { margin: 8px 0 0; font-size: .8rem; line-height: 1.4; }
/* ⚠ `help` on a label with a title, so the explanation is discoverable rather
   than something a customer finds by accident. */
.fko-app .fko-field-row label[title] { cursor: help; }
.fko-app .fko-notice--success { border-left-color: var(--fko-green); background: rgba(74, 222, 128, 0.10); }
.fko-app .fko-notice--notification { border-left-color: var(--fko-amber); background: rgba(244, 189, 80, 0.10); }
/* ⚠ Alphas differ BY DESIGN: the pale blue reads at 0.08, but the saturated
   orange/red nearly vanish on navy at the same alpha — matched by eye, not math. */
.fko-app .fko-notice--warning { border-left-color: var(--fko-orange); background: rgba(255, 122, 26, 0.15); }
.fko-app .fko-notice--error { border-left-color: var(--fko-red); background: rgba(255, 102, 114, 0.13); }

/* ⚠ Supporting lines stay muted so the FIRST line carries the outcome. A whole
   panel in one colour is a wall, not a signal. */
.fko-app .fko-notice .fko-muted { color: var(--fko-muted); font-weight: 400; }

/* Notice close button. ⚠ Positioned, not floated: a float would let a long
   message wrap underneath it. */
.fko-app .fko-notice { position: relative; padding-right: 42px; }
.fko-app .fko-notice-close {
  position: absolute;
  top: 8px;
  right: 10px;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--fko-muted);
  font-size: 1.1rem;
  line-height: 1;
  cursor: pointer;
}
.fko-app .fko-notice-close:hover { color: var(--fko-white); background: var(--fko-navy-3); }

/* Credential VALUE badges -- own class, NOT .fko-badge.
   🔴 THE NAME COLLIDED. `.fko-badge` already exists (see the pricing/member
   pill near the top of this file): border 1px solid currentColor, uppercase,
   999px radius. Reusing it meant these inherited a BORDER that no rule here
   mentioned, so "remove the border" looked applied and rendered unchanged --
   the user saw outlined pills twice.
   ⚠ Do NOT fold these back into .fko-badge. That class is used by the pricing
   catalogue, member roles and agent-status chips, which all WANT the outline.
   🔴 THE SECRET BOXES ARE ALWAYS BLANK, because neither n8n nor the vault will
   reveal a stored secret. That made a healthy credential look broken, and the
   customer asked four separate times whether a save had worked. These badges
   answer it on the row rather than in prose they must read and believe.
   🔑 SAME PALETTE AS THE AUDIT LEGEND AND THE NOTICES -- green/amber/red mean
   the same thing everywhere in the panel, which is the point of a standard.
     ok     every defined field has a value
     warn   fewer stored than defined -- often normal, many fields are optional
     error  a stored field is EMPTY, which fails at run time while looking saved
   ⚠ Quiet, not shouty: these sit beside the Drift button and must not compete
   with it. Colour carries the meaning; weight and size stay constant. */
.fko-app .fko-valbadge {
  display: inline-block;
  /* ⚠ NO LEFT MARGIN: the badge now owns a COLUMN and is no longer trailing a
     name, so an inherited offset would push it off its own cell's edge. */
  padding: 2px 0;
  /* 🔑 EXPLICIT, not merely unmentioned. Relying on "no border declared" is how
     the previous attempt failed -- a same-named class elsewhere supplied one. */
  border: 0;
  text-transform: none;
  /* 🔑 BORDERLESS (user, 2026-08-20). In its own column the badge no longer
     needs a chip outline to separate it from adjacent text, and the border
     competed with the Drift button one column over -- two outlined pills side
     by side read as two buttons, when only one is clickable. Colour and weight
     carry the meaning on their own. */
  /* 🔑 NORMAL STYLE (user, 2026-08-20). Now that the label is one word in its
     own column it IS the cell's value, not a tag pinned to something else, so
     it matches the surrounding table text. Colour alone carries the state --
     bolding it as well made three ordinary values shout. */
  font-size: 0.92rem;
  font-weight: 400;
  vertical-align: middle;
  white-space: nowrap;
  /* 🔑 The tooltip carries the detail, so the cursor must say so -- otherwise
     a customer has no reason to hover and never sees why it is amber. */
  cursor: help;
}
.fko-app .fko-valbadge-ok    { color: var(--fko-green); }
.fko-app .fko-valbadge-warn  { color: var(--fko-amber); }
.fko-app .fko-valbadge-error { color: var(--fko-red); }
/* 🔑 "None" is where every credential STARTS -- nothing has been entered yet.
   Red read as a fault the customer had caused; orange reads as the prompt it is
   (user, 2026-08-30). Red stays for Rejected, where n8n refused a real value. */
.fko-app .fko-valbadge-todo  { color: var(--fko-orange); }

/* ⚠ RETAINED: older markup still emits this class. Removing it would silently
   drop the badge on any row rendered by a cached template. */
.fko-app .fko-stored-badge {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 8px;
  border: 1px solid var(--fko-border);
  border-radius: 999px;
  color: var(--fko-green);
  font-size: 0.72rem;
  font-weight: 600;
  vertical-align: middle;
  white-space: nowrap;
}

/* Drift explanation, rendered INSIDE the expanded credential panel.
   🔴 THIS REPLACED A MODAL (user, 2026-08-20). The dialog stated the problem in
   one place and the remedy in another, and its own text ended by telling the
   customer to expand the row -- which is now what opened it.
   🔑 Attention colour, not error red: drift is a state to resolve, not a
   failure. It sits above the note and the fields it refers to. */
/* Why a credential holds no value: a plan limit, a name our vault path refused,
   a type whose fields are all secret (user, 2026-08-31).

   🔑 BLUE, NOT ORANGE. Drift is a WARNING -- something is wrong and the customer
   should act. This is INFORMATION: the row is empty for a reason we know, and
   the next step is written in the message. Reusing the orange band would say
   "problem" about a credential that simply needs typing once, and would break
   the one-colour-per-state rule the status legend depends on. */
/* No workflow references this secret (user, 2026-08-31).

   🔑 ITS OWN COLOUR, per the one-colour-per-state rule. Drift is ORANGE (a
   warning: something is wrong). A skip reason is BLUE (information: here is why
   it is empty). This is a RECOMMENDATION -- nothing is broken, but an unused
   credential is still a live secret and deleting it reduces what an attacker
   could use.

   ⚠ NOT AMBER, though it was the obvious pick: amber is already THE warning
   colour here -- .fko-text-warning, .fko-badge--warning, .fko-notice--notification
   all use it. Painting this amber would say "warning" about a row whose own
   message says nothing is wrong, and would break the one-colour-per-state rule
   the status legend depends on. Purple is unused for status.

   ⚠ AND NOT A MADE-UP --fko-yellow: the palette has no yellow, so the var()
   fallback would have silently painted a colour no other state uses. */
/* No workflow references this secret.
   🔑 NO COLOUR OF ITS OWN (user, 2026-09-01: "should be orange"). The note now
   carries .fko-notice--warning, the SAME class the agent-update banner uses, so
   the two match by construction rather than by two rules being kept in step.
   A purple accent was tried first to keep one-colour-per-state; the user's call
   is that this reads as a warning, and reusing the class is how it stays one.
   ⚠ DO NOT ADD A border-left-color HERE. It would have higher specificity than
   .fko-notice--warning and silently win, which is the bug this replaced. */

.fko-app .fko-skip-note {
  margin: 0 0 10px;
  padding: 10px 12px;
  border-left: 3px solid var(--fko-blue, #4a9eff);
  background: rgba(74, 158, 255, 0.12);
  color: var(--fko-white);
  font-size: 0.9rem;
  line-height: 1.5;
}

.fko-app .fko-drift-note {
  margin: 0 0 10px;
  padding: 10px 12px;
  /* Drift = WARNING = ORANGE under the notification color standard (user,
     2026-08-18) — matches the Drift badge and the drift card border. */
  border-left: 3px solid var(--fko-orange);
  /* 0.15 not 0.08: saturated orange at low alpha vanishes on navy while the
     pale blue shows — matched by eye against the credentials instruction. */
  background: rgba(255, 122, 26, 0.15);
  /* White font per the refined standard -- the band and tint say "warning". */
  color: var(--fko-white);
  font-size: 0.9rem;
  line-height: 1.5;
}
/* ⚠ The workflow names belong to the customer and can be long; they must wrap
   rather than stretch the panel. */
.fko-app .fko-drift-workflows {
  margin: 0 0 12px;
  padding-left: 26px;
  color: var(--fko-muted);
  font-size: 0.87rem;
  overflow-wrap: anywhere;
}
.fko-app .fko-drift-workflows li { margin: 2px 0; }

/* The required mark on a credential field label.
   🔴 ORANGE, AND ITS OWN ELEMENT (user, 2026-08-20). Appended to the label text
   it inherited the label's grey, so the one mark separating a mandatory field
   from an optional one was invisible.
   🔑 WHAT "REQUIRED" MEANS HERE: n8n ships no default for the field, so only the
   customer can supply it. n8n's own `required` flag is declared on 21.5% of
   fields and on NONE for httpBasicAuth, gitlabApi or wooCommerceApi -- it would
   mark almost nothing. */
.fko-app .fko-required-mark {
  /* ⚠ --fko-orange is the real token; --fko-accent does not exist in this
     palette and a var() fallback would have silently painted the wrong
     orange while looking deliberate. */
  color: var(--fko-orange);
  font-weight: 700;
  /* ⚠ Not padded away from the label: it reads as part of the name, which is
     what an asterisk is for. */
  margin-left: 1px;
}

/* A STORED value that is already invalid.
   🔴 Validation only ran on SAVE, so a bad value written before the rule existed
   sat in the vault with nothing to show for it -- a GitLab server field holding
   backslashes, which n8n cannot resolve. The customer learned about it from a
   failing workflow, or never.
   🔑 AMBER, NOT RED. The credential is not broken in the panel's own terms --
   something IS stored -- but it will fail when a workflow runs. Same colour as
   the Missing badge, which carries the same "look at this" meaning. */
/* ⚠ RETAINED for the stored-value note elsewhere in the panel. Field-level
   VALIDATION messages moved to a modal (user, 2026-08-20: "orange is too hard
   to read, let's go back to the popup") -- coloured text under every box was
   hard to read on this navy ground and easy to scroll past. */
.fko-app .fko-field-warning {
  display: block;
  margin-top: 4px;
  color: var(--fko-orange);
  font-size: 0.82rem;
  line-height: 1.4;
}

/* ⚠ The border marks WHICH box, since the message sits under it and a long form
   would otherwise leave the customer hunting. */
.fko-app .fko-field-invalid input,
.fko-app .fko-field-invalid select {
  border-color: var(--fko-orange);
}

/* 🔴 OUR OWN VALIDATION BUBBLE, SHOWN ON BLUR (user, 2026-08-20: "add a
   validation pop-up when validation fails for the active field, should trigger
   when field loses focus").

   ⚠ WHY NOT THE NATIVE reportValidity() BUBBLE. That was built twice
   (748b18b, 0633280) and reverted both times: reportValidity() PULLS FOCUS BACK
   to the offending field, so tabbing forward bounced the customer backwards and
   the form felt like a trap. This looks like the native bubble and steals
   nothing -- focus lands wherever the customer sent it.

   🔑 The native bubble is still what SUBMIT uses. That path works and is
   untouched; this only adds a message on the way out of a field. */
.fko-field-row { position: relative; }

/* 🔴 LEFT OF THE FIELD, ON THE SAME PLANE (user, 2026-08-20: "move the message
   to the left of the field on the same plane, so it's easier to determine which
   field it applies to").

   It sat UNDER the input, in the gap before the next row -- equidistant from the
   field it described and the one below it, so on a multi-row form the customer
   had to work out which box it meant. On the same horizontal line as the input
   it reads as part of that row, the way the label does.

   🔑 THE ROW IS TWO FLEX COLUMNS, label and input, each flex:1 1 190px. The
   bubble occupies the LEFT column and is centred against the input's own line,
   so it aligns with the box it is complaining about rather than with the row. */
.fko-field-bubble {
  position: absolute;
  z-index: 30;
  /* The left column: from the row's left edge to where the input begins,
     less the flex gap. */
  right: calc(50% + 6px);
  /* 🔴 TOP-ALIGNED WITH THE VALUE FIELD (user, 2026-08-20: "align to top of
     value field").

     ⚠ NOT `top: 0`. The row's top is where the LABEL starts; the input sits
     below it inside its own column, so aligning to the row floats the message
     above the box it describes.

     🔑 THE OFFSET IS MEASURED, NOT ASSUMED. bubbleFor() reads the input's real
     top relative to the row and sets --fko-bubble-top, because deriving it from
     the label's font-size and line-height would silently drift the moment either
     changes -- and a few pixels out reads as a rendering fault. The fallback
     only applies before the first measurement. */
  top: var(--fko-bubble-top, 0px);
  max-width: min(360px, 46%);
  display: flex;
  gap: 8px;
  align-items: flex-start;
  padding: 9px 12px;
  border: 1px solid var(--fko-border);
  /* ⚠ THE ORANGE EDGE MOVES TO THE RIGHT, pointing AT the field it describes.
     On the left it pointed away from it. */
  border-right: 3px solid var(--fko-orange);
  border-radius: 5px;
  /* ⚠ OPAQUE ON PURPOSE. It overlays the label beneath it, and a translucent
     panel over text renders as unreadable double text. */
  background: var(--fko-navy-3);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
  color: var(--fko-white);
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.35;
  text-align: left;
}

/* 🔴 STACK ON A NARROW PANEL. Below the row's flex-wrap breakpoint the label and
   input are no longer side by side, so there IS no left column -- a bubble
   pinned to `right: 50%` would sit on top of the label. It returns to sitting
   under the field, which is the only place it fits. */
@media (max-width: 700px) {
  .fko-field-bubble {
    position: static;
    transform: none;
    max-width: 100%;
    margin-top: 6px;
    border-right: 1px solid var(--fko-border);
    border-left: 3px solid var(--fko-orange);
  }
}

/* The mark that says "problem", matching the native bubble's icon slot. */
.fko-field-bubble::before {
  content: "!";
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  margin-top: 1px;
  border-radius: 3px;
  background: var(--fko-orange);
  color: var(--fko-navy);
  font-weight: 900;
  font-size: 0.75rem;
  line-height: 16px;
  text-align: center;
}

/* ⚠ Never animate a message that must be READ before focus moves on. */
@media (prefers-reduced-motion: no-preference) {
  .fko-field-bubble { animation: fko-bubble-in 120ms ease-out; }
}
@keyframes fko-bubble-in {
  from { opacity: 0; transform: translateY(-3px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* A save button gated by live validation.
   🔴 DISABLED, NOT HIDDEN (user, 2026-08-20: "let's be proactive"). A button
   that vanishes leaves the customer wondering where it went; one that is
   visibly unavailable, with a title saying why, points at the problem.
   ⚠ The field-level message is the real explanation -- this only signals that
   the form is not ready. */
.fko-app .fko-button[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* 🔴 BLOCKED IS NOT `disabled` (user, 2026-08-20: "the save button should
   remain disabled until all validation of each value field passes").

   The button must READ as unavailable while every required field is not yet
   valid -- but it must still receive the click, because a truly disabled button
   fires no `submit`, and the submit handler is what shows the validation popup.
   Disabling it outright would replace a clear message with silence, which is
   the regression that made the button unconditionally enabled in the first
   place.

   ⚠ Same metrics as [disabled] above ON PURPOSE. If these drift, "blocked" and
   "disabled" become two visibly different states for one meaning. */
.fko-app .fko-button.fko-button-blocked {
  opacity: 0.5;
  cursor: not-allowed;
}
.fko-app .fko-button.fko-button-blocked:hover {
  border-color: var(--fko-orange);
  background: var(--fko-orange);
}

/* Agents rendered INSIDE their environment card (user, 2026-08-20).
   🔴 They used to live in a separate "Connected agents" table, so one agent
   appeared twice under two different names -- its environment's, and an empty
   display tag that rendered as "Unnamed agent".
   🔑 Indented and separated so the nesting is visible: these belong TO the
   environment above, they are not siblings of it. */
.fko-app .fko-env-agents {
  margin-top: 18px;
  padding-top: 4px;
  border-top: 1px solid var(--fko-border);
}
/* ⚠ The nested cards must not repeat the parent's chrome -- a panel inside a
   panel reads as two unrelated things stacked. */
.fko-app .fko-env-agents .fko-panel {
  margin: 12px 0 0;
  padding: 14px 16px;
  background: var(--fko-navy-2);
}
.fko-app .fko-env-agents .fko-panel:first-of-type { margin-top: 8px; }
/* The nested heading is a level down from the card's own <h3>. */
.fko-app .fko-env-agents .fko-panel h3 { font-size: 1rem; }

/* ── Agent card action rows ──────────────────────────────────────────────── */

/* 🔑 THE STANDARD PAIR ON ONE LINE, WITHOUT NESTING FORMS.
   Disconnect carries its own action and nonce, and a <form> inside a <form> is
   invalid HTML -- the browser drops the inner one and the button submits the
   SETTINGS form, silently doing the wrong thing. The two therefore stay sibling
   forms and are laid out as one row here.
   ⚠ A negative margin was tried first and rejected: it hard-codes the height of
   the row above, so any change to button padding silently misaligns them. Flex
   ordering on a shared wrapper has no such coupling. */
.fko-app .fko-agent-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  justify-content: flex-end;
  margin-top: 14px;
}
/* Both children are forms; they must not stretch or add their own spacing. */
.fko-app .fko-agent-actions > form { margin: 0; }

/* 🔴 REMOVE ENVIRONMENT NEEDS AIR (user, 2026-08-20). It sat tight against the
   rule above it, reading as part of the agent block rather than as the
   card-level action it is -- and it destroys the whole environment. */
/* ⚠ .fko-env-danger IS GONE (user, 2026-09-01: "remove the bar above the remove
   button"). Its border-top drew that rule, and the margin below it set the
   button apart as a danger zone of its own. Remove now sits in .fko-env-actions
   with Rename and Replace, so the separation is neither drawn nor needed. */

/* ══════════════════════════════════════════════════════════════════════════
   THE PANEL OWNS ITS PAGE (user, 2026-08-24)

   The panel renders inside WooCommerce's my-account shell, so Woo's own
   account tabs and the "My account" page title framed a screen that already
   has its own navigation down the left. Two navigations for one page is the
   confusion; the panel's is the one that works.

   🔴 HIDDEN IN CSS, NOT UNHOOKED. `woocommerce_account_navigation` is a global
   template action -- removing it would strip the nav from Orders, Addresses and
   every other Woo page too, stranding a customer who navigated there. Scoping
   to `.fko-panel-page`, which the body_class filter adds only on our endpoint,
   keeps the change to the one screen.

   ⚠ THE CONTENT COLUMN MUST RECLAIM THE WIDTH. Woo's layout floats the nav at
   ~25% and the content at ~75%; hiding the nav alone leaves a quarter of the
   page empty.

   ⚠ SEVERAL SELECTORS PER THING, deliberately. Themes rename or re-wrap these:
   Storefront uses `.woocommerce-MyAccount-navigation`, blocks themes emit
   `.wp-block-woocommerce-customer-account`, and the title may be `.entry-title`
   or `.woocommerce-account .page-title`. Listing them costs nothing and a miss
   shows as a stray heading nobody notices in review.
   ══════════════════════════════════════════════════════════════════════════ */
body.fko-panel-page .woocommerce-MyAccount-navigation,
body.fko-panel-page nav.woocommerce-MyAccount-navigation,
body.fko-panel-page .wp-block-woocommerce-customer-account { display: none !important; }

body.fko-panel-page .woocommerce-MyAccount-content {
  width: 100%;
  float: none;
  margin: 0;
  padding: 0;
}

/* ⚠ THE TITLE ONLY ON THIS PAGE. The panel prints its own section heading, so
   "My account" above it names the wrong thing. */
body.fko-panel-page .entry-title,
body.fko-panel-page .page-title,
body.fko-panel-page .woocommerce-account .entry-title { display: none; }

/* 🔑 A NOTICE MUST STILL SHOW. wc_print_notices() renders into the content
   column -- the Profile and address forms depend on it to report a failed save,
   and hiding it would make a validation error invisible. */
body.fko-panel-page .woocommerce-notices-wrapper { display: block; }

/* ==========================================================================
   PROVISIONING PROGRESS — the phased, striped bar shown while a tenant builds
   ==========================================================================
   🔴 EVERY POSITION HERE IS REAL. The width comes from a phase the DEPLOY
   reported after that step actually succeeded -- never a timer, never an
   interpolation. If provisioning stalls at `vault`, the bar sits at `vault`.
   The stripes animate so the customer can see the page is live; the BAR does
   not move unless the deploy moved.

   ⚠ .fko-app PREFIX ON EVERY RULE. The theme and Woo load AFTER us and win
   ties at equal specificity -- an unprefixed .fko-progress would silently lose
   and look exactly like "the CSS did not deploy".
   ========================================================================== */

.fko-app .fko-progress {
  margin: 18px 0 6px;
}

.fko-app .fko-progress__track {
  position: relative;
  height: 14px;
  border-radius: 999px;
  background: var(--fko-navy-3);
  border: 1px solid var(--fko-border);
  overflow: hidden;
}

.fko-app .fko-progress__fill {
  height: 100%;
  border-radius: 999px;
  /* The requested blue/orange stripe, from the brand tokens rather than raw
     hex so a palette change carries here too. */
  background-image: repeating-linear-gradient(
    45deg,
    var(--fko-blue) 0 12px,
    var(--fko-orange) 12px 24px
  );
  background-size: 34px 100%;
  animation: fko-progress-stripes 1s linear infinite;
  /* Width is set inline from the reported phase. The transition is on WIDTH
     only, so the bar glides between real positions instead of jumping -- it
     never advances on its own. */
  transition: width 600ms ease-out;
}

@keyframes fko-progress-stripes {
  from { background-position: 0 0; }
  to   { background-position: 34px 0; }
}

/* ⚠ An animated diagonal pattern is a known migraine and vestibular trigger.
   Progress is still fully visible -- the stripes simply stop moving. */
@media (prefers-reduced-motion: reduce) {
  .fko-app .fko-progress__fill {
    animation: none;
  }
}

.fko-app .fko-progress__phases {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: grid;
  gap: 6px;
}

/* Pre-phase: the deploy has reported nothing yet, so the step list would be
   describing work that may never happen (a CLAIMED tenant runs no build).
   The neutral line below stands in its place; advanceProgress() removes
   .is-prephase the moment a real phase arrives. Scoped .fko-app to match the
   rule above -- an unscoped rule loses to it on specificity. */
.fko-app .fko-progress__phases.is-prephase {
  display: none;
}

.fko-app .fko-progress__prephase {
  margin: 14px 0 0;
  color: var(--fko-muted);
  font-size: 14px;
}

.fko-app .fko-progress__phase {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--fko-muted);
  font-size: 14px;
}

.fko-app .fko-progress__phase::before {
  content: "○";
  color: var(--fko-border);
  font-size: 13px;
  line-height: 1;
}

.fko-app .fko-progress__phase.is-done {
  color: var(--fko-white);
}

.fko-app .fko-progress__phase.is-done::before {
  content: "●";
  color: var(--fko-green);
}

.fko-app .fko-progress__phase.is-current {
  color: var(--fko-white);
  font-weight: 600;
}

.fko-app .fko-progress__phase.is-current::before {
  content: "●";
  color: var(--fko-orange);
}

/* The live sub-line under the current phase (2026-10-20), on both bars:
   "Installing the ingress controller (4 of 4)". The li is a flex row (bullet,
   spinner, label); .has-detail makes it a GRID instead, so the line takes its
   own row, indented past the bullet and the spinner so it reads as belonging to
   the label. ⚠ NOT flex-wrap: with wrap, a label too long for the row (the
   workspace bar's "Creating your isolated environment" at phone width) jumped
   onto a row of its own, under the bullet -- measured in Chrome at 360px. In the
   grid the label stays beside the bullet and wraps inside its own column.
   Normal weight and muted: it is a detail of the bold current step. */
.fko-app .fko-progress__phase.has-detail {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr);
  column-gap: 10px;
  row-gap: 2px;
}

.fko-app .fko-progress__phase .fko-progress__detail {
  grid-column: 1 / -1;
  padding-left: 21px;   /* the bullet (~11px) + the li's 10px gap: under the label */
  color: var(--fko-muted);
  font-size: 13px;
  font-weight: 400;
}

/* ...and past the phase spinner too while it shows (16px + 2px margin + 10px
   gap) -- it is gated on the poller, so the label moves with it. Measured in
   Chrome: the sub-line's text starts at the label's x in both states. The
   workspace bar's sub-line (2026-10-20) follows its own poller's class. */
.fko-app.is-cell-build-polling .fko-progress__phase .fko-progress__detail,
.fko-app.is-provisioning-polling .fko-progress__phase .fko-progress__detail {
  padding-left: 49px;
}

.fko-app .fko-progress__elapsed {
  margin-top: 10px;
  font-size: 13px;
  color: var(--fko-muted);
}

/* The message now LEADS the bar rather than trailing it, so its margin sits
   below instead of above. */
.fko-app .fko-progress__elapsed--lead {
  margin-top: 0;
  margin-bottom: 10px;
}

/* 🔴 ONE SPINNER ON THE PAGE, ALWAYS ON THE THING THAT IS WORKING.
   Before any phase is reported there is no step to attach it to, so the lead
   message carries it. From the first phase onward the CURRENT PHASE carries it
   and this one goes -- two spinners would be two claims about what is busy. */
.fko-app .fko-progress__elapsed--lead.has-progress .fko-sync-spinner {
  display: none;
}

/* ⚠ The phase's own bullet (::before) still marks state; the spinner sits
   BESIDE it, so a customer reads "this step, working" rather than the bullet
   being replaced by an animation. */
.fko-app .fko-progress__spinner {
  flex: 0 0 auto;
  margin-right: 2px;
}

/* ⚠ THE AUTO-REFRESH PROMISE IS HIDDEN UNLESS THE POLLER IS RUNNING.
   `is-provisioning-polling` is added by countPoller() and removed when it gives
   up, so the page never promises to update itself while nothing is watching --
   which is the difference between a promise and a lie (progress standard, user
   2026-08-23). The give-up message is written into the same element, so it
   stays visible after the class is removed only because the poller sets its
   text; an idle page shows neither. */
.fko-app .fko-progress__elapsed {
  display: none;
  align-items: center;
  gap: 8px;
}

.fko-app.is-provisioning-polling .fko-progress__elapsed {
  display: flex;
}

/* ⚠ THE PHASE SPINNER IS GATED THE SAME WAY, and must be. It is the same
   promise in a different place: an animation on "Creating your Git repository"
   while nothing is polling tells a customer that step is running when the page
   has actually stopped watching. Hidden by default, shown only while the poller
   is alive -- exactly as the message above it. */
.fko-app .fko-progress__spinner {
  display: none;
}

.fko-app.is-provisioning-polling .fko-progress__spinner {
  display: inline-block;
}

/* The PRIVATE-CELL INFRASTRUCTURE bar (2026-10-18) keeps the same promise the
   same way: its message and phase spinner show only while ITS poller
   (is-cell-build-polling, countPoller) is alive. */
.fko-app.is-cell-build-polling .fko-progress__elapsed {
  display: flex;
}

.fko-app.is-cell-build-polling .fko-progress__spinner {
  display: inline-block;
}

/* A private build shows two bars in one card -- infrastructure, then the
   workspace -- so each names itself. */
.fko-app .fko-progress__title {
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--fko-white);
}

/* The infrastructure bar, collapsed once the cell is ready. */
.fko-app .fko-progress__ready {
  color: var(--fko-green);
  font-weight: 600;
}

/* The ✓ is GREEN like the done steps (user, 2026-10-20). The line rendered all
   white: the notice's paragraph rule out-specifies .fko-progress__ready, so the
   mark carries its own selector through the notice. */
.fko-app .fko-notice p.fko-progress__ready .fko-progress__ready-mark,
.fko-app p.fko-progress__ready .fko-progress__ready-mark {
  color: var(--fko-green);
}

/* -----------------------------------------------------------------------------
   Getting started & status guide (user, 2026-08-30)
   -----------------------------------------------------------------------------
   🔴 ONE CARD ON EVERY PAGE, directly under the section heading. Each page had
   grown its own status vocabulary with nothing on screen defining it -- 37
   Workflows rows reading "Unknown" under a heading saying GIT-BACKED AUTOMATION,
   where the real meaning was "never backed up at all".

   ⚠ PREFIXED AND NAMING THEIR CLASSES. The panel loads before the theme, so an
   unprefixed selector at equal specificity loses on source order. Everything
   here is scoped under .fko-guide for that reason. */
.fko-guide {
  margin: 0 0 18px;
  padding: 14px 16px;
}
.fko-guide-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* The whole head is the hit target, not just the 22px circle. */
.fko-guide-toggle { flex: 0 0 auto; }
/* One small control at the right end of the head row -- the Overview's "Take
   the tour" (user, 2026-09-11). `margin-left: auto` pushes it to the edge
   without touching the title's flex basis. */
.fko-guide-aside { flex: 0 0 auto; margin-left: auto; }
.fko-guide-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
}
.fko-guide-body { margin-top: 12px; }
.fko-guide-intro {
  margin: 0 0 14px;
  max-width: 78ch;
  color: var(--fko-muted);
}
.fko-guide-subhead {
  margin: 14px 0 8px;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fko-muted);
}
.fko-guide-steps {
  margin: 0 0 4px;
  padding-left: 20px;
}
.fko-guide-steps li { margin-bottom: 7px; }
.fko-guide-steps strong { margin-right: 6px; }
.fko-guide-steps span { color: var(--fko-muted); }

/* ⚠ A definition list, not a table: the legend is label -> meaning, and a table
   would need a header row saying nothing ("Status", "Meaning"). Grid gives the
   same alignment without the scaffolding, and collapses cleanly on narrow
   screens where a two-column table would force a horizontal scroll. */
.fko-guide-legend { margin: 0; }
.fko-guide-legend-row {
  display: grid;
  grid-template-columns: minmax(120px, 160px) 1fr;
  gap: 6px 14px;
  padding: 6px 0;
  border-top: 1px solid var(--fko-border);
}
.fko-guide-legend-row:first-child { border-top: 0; }
.fko-guide-legend dt { margin: 0; }
.fko-guide-legend dd {
  margin: 0;
  color: var(--fko-muted);
  max-width: 74ch;
}
.fko-guide-chip {
  display: inline-block;
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.4;
}
/* 🔑 THE CHIP MUST LOOK LIKE THE CELL IT EXPLAINS. A legend whose colours differ
   from the table teaches the wrong mapping, so these reuse the same tokens the
   status cells use rather than introducing a parallel palette.

   🔴 ONE COLOUR PER STATUS (user, 2026-08-30). The first cut had three tones for
   five states, so Missing/Failed/Rejected were all the same orange and the
   legend could not be used to tell them apart in the table -- which is the one
   job it has. These map 1:1 onto .fko-valbadge-ok / -warn / -error, the classes
   the STORED VALUES column actually emits. */
.fko-guide-chip--healthy { color: var(--fko-green); }   /* All / Stored     */
.fko-guide-chip--warn    { color: var(--fko-amber); }   /* Missing          */
.fko-guide-chip--error   { color: var(--fko-red); }     /* None / Rejected  */
.fko-guide-chip--accent  { color: var(--fko-orange); }  /* Drift            */
.fko-guide-chip--info    { color: var(--fko-blue); }    /* Pending / queued */
.fko-guide-chip--muted   { color: var(--fko-muted); }   /* neutral          */

/* ⚠ THE PILL IS A PICTURE OF THE CONTROL. Drift renders in the table as an
   orange outline pill, so the legend shows that pill rather than coloured text:
   a customer matches a shape. It inherits .fko-drift-sample, so it cannot drift
   from the real one. Sized down slightly to sit on a legend row. */
.fko-guide-legend .fko-drift-sample {
  font-size: 0.78rem;
  padding: 1px 10px;
}

/* The page's own cautions, carried into the card from the panel that used to
   sit beneath it. */
.fko-guide-note {
  margin: 0 0 10px;
  max-width: 82ch;
  color: var(--fko-muted);
}

@media (max-width: 640px) {
  .fko-guide-legend-row { grid-template-columns: 1fr; gap: 2px; }
}


/* Workflow verdicts: one colour per answer.
   🔴 "Not backed up" and "Unknown" both used .fko-muted, so two genuinely
   different answers -- "not committed yet, nothing to do" and "we could not
   check" -- were the same grey (user, 2026-08-30). A legend cannot help a
   customer tell apart two rows that render identically.
   🔑 Blue for pending work FlowKeyOps will do on its own; grey stays for the
   one state that really is an absence of information. */
.fko-app .fko-status-drifted { color: var(--fko-orange); }
.fko-app .fko-status-pending { color: var(--fko-blue); }
.fko-app .fko-status-unknown { color: var(--fko-muted); }

/* Secret jobs (2026-09-10): the overwrite tick sits beside its button and reads
   as part of the same action, not as a stray form control. */
.fko-app .fko-job-overwrite { display: inline-flex; align-items: center; gap: 6px; margin-right: 10px; font-size: .85rem; color: var(--fko-muted); }

/* One secret job, expandable to its secrets (2026-09-10). The summary is the
   row; the list is the detail, indented so it reads as belonging to it. */
.fko-app .fko-job-detail summary { cursor: pointer; font-weight: 600; }
.fko-app .fko-job-detail-list { margin: 8px 0 0 14px; padding: 0; list-style: none; font-size: .85rem; }
.fko-app .fko-job-detail-list li { margin: 3px 0; }

/* Secret job rows reuse the rewrites disclosure marker (orange minus when open), per check-collapse-markers. */
/* 🔴 THE +/- IS DRAWN, NOT TYPED. Three attempts to centre a '+' / '−'
   CHARACTER failed, because a glyph's position is decided by the FONT, not the
   box: in Inter both are maths symbols sitting well above the baseline, so the
   box centred perfectly while the ink inside it did not. line-height:0 centres
   the collapsed LINE BOX, which still is not the ink.
   🔑 So the box is empty and the bars are two background gradients, both
   centred by `background-position: center`. No font, no positioning, no nested
   pseudo-element -- geometry we control, so the cross cannot sit off-centre.
   ⚠ WHITE while collapsed (the action is "expand"), ORANGE when open (the
   action is "collapse"): the colour says what the control WILL do. */
.fko-app .fko-audit-detail > summary::before,
.fko-app .fko-install-steps > summary::before,
.fko-app .fko-env-card > summary::before,
.fko-app .fko-promotion-path > summary::before,
.fko-app .fko-job-detail > summary::before,
.fko-app .fko-mapping-step > summary::before {
  content: '';
  flex: none;
  display: block;
  width: 22px;
  height: 22px;
  /* 🔴 THE PILL STANDARD, not a square with a grey border. This shipped as
     `1px var(--fko-border)` / `4px` radius -- an invented look that matched
     nothing else in the panel (reported 2026-08-22, the second time this
     control was wrong). Round and 2px orange, like every other bordered
     control here. */
  /* ⚠ WHITE WHILE COLLAPSED, ORANGE ONCE OPEN (user, 2026-08-19 for the audit
     rows, 2026-08-22 for these): the colour says what the control WILL do --
     white offers "expand", orange offers "collapse" -- and the orange also
     marks the one row you are currently reading. */
  border: 2px solid var(--fko-white);
  border-radius: 999px;
  background-image:
    linear-gradient(var(--fko-white), var(--fko-white)),
    linear-gradient(var(--fko-white), var(--fko-white));
  background-size: 10px 2px, 2px 10px;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
}

/* ── Expand / collapse HOVER STANDARD (user, 2026-09-11) ─────────────────
   Every square or round expand/collapse control on every page hovers the
   same way: the LABEL and the GLYPH turn orange, the background does not
   change. The earlier fill (orange background, navy text) read as a pressed
   button on a control that only discloses.
   ⚠ :not([open]) on the collapsed rule: hover adds a pseudo-class, so without
   it the two-bar (+) image would outrank the open rule's one-bar (−) image and
   a hovered open control would show a plus. The open hover repaints one bar. */
.fko-app .fko-audit-detail > summary:hover,
.fko-app .fko-install-steps > summary:hover,
.fko-app .fko-env-card > summary:hover,
.fko-app .fko-promotion-path > summary:hover,
.fko-app .fko-job-detail > summary:hover,
.fko-app .fko-mapping-step > summary:hover {
  color: var(--fko-orange);
}
.fko-app .fko-audit-detail:not([open]) > summary:hover::before,
.fko-app .fko-install-steps:not([open]) > summary:hover::before,
.fko-app .fko-env-card:not([open]) > summary:hover::before,
.fko-app .fko-promotion-path:not([open]) > summary:hover::before,
.fko-app .fko-job-detail:not([open]) > summary:hover::before,
.fko-app .fko-mapping-step:not([open]) > summary:hover::before {
  border-color: var(--fko-orange);
  background-image:
    linear-gradient(var(--fko-orange), var(--fko-orange)),
    linear-gradient(var(--fko-orange), var(--fko-orange));
}
.fko-app .fko-audit-detail[open] > summary:hover::before,
.fko-app .fko-install-steps[open] > summary:hover::before,
.fko-app .fko-env-card[open] > summary:hover::before,
.fko-app .fko-promotion-path[open] > summary:hover::before,
.fko-app .fko-job-detail[open] > summary:hover::before,
.fko-app .fko-mapping-step[open] > summary:hover::before {
  border-color: var(--fko-orange);
  background-image: linear-gradient(var(--fko-orange), var(--fko-orange));
}
.fko-app .fko-job-detail > summary {
  /* 🔴 MATCH THE SECRETS ROW (user, 2026-09-02: "should match the second
     image"). Secrets renders its migration control as a small square expander
     with a plain text label; this was an outline PILL with button padding, so
     the two Migration tabs read as two different features.
     🔑 The +/- itself comes from the shared ::before marker, which the small
     variant below resizes -- so only the chrome is removed here. */
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  /* 0.92rem with the table text it sits in (Inventory's application names,
     Migrations' "2 workflows"), 2026-09-26. */
  font-size: 0.92rem;
  list-style: none;
}
.fko-app .fko-job-detail > summary::-webkit-details-marker { display: none; }
/* Hover: the expand/collapse standard -- text and glyph turn orange, the
   background stays (user, 2026-09-11). See the HOVER STANDARD block. */
/* 🔴 THE SAME BUTTON, SMALLER AND SQUARE (user, 2026-09-01: "use the same type
   of buttons as the main expand buttons, only smaller, 1px and square").
   These sit INSIDE an already-expanded panel, so matching the 22px pill of the
   control that opened them made two levels of one thing look like one level.

   🔑 CHANGE ONLY WHAT MUST CHANGE. Everything else -- the gradients, their
   centring, the open-state colour, the hover -- is inherited from .fko-expander,
   so the two can never drift apart. An earlier attempt restated box-sizing and
   min-width here and the glyphs stopped agreeing with each other.

   ⚠ EVEN NUMBERS ONLY. The +/- is two background gradients centred in the box:
   at 18px with a 2px bar the bar lands on a whole pixel, at 1px on a half one,
   and a half-pixel bar renders a different width per button -- which is exactly
   what "uneven and unmatching" looked like. 18/2 keeps the same 9:1 proportion
   the 22px/2px original has. */
/* 🔴 THE SAME SMALL SQUARE AS THE SECRETS ROW (user, 2026-09-02). This marker
   inherits the shared 22px pill; the Secrets migration control is 18px with a
   1px square border, and the two Migration tabs sit one nav item apart.
   ⚠ background-size shrinks with the box, and the bar stays 2px: at 18px with
   a 1px border the content box is 16px, so a 2px bar centres on a whole pixel
   and a 1px bar lands on 7.5 -- which renders a different width per button. */
.fko-app .fko-job-detail > summary::before {
  width: 18px;
  height: 18px;
  border-width: 1px;
  border-radius: 1px;
  background-size: 8px 2px, 2px 8px;
}

/* ── Credential impact (item 3.22, 2026-09-11) ──────────────────────────
   The Used by count on the Secrets table, and the heading above the workflow
   list in an expanded row. White, because it is the fact the row is opened
   for; the list beneath keeps the drift-workflows styling. */
.fko-app .fko-cred-usage { color: var(--fko-white); cursor: help; }
.fko-app .fko-cred-impact { margin: 12px 0 4px; color: var(--fko-white); font-size: .875rem; }

/* ── Audit: the content of a committed change (item 3.7a) ───────────────
   A button under the detail list, a status line, and the diff as plain
   preformatted text in its own scrolling box -- never wider than the row. */
.fko-app .fko-audit-diff { margin-top: 10px; }
.fko-app .fko-audit-diff-body {
  margin: 8px 0 0;
  padding: 10px 12px;
  max-height: 420px;
  overflow: auto;
  border: 1px solid var(--fko-border);
  border-radius: 6px;
  background: var(--fko-navy-2);
  color: var(--fko-white);
  font-size: .78rem;
  line-height: 1.45;
}
/* The side-by-side diff (fkoRenderDiff) fills the box; the box scrolls. */
.fko-app .fko-audit-diff-body .fko-audit-diff-pane { padding: 0; overflow: visible; }
.fko-app .fko-audit-diff-body .fko-audit-diff-file { margin: 10px 0 4px; font-weight: 700; }
.fko-app .fko-audit-diff-body .fko-audit-diff-file:first-child { margin-top: 0; }
.fko-app .fko-audit-diff-body .fko-audit-diff-text { margin: 0; white-space: pre; font: inherit; }

/* ── Workflow tags (item 3.24) ───────────────────────────────────────────
   The customer's own n8n labels under the workflow name: small, muted,
   bordered chips -- a grouping cue, never a verdict, so no colour. */
.fko-app .fko-wf-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.fko-app .fko-wf-tag {
  display: inline-block;
  padding: 1px 7px;
  border: 1px solid var(--fko-border);
  border-radius: 999px;
  color: var(--fko-muted);
  font-size: .72rem;
  line-height: 1.5;
}

/* ── Applications in use (item 3.21) ────────────────────────────────────
   Sits above the Workflows table with the same shell; nothing but spacing. */
.fko-app .fko-apps-card { margin-bottom: 16px; }
/* 🔑 TOP-ALIGN THE APPLICATIONS ROWS (user, 2026-09-12). Expanding a row lists
   its workflows, so the first cell grows while the count and the node type stay
   one line -- and `vertical-align: middle` (the panel-wide default on
   `.fko-table th/td`) floated them to the centre of a tall row, away from the
   name they describe.
   ⚠ SCOPED TO THIS TABLE by its own class: middle alignment is right for every
   other table in the panel, where a row is one line high. */
.fko-app .fko-apps-table th,
.fko-app .fko-apps-table td { vertical-align: top; }
/* External endpoints (item 3.21): the name input and its Save sit on one line
   inside the first cell, so the row reads "name -> address" left to right. */
.fko-app .fko-endpoints-card { margin-bottom: 16px; }
/* How a workflow starts, inside its expanded row (item 3.35, user 2026-09-12).
   A definition list rather than a table: two to four facts about ONE workflow,
   where a table would imply a set to compare. */
.fko-app .fko-trigger-facts { display: block; }
/* Restore an earlier version (item 3.36). One column: a picker, then the
   verdict for the version chosen. */
.fko-app .fko-restore-panel { padding: 4px 0; }
.fko-app .fko-restore-intro { margin: 0 0 10px; }
.fko-app .fko-restore-pick, .fko-app .fko-restore-map { max-width: 420px; padding: 7px 9px; border: 1px solid var(--fko-border); border-radius: 5px; background: var(--fko-navy); color: var(--fko-white); font-size: 0.875rem; }
.fko-app .fko-restore-verdict { margin-top: 12px; display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
/* 🔑 THE CREDENTIAL GAPS BLOCK ON A WORKFLOW ROW (2026-09-22). It reuses the
   restore preflight's list classes deliberately -- the same problem stated the
   same way in both places -- so only the wrapper and its title are new.
   ⚠ flex-basis:100% for the same reason the migration note needs it: the tab's
   action area is a flex row, and a block dropped in would shrink-wrap beside
   the controls instead of taking its own line. */
.fko-app .fko-wf-credgaps {
  flex: 1 0 100%;
  margin: 12px 0 0;
  text-align: left;
}
.fko-app .fko-wf-credgaps-title {
  margin: 0 0 4px;
  font-size: .875rem;
  font-weight: 600;
  color: var(--fko-amber);
}
.fko-app .fko-restore-creds { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.fko-app .fko-restore-creds li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.fko-app .fko-restore-cred-name { font-weight: 700; }
.fko-app .fko-restore-ok { color: var(--fko-green); font-size: 0.825rem; }
.fko-app .fko-restore-gap { color: var(--fko-amber); font-size: 0.825rem; }
/* Shown in place of the mapping dropdown when no existing secret matches the
   needed credential type. Muted: it is an absence of options, not a fault. */
.fko-app .fko-restore-map-none { color: var(--fko-muted); font-size: 0.825rem; align-self: center; }
/* A loading line in the Versions tab: spinner beside the text, baseline-aligned.
   ⚠ Uses .fko-sync-spinner so the reduced-motion rule below still applies. */
/* The Add-a-credential form, borrowed into the Versions tab so a missing secret
   can be created without leaving the workflow. Set apart from the checklist
   above it, but it is NOT a warning -- muted border, no accent rule. */
.fko-app .fko-restore-create { margin: .75rem 0; padding: .75rem; border: 1px solid var(--fko-border); border-radius: 8px; }
.fko-app .fko-restore-create-head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin-bottom: .5rem; }
/* A field the restore fills in and the customer must not change: n8n binds a
   credential by name AND type, so a different value creates a secret the
   restore still cannot find. Muted, not disabled-looking, because the value
   IS correct -- it is simply not a choice here. */
.fko-app .fko-restore-create [data-fko-locked] { opacity: .75; cursor: not-allowed; }
.fko-app .fko-restore-loading { display: flex; align-items: center; gap: .5rem; }
.fko-app .fko-restore-loading .fko-sync-spinner { margin-top: 0; }
.fko-app .fko-restore-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
/* View then Restore, on one row with real space between them (user,
   2026-09-12). They were stacked and touching, which put the destructive
   button directly under the reading one. */
.fko-app .fko-restore-buttons { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin-top: 12px; }
/* 🔑 FOUR EQUAL COLUMNS ACROSS 75% OF THE PANEL (user, 2026-09-12: "distribute
   the Source columns wider so they are less crowded, try 75% width"). As a
   content-sized flex row the four facts huddled at the left third. Narrow
   screens fall back to two columns at full width (see the media rule). */
.fko-app .fko-trigger-list { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px 24px; width: 75%; max-width: 100%; margin: 0; }
@media (max-width: 900px) {
  .fko-app .fko-trigger-list { grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; }
}
.fko-app .fko-trigger-list > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.fko-app .fko-trigger-list dt { color: var(--fko-muted); font-size: 0.775rem; font-weight: 850; letter-spacing: .06em; text-transform: uppercase; }
.fko-app .fko-trigger-list dd { margin: 0; font-size: 0.875rem; overflow-wrap: anywhere; }
/* ⚠ .fko-field-row is the ESTABLISHED input shell and styles its own inputs;
   inventing a class here rendered fine and would have drifted from every other
   form in the panel (caught by check-orphan-classes). This only tightens the
   row so the name and Save sit on one line inside a table cell. */
.fko-app .fko-endpoint-name-form { align-items: center; gap: 8px; margin: 0; flex-wrap: nowrap; }
/* 🔑 WHITE FIELD, DARK TEXT, GREY HINT (user, 2026-09-12). .fko-field-row's
   own input rule paints navy-on-white text for the dark forms elsewhere; this
   field is a light box, so all three colours are set EXPLICITLY here and the
   selector is one step more specific than the shared rule so it wins.
   ⚠ The placeholder needs its own pseudo-element rule: it inherits `color`
   nowhere, and left alone it renders at the browser's default opacity against
   white, which is nearly invisible. */
.fko-app .fko-endpoint-name-form input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 240px;
  background: var(--fko-input-bg);
  color: var(--fko-input-ink);
  border: 1px solid var(--fko-border);
}
.fko-app .fko-endpoint-name-form input[type="text"]::placeholder {
  color: var(--fko-input-placeholder);
  opacity: 1;
}
/* Keyboard focus must still be visible on a light field: the orange ring the
   rest of the panel uses reads correctly against white. */
.fko-app .fko-endpoint-name-form input[type="text"]:focus-visible {
  outline: 2px solid var(--fko-orange);
  outline-offset: 1px;
}

/* ── Secrets vault toggle (item 3.19) ───────────────────────────────────
   Two stacked radio options with their explanation beside them. */
.fko-app .fko-secrets-mode-form { display: grid; gap: 10px; }
.fko-app .fko-secrets-mode-option { display: grid; grid-template-columns: 18px minmax(0, 1fr); gap: 10px; align-items: start; padding: 10px 12px; border: 1px solid var(--fko-border); border-radius: 6px; cursor: pointer; }
.fko-app .fko-secrets-mode-option input { margin-top: 3px; }
.fko-app .fko-secrets-mode-option span { color: var(--fko-muted); font-size: .9rem; line-height: 1.45; }
.fko-app .fko-secrets-mode-option strong { color: var(--fko-white); }
/* The scan button inside the Applications card (item 3.21): same row shell as
   every other card action, sits under the heading text. */
.fko-app .fko-apps-scan { padding: 0 18px 16px; justify-content: flex-start; }
/* Workflows: find + sort (item 3.1 residual, 2026-09-16). The label matches the Audit filters; the sort glyph is drawn from aria-sort so the state on screen IS the state the script holds. */
.fko-wf-filter { display: grid; gap: 5px; margin-right: auto; } .fko-wf-filter span { color: var(--fko-muted); font-size: 0.785rem; text-transform: uppercase; letter-spacing: .04em; } .fko-wf-filter input[type="search"] { box-sizing: border-box; height: 34px; min-width: 220px; padding: 7px 10px; border: 1px solid var(--fko-border); border-radius: 0; background: transparent; color: inherit; font-family: inherit; font-size: 0.885rem; line-height: 1.2; } .fko-wf-filter-count { margin: 0 0 10px; font-size: 0.85rem; } .fko-workflow-table th[data-fko-sort] { cursor: pointer; user-select: none; white-space: nowrap; } .fko-workflow-table th[data-fko-sort]:focus-visible { outline: 2px solid var(--fko-orange); outline-offset: -2px; } .fko-workflow-table th[data-fko-sort]::after { content: ""; display: inline-block; width: 0; height: 0; margin-left: 6px; border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 5px solid currentColor; opacity: .25; vertical-align: middle; } .fko-workflow-table th[aria-sort="ascending"]::after { opacity: 1; } .fko-workflow-table th[aria-sort="descending"]::after { border-bottom: 0; border-top: 5px solid currentColor; opacity: 1; }
/* The setup wizard's own tour entry point (1.26.469 shipped the class without a rule; check-orphan-classes flagged it). */
.fko-setup-tour { margin-top: 12px; }
/* The shared scan banner inside the setup wizard (2026-09-21): it replaced .fko-setup-wait there and lacked that banner's top margin, so it rode up over the Scan again button above it. Same spacing and type size as the banner it stands in for. */
.fko-setup .fko-scan-banner { margin: 12px 0 0; font-size: 0.885rem; }
/* Rename a secret in place (2026-09-21): a pencil beside the name, then an input with Save and Cancel in the same cell. */
.fko-name-edit { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 8px; } .fko-app .fko-name-edit-btn { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; padding: 0; border: 1px solid var(--fko-border); border-radius: 6px; background: transparent; color: var(--fko-muted); font-size: 0.9rem; line-height: 1; cursor: pointer; } .fko-name-edit-form { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 8px; } .fko-name-edit-form input[type="text"] { box-sizing: border-box; height: 34px; min-width: 260px; padding: 7px 10px; border: 1px solid var(--fko-border); border-radius: 0; background: transparent; color: inherit; font-family: inherit; font-size: 0.885rem; }
/* The Find row above a table (2026-09-21), shared by Secrets and Workflows so the box sits in the same place on both. It reuses the Workflows filter's own classes so the two cannot drift. */
.fko-find-row { justify-content: flex-start; margin: 0 0 12px; } .fko-credential-table tr[hidden] { display: none; }

/* ── Rule health (item 3.41) ───────────────────────────────────────────────
   A rewrite rule whose target no longer resolves. Rendered on the Mappings tab
   beside the rule it describes, and summed on the Drift page in its own
   section -- never inside the drift table, since a broken rule is independent
   of whether a workflow matches its approved version. */
.fko-app .fko-map-health { margin: 12px 0 0; }
.fko-app .fko-map-health-list { margin: 8px 0 0; padding: 0; list-style: none; display: grid; gap: 10px; }
/* Each finding is one row: the rule, then what is wrong with it. Hairline
   between, matching the ROW SEPARATOR STANDARD rather than a second card. */
.fko-app .fko-map-health-list li {
  display: grid; gap: 3px; padding: 0 0 10px;
  border-bottom: 1px solid var(--fko-border);
}
.fko-app .fko-map-health-list li:last-child { padding-bottom: 0; border-bottom: 0; }
.fko-app .fko-map-health-rule { color: var(--fko-white); font-size: 0.905rem; }
.fko-app .fko-map-health-detail { color: var(--fko-muted); font-size: 0.865rem; }
/* ⚠ The scan age is the quietest thing in the block: it qualifies the verdict
   without competing with it. */
.fko-app .fko-map-health-age { margin: 10px 0 0; font-size: 0.845rem; }

/* ── Empty credential fields (user, 2026-09-23) ────────────────────────────
   Named so the customer knows which box to open. Matches .fko-unused-note:
   both are warnings inside the same expander, and two shapes there would read
   as two products. */
.fko-app .fko-empty-fields-note { margin: 12px 0 0; }
.fko-app .fko-empty-fields-list {
  margin: 6px 0 8px; padding: 0 0 0 18px;
  color: var(--fko-white); font-size: 0.905rem;
}
.fko-app .fko-empty-fields-list li { margin: 2px 0; }

/* ── The unused-credential notice is AMBER, matching its own row badge
   (user, 2026-09-23) ───────────────────────────────────────────────────────
   The row's "No workflow uses it" cell is `.fko-valbadge-warn` (amber), and
   the notice inside the expander says the same thing in full — so the two must
   read as one statement rather than two severities.

   🔴 A VARIANT, NOT AN EDIT TO `.fko-notice--warning`. That class is shared
   with the agent-update banner and several template notices (7 call sites);
   recolouring it here would repaint all of them to fix one. Scoped to this
   notice, the base stays orange for everything that still wants orange.

   ⚠ Specificity: `.fko-app` + two classes (0,3,0) beats the base
   `.fko-notice--warning` (0,1,0), so source order does not decide this — the
   trap this stylesheet's own notes warn about. */
.fko-app .fko-notice--warning.fko-unused-note {
  border-left-color: var(--fko-amber);
  background: rgba(244, 189, 80, 0.12);
}

/* ─────────────────────────────────────────────────────────────────────────────
   🔑 ONE ORANGE TEXT STYLE (user, 2026-09-26: "I want all orange text to match
   the No value stored text"). Every orange STATUS or MESSAGE -- a verdict
   (Blocked, Drifted, Missing), a finding, a field warning, a note -- reads at
   the same size and weight as the in-field "No value stored — enter one"
   placeholder: orange, weight 400, 0.92rem -- the size of Synced / Ready in the
   status columns (user, 2026-09-26: "make the smallest text size same as Synced").
   Before this there were four sizes (0.82 / 0.845 / 0.875 / 0.92rem) and two
   weights (400 / 700) for the same kind of text.

   ⚠ NOT headings, controls or decoration, which follow their own standards:
   eyebrows (uppercase section labels), button text, the active nav item, pill
   badges, the brand mark, arrows, the required-field asterisk, calendar
   counts, and .fko-drift-sample (a picture of a button, drawn to match it).

   ⚠ AT THE FOOT OF THE FILE ON PURPOSE: several of these classes are declared
   earlier at the same specificity (.fko-app .fko-valbadge sets 0.92rem;
   .fko-app .fko-field-warning 0.82rem), and source order decides a tie.
   `p.fko-orphan-note` matches its own (0,2,1) rule. The Workflows-table size
   override no longer lists .fko-text-accent.
   ───────────────────────────────────────────────────────────────────────────── */
.fko-app .fko-text-accent,
.fko-app .fko-valbadge-todo,
.fko-app .fko-drift-verdict,
.fko-app .fko-status-drifted,
.fko-app .fko-field-warning,
.fko-app p.fko-orphan-note,
.fko-app .fko-orphan-note-text {
  color: var(--fko-orange);
  font-size: 0.92rem;
  font-weight: 400;
}
/* The reference itself, in a rule of its own: a pseudo-element in a selector
   list makes some engines drop the WHOLE list. */
.fko-app input.fko-input-missing::placeholder { color: var(--fko-orange); font-size: 0.92rem; font-weight: 400; }

/* ─────────────────────────────────────────────────────────────────────────────
   🔑 THE STATUS TEXT STANDARD, EVERY PAGE (user, 2026-09-26: "make the smallest
   text size same as Synced ... make this the standard across all pages").
   Every coloured status or message word -- green, blue, amber, orange, red --
   is 0.92rem, the size of Synced / Ready in the Workflows status columns.
   Until now only that table was sized; the same words elsewhere took the
   table-cell (0.875rem), tile (0.815rem) or paragraph size.

   ⚠ WEIGHT 400 TOO (see below): the computed table statuses were already 400,
   and the bold tile verdicts / route flag were the visible outliers.
   ⚠ NOT ON CONTROLS: a button carrying a status colour (the red Delete) keeps
   the button standard's size -- hence `:not([class*="button"])`.
   Enforced by tools/check-status-text.js.
   ───────────────────────────────────────────────────────────────────────────── */
.fko-app .fko-text-healthy:not([class*="button"]),
.fko-app .fko-text-info:not([class*="button"]),
.fko-app .fko-text-warning:not([class*="button"]),
.fko-app .fko-text-accent:not([class*="button"]),
.fko-app .fko-text-danger:not([class*="button"]),
.fko-app .fko-valbadge,
.fko-app .fko-status-verdict,
.fko-app .fko-outcome {
  font-size: 0.92rem;
}
/*
 * 🔴 WHAT 1.26.580 MISSED, found by rendering the LIVE pages as the customer and
 * computing every status word (user, 2026-09-26: "deployed 580 and text is
 * still inconsistent"). The first check probed markup written for it; the pages
 * render differently:
 *   - "No workflow uses it" is a BARE .fko-valbadge-warn (no base class) -> 0.875rem
 *   - the Used by column's "2 workflows" (.fko-cred-usage) -> 0.875rem
 *   - "Inactive" is a grey .fko-muted span directly in a table cell -> 0.905rem
 *     on Overview (0.92rem on Workflows only because of a table-specific rule)
 *   - the tile verdicts (.fko-stat-trend) and the Migrations route flag were
 *     BOLD, and the uncoloured "No changes this week" was 0.815rem
 * 🔑 ONE STANDARD NOW, SIZE AND WEIGHT: 0.92rem, weight 400 -- exactly what the
 * table statuses (Synced, Active, Ready, Missing, All) already computed to.
 */
.fko-app [class*="fko-valbadge-"],
.fko-app .fko-cred-usage,
.fko-app .fko-table td > span.fko-muted,
.fko-app .fko-stat .fko-stat-trend,
.fko-app .fko-route-flag {
  font-size: 0.92rem;
}
.fko-app .fko-text-healthy:not([class*="button"]),
.fko-app .fko-text-info:not([class*="button"]),
.fko-app .fko-text-warning:not([class*="button"]),
.fko-app .fko-text-danger:not([class*="button"]),
.fko-app .fko-stat .fko-stat-trend,
.fko-app .fko-route-flag {
  font-weight: 400;
}

/* ── Insights charts (item 3.45 v1.1, user 2026-10-05: "add graphs and/or charts") ──
   Drawn on the SERVER as plain SVG/CSS: no chart library and no script, so a chart
   renders even when the panel JS does not. Sizes come from the counts as inline
   widths/heights; every colour is a token. ⚠ A tone here names a COLOUR, not a
   status: each chart carries its own key (.fko-ins-key), so a segment is never read
   through the page legend. Bars are not controls, so their pill radius is a shape,
   not the button standard. */
.fko-app .fko-ins-tabs .fko-tablist { flex-wrap: wrap; }
.fko-app .fko-ins-chart { display: block; width: 100%; height: auto; max-height: 240px; margin: 8px 0 4px; }
.fko-app .fko-ins-axis { fill: var(--fko-muted); font-size: 12px; }
.fko-app .fko-ins-axis-line { stroke: var(--fko-border); stroke-width: 1; }
.fko-app .fko-ins-rows { display: grid; gap: 10px; margin: 6px 0 10px; }
.fko-app .fko-ins-row { display: grid; grid-template-columns: minmax(90px, 180px) 1fr auto; align-items: center; gap: 12px; }
.fko-app .fko-ins-row-label { overflow: hidden; font-size: .92rem; text-overflow: ellipsis; white-space: nowrap; }
.fko-app .fko-ins-row-value { color: var(--fko-muted); font-size: .92rem; white-space: nowrap; }
/* Allowance tachometers (Insights -> Health). Zone band = the legend's three colours, faint; fill = the reading's tone; needle in the text colour. */
.fko-app .fko-ins-gauges { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 18px 14px; margin: 8px 0 12px; }
.fko-app .fko-ins-gauge { display: flex; flex-direction: column; align-items: center; margin: 0; text-align: center; }
.fko-app .fko-ins-gauge-dial { display: block; width: 100%; max-width: 170px; height: auto; }
.fko-app .fko-ins-gauge-zone { opacity: .45; }
.fko-app .fko-ins-gauge-track { fill: var(--fko-navy-3); }
.fko-app .fko-ins-gauge-needle { fill: var(--fko-white); }
.fko-app .fko-ins-gauge figcaption { display: grid; gap: 2px; margin-top: 4px; }
.fko-app .fko-ins-gauge-value { color: var(--fko-white); font-size: 1.15rem; font-weight: 600; }
.fko-app .fko-ins-gauge-label { font-size: .92rem; }
/* 5.PO: the Master Subscription Agreement read in place, and the notes exchanged about it. */
.fko-app .fko-po-agreement { max-height: 420px; overflow: auto; margin: 10px 0 14px; padding: 14px 18px; border: 1px solid var(--fko-border); border-radius: 8px; background: var(--fko-navy-3); font-size: .92rem; line-height: 1.55; }
.fko-app .fko-po-agreement h1 { font-size: 1.15rem; margin: 0 0 8px; }
.fko-app .fko-po-agreement h2 { font-size: 1rem; margin: 16px 0 6px; }
.fko-app .fko-po-note { white-space: pre-wrap; margin: 4px 0 12px; padding: 8px 12px; border-left: 3px solid var(--fko-border); }
.fko-app .fko-po-agreement:fullscreen { max-height: none; height: 100%; margin: 0; padding: 32px max(24px, calc((100vw - 880px) / 2)); border: 0; border-radius: 0; background: var(--fko-navy); font-size: 1rem; }
/* "Request changes" stays hidden until its checkbox is ticked: a sibling toggle, so it works without JavaScript. */
/* Accept rows (user, 2026-10-10): the statement centred, the button centred beneath it, and the button
   disabled until the box is ticked. (0,3,0) so it beats the right-aligning .fko-agent-actions. */
.fko-app .fko-actions.fko-po-accept-row { flex-direction: column; align-items: center; justify-content: center; gap: 12px; margin-top: 24px; text-align: center; }
.fko-app .fko-po-accept-row .fko-job-overwrite { margin-right: 0; font-size: .92rem; }
.fko-app .fko-po-accept-row .fko-button:disabled { opacity: .45; cursor: not-allowed; }
.fko-app .fko-po-contest-toggle { margin: 0 6px 0 0; vertical-align: middle; }
.fko-app .fko-po-contest-toggle + label { display: inline; }
.fko-app .fko-po-contest { display: none; margin-top: 10px; }
.fko-app .fko-po-contest-toggle:checked ~ .fko-po-contest { display: block; }
.fko-app .fko-po-divider { margin: 18px 0 14px; border: 0; border-top: 1px solid var(--fko-border); }
/* The changes table: section dropdown | the customer's note. */
.fko-app .fko-po-changes th:first-child, .fko-app .fko-po-changes td:first-child { width: 38%; }
.fko-app .fko-po-changes td { vertical-align: top; }
.fko-app .fko-po-changes select, .fko-app .fko-po-changes textarea { width: 100%; margin: 0; padding: 8px 10px; border: 1px solid var(--fko-border); border-radius: 5px; background: var(--fko-navy); color: var(--fko-white); font: inherit; font-size: 0.905rem; }
.fko-app .fko-po-changes textarea { min-height: 72px; resize: vertical; }
.fko-app .fko-po-note-cell { white-space: pre-wrap; }
.fko-app .fko-ins-gauge .fko-ins-row-value { white-space: normal; }
.fko-app .fko-ins-bar { display: flex; height: 12px; overflow: hidden; border-radius: 999px; background: var(--fko-navy-3); }
.fko-app .fko-ins-seg { display: block; height: 100%; }
.fko-app .fko-ins-key { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 4px 0 14px; padding: 0; color: var(--fko-muted); font-size: .85rem; list-style: none; }
.fko-app .fko-ins-key li { display: flex; align-items: center; gap: 6px; margin: 0; }
.fko-app .fko-ins-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 999px; }
.fko-app .fko-ins-tone--healthy { background: var(--fko-green); fill: var(--fko-green); }
.fko-app .fko-ins-tone--info { background: var(--fko-blue); fill: var(--fko-blue); }
.fko-app .fko-ins-tone--warn { background: var(--fko-amber); fill: var(--fko-amber); }
.fko-app .fko-ins-tone--accent { background: var(--fko-orange); fill: var(--fko-orange); }
.fko-app .fko-ins-tone--error { background: var(--fko-red); fill: var(--fko-red); }
.fko-app .fko-ins-tone--muted { background: var(--fko-muted); fill: var(--fko-muted); }
.fko-app .fko-ins-tone--purple { background: var(--fko-purple); fill: var(--fko-purple); }
@media (max-width: 640px) {
  .fko-app .fko-ins-row { grid-template-columns: 1fr auto; }
  .fko-app .fko-ins-row-label { grid-column: 1 / -1; }
}
/* Estate donut (user, 2026-10-05: "pie should look similar to the one in Overview
   page") -- the Overview's .fko-donut / .fko-legend, larger because it has the card to
   itself. `--keyed`: each row brings its own swatch instead of the four positional dots. */
.fko-app .fko-ins-donut-row { grid-template-columns: 170px minmax(0, 1fr); gap: 16px; margin: 6px 0 10px; }
.fko-app .fko-ins-donut-row .fko-donut { width: 140px; height: 140px; }
.fko-app .fko-ins-donut-row .fko-donut::after { width: 88px; height: 88px; }
.fko-app .fko-ins-donut-row .fko-donut strong { font-size: 1.9rem; }
.fko-app .fko-ins-donut-row .fko-legend { gap: 9px; font-size: .92rem; }
.fko-app .fko-legend--keyed span::before { content: none; }
.fko-app .fko-legend--keyed .fko-ins-swatch { flex: 0 0 auto; margin-right: 6px; }
@media (max-width: 640px) {
  .fko-app .fko-ins-donut-row { grid-template-columns: 1fr; justify-items: center; }
  .fko-app .fko-ins-donut-row .fko-legend { width: 100%; }
}

/* Inventory → Applications donut (moved from Insights → Estate, user 2026-10-05). */
.fko-app .fko-inv-donut { margin: 4px 0 14px; }
/* Insights → Estate: the shape of the automation. Counts are LINKS to the Inventory
   tab or page that lists them, so they are anchors, not buttons (button standard). */
.fko-app .fko-ins-counts { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 10px; margin: 4px 0 18px; }
.fko-app .fko-ins-count { display: grid; gap: 4px; padding: 12px 14px; border: 1px solid var(--fko-border); border-radius: 9px; background: var(--fko-navy); color: var(--fko-white); text-decoration: none; }
.fko-app a.fko-ins-count:hover { border-color: var(--fko-orange); }
.fko-app .fko-ins-count strong { font-size: 1.6rem; line-height: 1.1; }
.fko-app .fko-ins-count span { color: var(--fko-muted); font-size: .92rem; }
.fko-app .fko-ins-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 28px; margin-bottom: 8px; }
.fko-app .fko-ins-subhead { margin: 0 0 10px; color: var(--fko-white); font-size: .95rem; font-weight: 700; }
.fko-app .fko-ins-attn { display: grid; gap: 6px; margin: 0 0 8px; padding: 0; list-style: none; font-size: .92rem; }
@media (max-width: 900px) {
  .fko-app .fko-ins-counts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .fko-app .fko-ins-split { grid-template-columns: 1fr; }
}
@media (max-width: 520px) {
  .fko-app .fko-ins-counts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Settings → Security (user, 2026-10-05): Wordfence's own passkey and 2FA views,
   rendered by its shortcodes, RE-SKINNED to the panel (user: "match the rest of
   the CP pages"). Wordfence ships white cards, blue buttons and a pale-blue info
   box; below they become navy cards, light text, CP inputs and orange pills.

   🔴 SPECIFICITY, NOT !important. Wordfence's stylesheet is printed INLINE in the
   body (after this file), so it wins every tie -- each rule here out-ranks its
   counterpart instead: buttons are `a.wfls-btn.wfls-btn` (0,4,1) against
   Wordfence's deepest `.wfls-btn-default.wfls-disabled.wfls-focus` (0,3,0).

   ⚠ jQuery UI dialogs (recovery codes, deactivate confirm) are appended to
   <body>, OUTSIDE .fko-app, and keep Wordfence's own look. */
.fko-app .fko-settings-tabs .fko-tabpanel--b .fko-security-panel { margin-top: 0; }
.fko-app .fko-wordfence + .fko-wordfence { margin-top: 20px; }
.fko-app .fko-wordfence p { color: var(--fko-white); }

/* Cards: Wordfence's white .wfls-block -> a navy card inside the navy-2 panel. */
.fko-app .fko-wordfence .wfls-block {
  margin: 0 0 12px;
  border: 1px solid var(--fko-border);
  border-radius: 9px;
  background: var(--fko-navy);
  box-shadow: none;
  color: var(--fko-white);
}
.fko-app .fko-wordfence .wfls-block .wfls-block-header-border-bottom { border-bottom: 1px solid var(--fko-border); }
.fko-app .fko-wordfence .wfls-block .wfls-block-title,
.fko-app .fko-wordfence .wfls-block .wfls-block-title strong,
.fko-app .fko-wordfence .wfls-block .wfls-two-factor-card-title,
.fko-app .fko-wordfence .wfls-block .wfls-passkey-card-title { font-family: inherit; font-weight: 700; color: var(--fko-white); }
.fko-app .fko-wordfence .wfls-block .wfls-block-subtitle,
.fko-app .fko-wordfence .wfls-block .wfls-block-content p,
.fko-app .fko-wordfence .wfls-block .wfls-passkey-empty { color: var(--fko-muted); }
.fko-app .fko-wordfence .wfls-block .wfls-block-footer { border-top: 1px solid var(--fko-border); background: transparent; }
.fko-app .fko-wordfence .wfls-block .wfls-two-factor-card-title-icon,
.fko-app .fko-wordfence .wfls-block .wfls-passkey-card-title-icon,
.fko-app .fko-wordfence .wfls-block .wfls-passkey-item-icon { color: var(--fko-orange); }
.fko-app .fko-wordfence .wfls-block .wfls-passkey-item { border-color: var(--fko-border); }
.fko-app .fko-wordfence .wfls-block .wfls-recovery-codes li { color: var(--fko-white); }

/* Links: the panel's orange, not Wordfence blue. */
.fko-app .fko-wordfence a:not(.wfls-btn) { color: var(--fko-orange); text-decoration: none; }
.fko-app .fko-wordfence a:not(.wfls-btn):hover { text-decoration: underline; }

/* Text inputs: the CP input surface -- white, 5px (fields are not pills). */
.fko-app .fko-wordfence input[type="text"].wfls-input-text,
.fko-app .fko-wordfence input#wfls-activate-field {
  min-height: 39px;
  padding: 8px 10px;
  border: 1px solid var(--fko-border);
  border-radius: 5px;
  background: var(--fko-input-bg);
  color: var(--fko-input-ink);
  box-shadow: none;
}
.fko-app .fko-wordfence input[type="text"].wfls-input-text::placeholder,
.fko-app .fko-wordfence input#wfls-activate-field::placeholder { color: var(--fko-input-placeholder); font-style: normal; }

/* Buttons: the BUTTON STANDARD. The two that finish the job (Add passkey,
   Activate) are the solid primary; every other Wordfence button is the
   orange outline pill. Same metrics as .fko-button. */
.fko-app .fko-wordfence a.wfls-btn.wfls-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 39px;
  padding: 9px 18px;
  border: 2px solid var(--fko-orange);
  border-radius: 999px;
  background: transparent;
  color: var(--fko-orange);
  font-size: 0.905rem;
  font-weight: 400;
  text-decoration: none;
}
.fko-app .fko-wordfence a.wfls-btn.wfls-btn:hover,
.fko-app .fko-wordfence a.wfls-btn.wfls-btn:focus-visible { background: var(--fko-orange); color: var(--fko-navy); }
.fko-app .fko-wordfence a.wfls-btn.wfls-btn#wfls-activate,
.fko-app .fko-wordfence a.wfls-btn.wfls-btn#wfls-passkey-add { background: var(--fko-orange); color: var(--fko-navy); }
.fko-app .fko-wordfence a.wfls-btn.wfls-btn#wfls-activate:hover,
.fko-app .fko-wordfence a.wfls-btn.wfls-btn#wfls-passkey-add:hover { border-color: #ff964e; background: #ff964e; }
.fko-app .fko-wordfence a.wfls-btn.wfls-btn.wfls-disabled { opacity: .45; cursor: not-allowed; }
.fko-app .fko-wordfence a.wfls-btn.wfls-btn.wfls-disabled:hover { background: transparent; color: var(--fko-orange); }
.fko-app .fko-wordfence a.wfls-btn.wfls-btn#wfls-activate.wfls-disabled:hover,
.fko-app .fko-wordfence a.wfls-btn.wfls-btn#wfls-passkey-add.wfls-disabled:hover { border-color: var(--fko-orange); background: var(--fko-orange); color: var(--fko-navy); }

/* The QR code must stay dark-on-white or phones cannot scan it. */
.fko-app .fko-wordfence #wfls-qr-code { box-sizing: content-box; padding: 10px; border-radius: 9px; background: #ffffff; }

/* Wordfence's pale-blue passkey explainer -> a navy card with orange accents. */
.fko-app .fko-wordfence .wfls-passkey-information {
  border: 1px solid var(--fko-border);
  border-radius: 9px;
  background: var(--fko-navy);
  color: var(--fko-white);
}
.fko-app .fko-wordfence .wfls-passkey-information .wfls-passkey-information-title { color: var(--fko-white); }
.fko-app .fko-wordfence .wfls-passkey-information .wfls-passkey-information-copy > p,
.fko-app .fko-wordfence .wfls-passkey-information .wfls-passkey-information-list { color: var(--fko-muted); }
.fko-app .fko-wordfence .wfls-passkey-information .wfls-passkey-information-list strong { color: var(--fko-white); }
.fko-app .fko-wordfence .wfls-passkey-information .wfls-passkey-information-list { border-left-color: var(--fko-border); }
.fko-app .fko-wordfence .wfls-passkey-information .wfls-passkey-information-illustration,
.fko-app .fko-wordfence .wfls-passkey-information .wfls-passkey-information-list-icon { color: var(--fko-orange); }

/* 🔴 The press message sits on its own line under the controls, with room
   above it (user 2026-10-07: "crowding the button"). In a table it spans the
   row below the one pressed. */
.fko-app [data-fko-instant-notice].fko-notice,
.fko-app .fko-instant-row .fko-notice {
	display: block;
	width: 100%;
	box-sizing: border-box;
	margin-top: 0.85rem;
	flex: 1 0 100%;
}
.fko-app .fko-instant-row > td {
	padding-top: 0;
}

/* 5.3 support access card: the duration and its button on one line, with room
   between them (user 2026-10-08: "button is crowding the dropdown"). */
.fko-app [data-fko-support-access] .fko-field-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.fko-app [data-fko-support-access] .fko-inline-form { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.fko-app [data-fko-support-access] select { width: auto; min-width: 140px; margin: 0; }
