/* Priority Gateway Studio — the design system.
   ============================================================================

   ONE stylesheet is the source of truth for the whole Studio: the plain-DOM
   shell, the Builder island and the Make-import island all read the tokens
   declared here, and neither island redeclares a colour, a radius or a shadow
   of its own. A new screen composes the classes below; it does not invent CSS.

   Hebrew-first and genuinely RTL: dir="rtl" on <html>, logical properties
   everywhere, and no left/right in the layout. Technical text - JSON, a
   Priority column, a URL - is LTR inside its own isolated container, which is
   what .ltr and .mono are for.

   No framework, no build step for this file.

   Layers, in the order they appear:
     1  tokens            colour, type, space, radius, shadow, motion
     2  base              resets, headings, the two direction primitives
     3  shell             rail, page frame, page header
     4  surfaces          card, section, panel, banner
     5  controls          button, input, select, textarea, checkbox, switch
     6  status            tag, msg, badge, progress
     7  data              table, list, key-value, technical viewer, files
     8  overlays          dialog, drawer, tooltip
     9  states            empty, loading, error
    10  feature surfaces  action bank, builder, runner, matrix
   ========================================================================= */

/* ------------------------------------------------------------- 1. tokens */

:root {
  /* Surface. Three levels only: the canvas behind everything, the panel that
     sits on it, and the quiet inset inside a panel. A fourth level is how a
     screen starts looking like a different product. */
  --bg: #f5f7fa;
  --panel: #ffffff;
  --panel-2: #f8fafc;
  --panel-3: #eef2f7;

  /* Line. Two weights: the hairline that separates, and the one that outlines
     something you can type into. */
  --line: #e3e8ef;
  --line-strong: #cbd4e1;

  /* Ink. Four steps, and never a fifth: a heading, body text, a label, and the
     thing you are allowed to overlook. */
  --text: #101828;
  --text-2: #344054;
  --muted: #667085;
  --faint: #98a2b3;

  /* Accent. One blue for the whole product, with a hover, a pressed and a
     wash. PrilinQ PM's blue, not a second blue beside it. */
  --accent: #2f6fed;
  --accent-hover: #2560d8;
  --accent-press: #1c4fbb;
  --accent-soft: #eaf1fe;
  --accent-line: #b9d0fb;
  --on-accent: #ffffff;

  /* Status. Every one of them is a triplet - ink, wash, line - so a badge, a
     banner and a row highlight can be built from the same three values. */
  --ok: #087443;
  --ok-soft: #e7f6ee;
  --ok-line: #a7dfc3;
  --warn: #9a5b06;
  --warn-soft: #fdf3e3;
  --warn-line: #f0cf9a;
  --err: #b42318;
  --err-soft: #fdeceb;
  --err-line: #f3b9b4;
  --info: var(--accent);
  --info-soft: var(--accent-soft);
  --info-line: var(--accent-line);
  --skip: #667085;
  --skip-soft: #eef1f5;
  --skip-line: #d7dde6;

  /* Type. A scale, not a pile of pixel values. Sizes are named for the job
     they do, so a screen asks for a "section title" and gets the same one
     everywhere. */
  --font-sans: "Segoe UI", "Segoe UI Hebrew", Rubik, Assistant, system-ui,
    -apple-system, Arial, sans-serif;
  --font-mono: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
  --fs-page: 22px;      /* the one h1 on a screen */
  --fs-section: 16px;   /* a card or section heading */
  --fs-sub: 14.5px;     /* a sub-heading inside a section */
  --fs-body: 14.5px;
  --fs-label: 13px;
  --fs-small: 12.5px;
  --fs-micro: 11.5px;   /* a badge, a count */
  --fs-code: 12.5px;
  --lh-tight: 1.25;
  --lh-heading: 1.35;
  --lh-body: 1.6;
  --fw-normal: 400;
  --fw-medium: 500;
  --fw-semi: 600;
  --fw-bold: 700;

  /* Space. A 4px grid. Section rhythm is its own token because the distance
     between two sections is a decision, not an accident. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-8: 32px;
  --sp-10: 40px;
  --sp-section: 20px;
  --sp-page: 24px;

  /* Shape. */
  --radius: 12px;
  --radius-sm: 8px;
  --radius-xs: 6px;
  --radius-pill: 999px;

  /* Depth. Two shadows: something resting on the canvas, and something
     floating above the page. */
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, .05);
  --shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 6px 16px -6px rgba(16, 24, 40, .1);
  --shadow-lg: 0 12px 40px -8px rgba(16, 24, 40, .22);

  /* Controls. A button and an input that stand side by side must be the same
     height, in every size, on every screen. */
  --h-control: 36px;
  --h-control-sm: 30px;
  --h-control-lg: 42px;
  --ic-sm: 16px;
  --ic: 18px;
  --ic-lg: 22px;
  --ic-xl: 28px;

  /* Focus. One ring, used by everything the keyboard can reach. */
  --focus: 0 0 0 3px rgba(47, 111, 237, .28);
  --focus-err: 0 0 0 3px rgba(180, 35, 24, .25);

  --motion: 140ms cubic-bezier(.2, .6, .3, 1);
  --rail: 236px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0d1219;
    --panel: #161d27;
    --panel-2: #1c2431;
    --panel-3: #222b3a;
    --line: #28313f;
    --line-strong: #3a4658;
    --text: #e8edf5;
    --text-2: #c3ccd9;
    --muted: #94a1b3;
    --faint: #6f7c8d;
    --accent: #6fa2ff;
    --accent-hover: #85b1ff;
    --accent-press: #a0c2ff;
    --accent-soft: #17243a;
    --accent-line: #2c456e;
    --on-accent: #0d1219;
    --ok: #52cd92;
    --ok-soft: #10291d;
    --ok-line: #205a3e;
    --warn: #e3a44d;
    --warn-soft: #2c2210;
    --warn-line: #5c471b;
    --err: #ff8f87;
    --err-soft: #331b1c;
    --err-line: #6a3230;
    --skip: #8b97a8;
    --skip-soft: #1f2733;
    --skip-line: #333d4c;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .35);
    --shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 6px 18px -6px rgba(0, 0, 0, .5);
    --shadow-lg: 0 16px 46px -10px rgba(0, 0, 0, .65);
    --focus: 0 0 0 3px rgba(111, 162, 255, .32);
  }
}

/* --------------------------------------------------------------- 2. base */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0; font-weight: var(--fw-bold); line-height: var(--lh-heading); }
h1 { font-size: var(--fs-page); letter-spacing: -.01em; }
h2 { font-size: var(--fs-section); }
h3 { font-size: var(--fs-sub); }
h4 { font-size: var(--fs-label); color: var(--muted); font-weight: var(--fw-semi); }
p { margin: 0; }
a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent-hover); }

/* THE TWO DIRECTION PRIMITIVES.
   Everything that is not Hebrew prose - a URL, a JSON document, a Priority
   column name, an identifier - is left-to-right inside its own isolate, so it
   reads correctly without flipping the sentence around it. */
.ltr { direction: ltr; text-align: left; unicode-bidi: isolate; }
.mono {
  font-family: var(--font-mono); font-size: var(--fs-code);
  direction: ltr; text-align: left; unicode-bidi: isolate;
  /* A URL and an identifier have no spaces to break at, so without this one
     value decides the width of everything around it - #117, bug 1. `anywhere`
     rather than `break-word` because only `anywhere` also shrinks the
     element's intrinsic minimum width, which is the number the grid reads. */
  overflow-wrap: anywhere;
}
bdi { unicode-bidi: isolate; }

/* THE ICON. One system, one shape, one size scale.
   Every picture in the Studio is a Material Symbol from web/icons.js drawn at
   one of four sizes. It inherits the colour of the text it sits in, which is
   what keeps a badge's icon and its label the same colour without a rule per
   badge. */
.ic-svg {
  width: var(--ic); height: var(--ic); flex: none;
  display: inline-block; vertical-align: -.22em;
}
.ic-svg.sm { width: var(--ic-sm); height: var(--ic-sm); }
.ic-svg.lg { width: var(--ic-lg); height: var(--ic-lg); }
.ic-svg.xl { width: var(--ic-xl); height: var(--ic-xl); }
/* A directional icon that must follow the reading direction rather than the
   page's physical left and right - a "next" chevron, a "back" arrow. */
[dir="rtl"] .ic-svg.flip-rtl { transform: scaleX(-1); }

/* -------------------------------------------------------------- 3. shell */

#shell { display: grid; grid-template-columns: var(--rail) 1fr; min-height: 100vh; }
@media (max-width: 900px) { #shell { grid-template-columns: 1fr; } }

#rail {
  background: var(--panel);
  border-inline-end: 1px solid var(--line);
  padding: var(--sp-4) var(--sp-3) var(--sp-3);
  display: flex; flex-direction: column; gap: 2px;
  position: sticky; top: 0; align-self: start; max-height: 100vh; overflow-y: auto;
}
@media (max-width: 900px) {
  #rail { position: static; max-height: none; flex-direction: row; flex-wrap: wrap; }
}
#rail .brand {
  padding: var(--sp-2) var(--sp-3) var(--sp-5);
  display: flex; align-items: center; gap: var(--sp-3);
}
#rail .brand .mark {
  width: 34px; height: 34px; border-radius: var(--radius-sm); flex: none;
  background: var(--accent); color: var(--on-accent);
  display: inline-flex; align-items: center; justify-content: center;
}
#rail .brand .mark .ic-svg { width: 21px; height: 21px; }
#rail .brand b { display: block; font-size: 15px; line-height: 1.2; }
#rail .brand span { font-size: var(--fs-micro); color: var(--faint); }

/* A rail section title. The navigation is grouped by subject, and the groups
   need a name or they are just gaps. */
#rail .navgroup {
  padding: var(--sp-4) var(--sp-3) var(--sp-1);
  font-size: var(--fs-micro); font-weight: var(--fw-bold);
  letter-spacing: .04em; color: var(--faint); text-transform: none;
}
#rail .navgroup:first-of-type { padding-top: 0; }
#rail a.nav {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 9px var(--sp-3); border-radius: var(--radius-sm);
  color: var(--text-2); text-decoration: none;
  font-weight: var(--fw-semi); font-size: var(--fs-body);
  transition: background var(--motion), color var(--motion);
  position: relative;
}
#rail a.nav:hover { background: var(--panel-3); color: var(--text); }
#rail a.nav:focus-visible { outline: none; box-shadow: var(--focus); }
#rail a.nav.on { background: var(--accent-soft); color: var(--accent); }
/* Active state is never colour alone: a bar on the reading edge says it too. */
#rail a.nav.on::before {
  content: ""; position: absolute; inset-inline-start: 0; inset-block: 7px;
  width: 3px; border-radius: var(--radius-pill); background: var(--accent);
}
#rail a.nav .ic { display: inline-flex; width: 20px; justify-content: center; color: inherit; }
#rail a.nav .ic .ic-svg { width: 20px; height: 20px; }
#rail .spacer { flex: 1; min-height: var(--sp-4); }
#rail .who {
  padding: var(--sp-3); border-top: 1px solid var(--line);
  font-size: var(--fs-small); color: var(--muted);
}

/* The shell's reading column, addressed by its id and not by its tag.
   The Builder island renders <main> elements of its own inside this one - the
   list surface and the canvas - and a bare `main` selector reached them too,
   handing the island the shell's padding, its max-width and (in full-bleed)
   height:100vh with overflow:hidden. The shell styles the screen it owns; the
   island styles itself. */
#view { padding: var(--sp-6) var(--sp-8) var(--sp-10); max-width: 1440px; }
@media (max-width: 900px) { #view { padding: var(--sp-4) var(--sp-4) var(--sp-8); } }

/* THE PAGE HEADER. Identical on every screen: a title, one line saying what
   the screen is for, and the actions on the far side. */
.pagehead {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap;
  margin-bottom: var(--sp-6); padding-bottom: var(--sp-4);
  border-bottom: 1px solid var(--line);
}
.pagehead .titlewrap { display: flex; align-items: flex-start; gap: var(--sp-3); min-width: 0; }
/* Deliberately NOT scoped to .pagehead: the sign-in card wears the same mark,
   and it has no page header to hang it on. */
.pageicon {
  width: 38px; height: 38px; border-radius: var(--radius-sm); flex: none;
  background: var(--accent-soft); color: var(--accent);
  display: inline-flex; align-items: center; justify-content: center;
}
.pagehead p { margin: 3px 0 0; color: var(--muted); font-size: var(--fs-body); max-width: 78ch; }
.pagehead .actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; align-items: center; }

/* A row of tools under the header: search, filters, a sort. Same on every
   list screen, so the eye finds the search box in the same place. */
.toolbar {
  display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}
.toolbar .grow { flex: 1; min-width: 220px; }

/* ----------------------------------------------------------- 4. surfaces */

.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: var(--sp-4); box-shadow: var(--shadow-sm);
  /* Nothing leaves a card - #117, bug 1. It only takes effect on a word that
     genuinely cannot fit, so ordinary Hebrew prose still breaks between words. */
  overflow-wrap: anywhere;
}
.card > h2, .card > h3 { margin-bottom: var(--sp-3); }

/* A SECTION. A card with a titled head, which is the shape most of the Studio
   actually needs: a name, an optional action beside it, and a body. */
.section {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-sm);
  margin-bottom: var(--sp-section); overflow: hidden;
}
.section > .sec-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line);
  background: var(--panel-2);
}
.section > .sec-head .sec-title {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-section); font-weight: var(--fw-bold);
}
.section > .sec-head .sec-title .ic-svg { color: var(--muted); }
.section > .sec-head .sec-sub { font-size: var(--fs-small); color: var(--muted); font-weight: var(--fw-normal); }
.section > .sec-body { padding: var(--sp-4); }
.section > .sec-body > :last-child { margin-bottom: 0; }

.grid { display: grid; gap: var(--sp-4); }
.cards { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.fields { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.row { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.spread { display: flex; gap: var(--sp-2); align-items: center; justify-content: space-between; flex-wrap: wrap; }
.stack { display: grid; gap: var(--sp-3); }
.wide { grid-column: 1 / -1; }

/* A LONG VALUE MUST NARROW THE TEXT, NEVER WIDEN THE CARD - #117, bug 1.

   A grid item and a flex item are both born with min-width:auto, which reads
   "never narrower than my longest unbreakable word". A project address, an
   X-Client-ID or a long company name is exactly such a word: the card grew
   past its column, the column past the grid, and the neighbouring card ended
   up underneath somebody else's text.

   The floor is removed here, once, on the containers the whole Studio composes
   from - so a screen gets the behaviour from using .cards and .stack, and not
   from remembering a rule. Buttons are deliberately NOT included: .row keeps
   its floor so a toolbar wraps instead of squashing its controls. */
.cards > *, .grid > *, .stack > *, .spread > * { min-width: 0; }
.card { min-width: 0; }

/* A banner across the top of a screen. Never more than one at a time. */
.banner {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 10px var(--sp-4); font-size: var(--fs-body); font-weight: var(--fw-semi);
  background: var(--warn-soft); color: var(--warn);
  border: 1px solid var(--warn-line); border-radius: var(--radius-sm);
  margin-bottom: var(--sp-4);
}
.banner.prod { background: var(--err-soft); color: var(--err); border-color: var(--err-line); }

/* Side panel: action configuration, step editing. A drawer on the reading
   edge, with a fixed head and foot so its actions never scroll away. */
.panel {
  position: fixed; inset-block: 0; inset-inline-end: 0; width: min(600px, 100vw);
  background: var(--panel); border-inline-start: 1px solid var(--line);
  box-shadow: var(--shadow-lg); z-index: 40;
  display: flex; flex-direction: column;
}
.panel .phead {
  padding: var(--sp-4); border-bottom: 1px solid var(--line);
  background: var(--panel-2);
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3);
}
.panel .pbody { padding: var(--sp-4); overflow-y: auto; flex: 1; }
.panel .pfoot {
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--line);
  display: flex; gap: var(--sp-2); background: var(--panel-2);
}

/* ----------------------------------------------------------- 5. controls */

label {
  display: block; font-size: var(--fs-label); color: var(--text-2);
  margin-bottom: 5px; font-weight: var(--fw-semi);
}
label .req { color: var(--err); }
/* A parameter's technical name, beside its business name rather than instead
   of it. Both are needed: one for a product manager, one for a Priority person
   matching the field against a form. */
label .param-name {
  font-weight: var(--fw-normal); direction: ltr; display: inline;
  color: var(--faint); font-family: var(--font-mono); font-size: var(--fs-micro);
}

input, select, textarea {
  width: 100%; min-height: var(--h-control); padding: 7px 11px;
  font: inherit; font-size: var(--fs-body); color: var(--text);
  background: var(--panel); border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  transition: border-color var(--motion), box-shadow var(--motion);
}
input::placeholder, textarea::placeholder { color: var(--faint); }
input:hover:not(:disabled), select:hover:not(:disabled), textarea:hover:not(:disabled) {
  border-color: var(--muted);
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: var(--focus);
}
input:disabled, select:disabled, textarea:disabled,
input[readonly], textarea[readonly] {
  background: var(--panel-3); color: var(--muted); cursor: not-allowed;
}
select {
  appearance: none;
  /* The caret is drawn, not a glyph, so it is the same in both directions and
     in both themes. padding keeps the text off it. */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 16px) calc(50% + 2px), calc(100% - 11px) calc(50% + 2px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-inline-end: 30px;
}
:where([dir="rtl"]) select {
  /* 11px first, 16px second: the two halves of the chevron are positional, so
     mirroring the offsets without swapping them draws a cross rather than a v. */
  background-position: 11px calc(50% + 2px), 16px calc(50% + 2px);
  padding-inline-end: 11px; padding-inline-start: 30px;
}
textarea {
  min-height: 96px; resize: vertical;
  font-family: var(--font-mono); font-size: var(--fs-code);
  direction: ltr; text-align: left;
}
input[type=checkbox], input[type=radio] {
  width: 16px; height: 16px; min-height: 0; padding: 0; accent-color: var(--accent);
  vertical-align: -2px; cursor: pointer;
}
input[type=checkbox]:focus-visible, input[type=radio]:focus-visible {
  outline: none; box-shadow: var(--focus);
}
input[type=search] { -webkit-appearance: none; }
.hint { font-size: var(--fs-small); color: var(--muted); margin-top: 4px; line-height: 1.5; overflow-wrap: anywhere; }

/* A field that failed validation. Presentation only - the rule that decided
   it lives in the view, unchanged. */
.input-row.bad > label { color: var(--err); }
.input-row.bad input,
.input-row.bad select,
.input-row.bad textarea { border-color: var(--err); }
.input-row.bad input:focus,
.input-row.bad select:focus,
.input-row.bad textarea:focus { box-shadow: var(--focus-err); }
.input-row.bad .json-ed { border-color: var(--err); }

/* THE BUTTON.
   Four variants and three sizes, and nothing else anywhere in the Studio:
     (default)  primary   the one action a screen is for
     .ghost     secondary an action of equal standing, quieter
     .quiet     tertiary  everything else
     .danger    destructive, on its own or as .danger.quiet
   Every one of them is the same height as an input beside it. */
button, .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: var(--h-control); padding: 0 var(--sp-4);
  font: inherit; font-size: var(--fs-body); font-weight: var(--fw-semi);
  line-height: 1; cursor: pointer; white-space: nowrap;
  border-radius: var(--radius-sm);
  border: 1px solid var(--accent); background: var(--accent); color: var(--on-accent);
  transition: background var(--motion), border-color var(--motion),
              color var(--motion), box-shadow var(--motion);
}
button:hover:not(:disabled), .btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
button:active:not(:disabled) { background: var(--accent-press); border-color: var(--accent-press); }
button:focus-visible, .btn:focus-visible, a:focus-visible, summary:focus-visible {
  outline: none; box-shadow: var(--focus);
}
button .ic-svg { width: var(--ic-sm); height: var(--ic-sm); }

button.ghost {
  background: var(--panel); color: var(--accent); border-color: var(--accent-line);
}
button.ghost:hover:not(:disabled) { background: var(--accent-soft); border-color: var(--accent); }

button.quiet {
  background: var(--panel); color: var(--text-2); border-color: var(--line-strong);
}
button.quiet:hover:not(:disabled) { background: var(--panel-3); border-color: var(--muted); color: var(--text); }
/* A quiet button that is currently ON - a view toggle, a filter. */
button.quiet.on, button.quiet.small.on {
  background: var(--accent-soft); color: var(--accent); border-color: var(--accent);
}

button.danger { background: var(--err); border-color: var(--err); color: #fff; }
button.danger:hover:not(:disabled) { filter: brightness(1.08); }
/* A destructive action that is not the primary one: still unmistakably red,
   still not competing for the eye with the buttons beside it. */
button.danger.quiet { background: var(--panel); color: var(--err); border-color: var(--err-line); }
button.danger.quiet:hover:not(:disabled) { background: var(--err-soft); border-color: var(--err); }

/* AN ICON-ONLY DESTRUCTIVE BUTTON - the delete on a process card, the discard
   on an unfinished import.
   Stated here rather than left to the cascade. `button.danger.quiet` does
   outrank `button.icon` by one point of specificity, so the red survives today
   by arithmetic nobody wrote down - and the day somebody reorders these rules
   or scopes .icon a little tighter, a delete button turns grey in silence.
   test/ui-actions.js §G reads the computed colour back and fails if it does. */
button.danger.icon { color: var(--err); border-color: var(--err-line); }
button.danger.icon:hover:not(:disabled) {
  background: var(--err-soft); color: var(--err); border-color: var(--err);
}

button.small, .btn.small { min-height: var(--h-control-sm); padding: 0 var(--sp-3); font-size: var(--fs-label); }
button.large { min-height: var(--h-control-lg); padding: 0 var(--sp-5); }
button:disabled, .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }

/* An icon-only button. It carries no text, so it always carries a title or an
   aria-label - see the icon-only rule in test/ui-canvas.js. */
button.icon {
  min-height: var(--h-control-sm); width: var(--h-control-sm); padding: 0;
  background: transparent; border-color: transparent; color: var(--muted);
}
button.icon:hover:not(:disabled) { background: var(--panel-3); color: var(--text); border-color: transparent; }
button.icon .ic-svg { width: var(--ic); height: var(--ic); }
/* An icon button that carries text as well is not icon-only: let it breathe. */
button.icon.with-text { width: auto; padding: 0 var(--sp-2); }

/* A message that names a field and moves the cursor there. */
button.link {
  background: none; border: 0; padding: 0; min-height: 0; color: var(--accent);
  font: inherit; text-align: start; cursor: pointer; text-decoration: underline;
  display: inline; white-space: normal;
}
button.link:hover { background: none; color: var(--accent-hover); }

/* A TABLE ROW THAT IS WRONG SAYS SO ON ITSELF - #117, bug 5 §4.
   The message used to be one line at the bottom of a dialog that scrolls, so
   pressing save from the top read as a button that does nothing. The row is
   tinted, marked on its reading edge, and carries the sentence beside the very
   field it is about - never colour alone. */
tr.is-bad > td { background: var(--err-soft); }
tr.is-bad > td:first-child { box-shadow: inset 3px 0 0 var(--err); }
[dir="rtl"] tr.is-bad > td:first-child { box-shadow: inset -3px 0 0 var(--err); }
.row-problem { color: var(--err); font-weight: var(--fw-semi); }
.row-problem:empty { display: none; }

/* TABS: several management modes on one screen - #117, bug 4.
   A row of buttons that reads as a strip, and one panel below it. The active
   tab is marked by weight, colour AND an underline, so the current mode is
   never carried by colour alone. */
.tabwrap { margin-bottom: var(--sp-5); }
.tabs {
  display: flex; gap: var(--sp-1); flex-wrap: wrap;
  border-bottom: 1px solid var(--line); margin-bottom: var(--sp-4);
}
.tab {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--h-control); padding: 0 var(--sp-4);
  background: none; border: none; border-radius: 0;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  color: var(--muted); font-size: var(--fs-body); font-weight: var(--fw-semi);
  cursor: pointer;
  transition: color var(--motion), border-color var(--motion);
}
.tab:hover { color: var(--text); background: none; }
.tab:focus-visible { outline: none; box-shadow: var(--focus); }
.tab.on { color: var(--accent); border-bottom-color: var(--accent); }
.tab .ic-svg { color: currentColor; }

/* Chips: a filter you can switch on. */
.chips { display: flex; gap: 6px; flex-wrap: wrap; min-width: 0; }
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: var(--h-control-sm); padding: 0 var(--sp-3);
  border-radius: var(--radius-pill); font-size: var(--fs-label); font-weight: var(--fw-semi);
  border: 1px solid var(--line-strong); background: var(--panel);
  color: var(--text-2); cursor: pointer;
  transition: background var(--motion), color var(--motion), border-color var(--motion);
}
.chip:hover { background: var(--panel-3); }
.chip:focus-visible { outline: none; box-shadow: var(--focus); }
.chip.on { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.chip .ic-svg { width: var(--ic-sm); height: var(--ic-sm); }

/* The password field and its eye.
   The wrapper is LTR-isolated exactly like the input inside it, so
   inset-inline-end is the physical END of the typed string in either page
   direction - the eye never lands on the first character. padding-inline-end
   on the input is what keeps a long password from running underneath it. */
.pw { position: relative; direction: ltr; }
.pw input { padding-inline-end: 44px; }
.pw-eye {
  position: absolute; inset-inline-end: 4px; inset-block: 4px;
  width: 34px; min-height: 0; padding: 0;      /* a finger-sized target on a tablet */
  display: flex; align-items: center; justify-content: center;
  background: transparent; border-color: transparent; color: var(--faint);
}
.pw-eye:hover:not(:disabled) { background: transparent; border-color: transparent; color: var(--accent); }
.pw-eye[aria-pressed="true"] { color: var(--accent); }
.pw-eye .ic-svg { width: 20px; height: 20px; display: block; }
/* Which of the two glyphs is showing follows the button's own state, so
   app.js never touches the markup. */
.pw-eye .eye-off { display: none; }
.pw-eye[aria-pressed="true"] .eye-on { display: none; }
.pw-eye[aria-pressed="true"] .eye-off { display: block; }

/* ------------------------------------------------------------- 6. status */

/* THE BADGE. One shape for every status in the Studio, and never colour
   alone: an icon and a word carry the meaning, the colour only reinforces it.
   The five kinds map to the five meanings the product actually has. */
.tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-micro); font-weight: var(--fw-bold); line-height: 1.7;
  padding: 1px 9px; border-radius: var(--radius-pill);
  border: 1px solid var(--line-strong); color: var(--text-2); background: var(--panel-2);
  white-space: nowrap;
  /* A badge never widens what holds it - #117, bug 1. The badge keeps its
     single line; the LABEL inside it is what gives way, with an ellipsis, and
     the full text stays reachable through the badge's own title. */
  max-width: 100%; min-width: 0;
}
.tag .tag-text { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.tag .ic-svg { width: 14px; height: 14px; }
.tag.ok { color: var(--ok); background: var(--ok-soft); border-color: var(--ok-line); }
.tag.warn { color: var(--warn); background: var(--warn-soft); border-color: var(--warn-line); }
.tag.err { color: var(--err); background: var(--err-soft); border-color: var(--err-line); }
.tag.info { color: var(--accent); background: var(--accent-soft); border-color: var(--accent-line); }
.tag.skip { color: var(--skip); background: var(--skip-soft); border-color: var(--skip-line); }

/* A MESSAGE BOX. Same five kinds, at paragraph scale.
 *
 * A GRID AND NOT A FLEX ROW, AND THAT IS THE WHOLE POINT.
 *
 * `msg()` returns the box, and three callers have always gone on to append a
 * <ul> of details to it - the field list that blocks a run, the validator's
 * errors, and the server details in withBusy(). As a flex row those lists
 * became a THIRD COLUMN beside the heading, which squeezed the sentence they
 * belong to down to zero width.
 *
 * Two columns, and everything that is not the icon is pinned to the second one:
 * the heading takes row 1, anything appended afterwards falls to row 2 of the
 * same column - underneath the text and aligned with it, which is where it
 * always was. No caller has to know, and the next one cannot get it wrong. */
.msg {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  align-items: start; gap: 0 var(--sp-2);
  padding: 10px var(--sp-3); border-radius: var(--radius-sm);
  font-size: var(--fs-body); margin-bottom: var(--sp-3);
  border: 1px solid transparent;
}
.msg > .ic-svg { grid-column: 1; margin-top: 2px; }
.msg > :not(.ic-svg) { grid-column: 2; min-width: 0; }
.msg.err { background: var(--err-soft); color: var(--err); border-color: var(--err-line); }
.msg.ok { background: var(--ok-soft); color: var(--ok); border-color: var(--ok-line); }
.msg.warn { background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line); }
.msg.info { background: var(--accent-soft); color: var(--accent); border-color: var(--accent-line); }
.msg ul { margin: 6px 0 0; padding-inline-start: 20px; }

/* Progress is a COUNT of steps, never a guess at time. */
.progress { height: 6px; background: var(--panel-3); border-radius: var(--radius-pill); overflow: hidden; }
.progress > i { display: block; height: 100%; background: var(--accent); transition: width .25s ease; }

.spin {
  display: inline-block; width: 14px; height: 14px;
  border: 2px solid var(--accent); border-top-color: transparent;
  border-radius: 50%; animation: sp .7s linear infinite;
}
@keyframes sp { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spin { animation-duration: 2s; }
  * { transition-duration: 1ms !important; }
}

/* --------------------------------------------------------------- 7. data */

.scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius-sm); }
table { border-collapse: collapse; width: 100%; font-size: var(--fs-label); }
th, td {
  text-align: start; padding: 9px var(--sp-3);
  border-bottom: 1px solid var(--line); white-space: nowrap;
}
th {
  background: var(--panel-2); font-weight: var(--fw-bold); font-size: var(--fs-small);
  color: var(--muted); position: sticky; top: 0; z-index: 1;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--panel-2); }
td.null { color: var(--faint); }

.kv { display: grid; grid-template-columns: auto 1fr; gap: 6px var(--sp-4); font-size: var(--fs-label); }
.kv dt { color: var(--muted); font-weight: var(--fw-semi); }
.kv dd { margin: 0; }

/* THE TECHNICAL VIEWER.
   One pattern for JSON, a raw response, a request, a log line, a schema and a
   long payload - #109 §10. The rule it enforces: the business summary comes
   first and this comes second, folded, and it never takes the screen over on
   its own. */
pre {
  margin: 0; padding: var(--sp-3);
  font-family: var(--font-mono); font-size: var(--fs-code); line-height: 1.55;
  direction: ltr; text-align: left; white-space: pre-wrap; word-break: break-word;
  background: var(--panel-2); border-radius: var(--radius-sm);
  max-height: 340px; overflow: auto;
}

.json-view { border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; }
.json-view-bar {
  display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3);
  padding: 6px var(--sp-2); background: var(--panel-2); border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.json-view-bar .vt {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-small); font-weight: var(--fw-semi); color: var(--muted);
}
.json-view > pre { border-radius: 0; max-height: 420px; background: var(--panel); }

/* A JSON document, edited as a document.
   No editor library - a textarea, a synchronised gutter, and four buttons. */
.json-ed {
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--panel-2); overflow: hidden;
}
.json-ed.bad { border-color: var(--err); }
.json-ed-bar {
  display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3);
  padding: 6px var(--sp-2); border-bottom: 1px solid var(--line); background: var(--panel-2);
  flex-wrap: wrap;
}
.json-ed-tools { display: flex; gap: 6px; }
.json-ed-state { font-size: var(--fs-small); display: inline-flex; align-items: center; gap: 5px; }
.json-ed-state .ok-note { color: var(--ok); display: inline-flex; align-items: center; gap: 5px; }
.json-ed-state .err-note { color: var(--err); font-weight: var(--fw-semi); display: inline-flex; align-items: center; gap: 5px; }

/* The gutter and the textarea must share a line box exactly, or the numbers
   drift away from the lines they count. Same font, same size, same height. */
.json-ed-body { display: flex; align-items: stretch; }
.json-ed-lines {
  flex: 0 0 auto; min-width: 40px; padding: 10px 6px; overflow: hidden;
  background: var(--panel-2); border-inline-end: 1px solid var(--line);
  color: var(--faint); font-size: var(--fs-code); line-height: 1.5;
  font-family: var(--font-mono);
  display: flex; flex-direction: column; text-align: end; user-select: none;
}
.json-ed-area {
  flex: 1; min-width: 0; border: 0; outline: none; resize: none;
  padding: 10px var(--sp-3); background: var(--panel); color: var(--text);
  font-family: var(--font-mono); font-size: var(--fs-code); line-height: 1.5;
  min-height: 150px; white-space: pre; overflow: auto; border-radius: 0;
}
.json-ed-area:focus { box-shadow: none; }

/* Full screen. A large document is not editable in a 520px box, and scrolling
   a panel to find the closing brace is not editing. Escape leaves. */
.json-ed.json-ed-full {
  position: fixed; inset: var(--sp-6); z-index: 60; display: flex; flex-direction: column;
  box-shadow: var(--shadow-lg); background: var(--panel);
}
.json-ed.json-ed-full .json-ed-body { flex: 1; min-height: 0; }
.json-ed.json-ed-full .json-ed-area { height: 100%; }
.json-ed.json-ed-full .json-ed-lines { height: 100%; overflow-y: auto; }

/* A FILE. Name, kind, size, and whatever actions already exist for it - one
   presentation everywhere a file is shown or picked (#109 §11). */
.file-chip {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: 6px var(--sp-3); border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--panel-2); max-width: 100%;
}
.file-chip .ic-svg { color: var(--muted); }
.file-chip .fname { font-weight: var(--fw-semi); font-size: var(--fs-label); overflow: hidden; text-overflow: ellipsis; }
.file-chip .fmeta { font-size: var(--fs-micro); color: var(--faint); }

/* AN ACCORDION. `details.sect` is the one collapsible in the Studio: a titled
   strip that opens. The chevron is an icon like every other, drawn by the
   view, so nothing here injects a glyph through content. */
details.sect {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  margin-bottom: var(--sp-3); background: var(--panel); overflow: hidden;
}
details.sect > summary {
  padding: 10px var(--sp-3); cursor: pointer;
  font-weight: var(--fw-semi); font-size: var(--fs-body); color: var(--text);
  list-style: none; display: flex; justify-content: space-between; align-items: center;
  gap: var(--sp-2); background: var(--panel-2);
}
details.sect > summary:hover { background: var(--panel-3); }
details.sect > summary::-webkit-details-marker { display: none; }
/* The affordance, drawn rather than lettered, so it is identical in both
   directions and needs no glyph. */
details.sect > summary::after {
  content: ""; flex: none; width: 8px; height: 8px; margin-inline-start: auto;
  border-inline-end: 2px solid var(--faint); border-block-end: 2px solid var(--faint);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--motion);
}
details.sect[open] > summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
details.sect > .body { padding: var(--sp-3); border-top: 1px solid var(--line); }

/* Values that normally come from the client's configuration. Collapsed, so a
   form of three real questions does not look like a form of thirteen. */
details.advanced-inputs > summary { font-weight: var(--fw-semi); color: var(--muted); }

/* ----------------------------------------------------------- 8. overlays */

dialog {
  border: 1px solid var(--line); border-radius: var(--radius); padding: 0;
  background: var(--panel); color: var(--text);
  max-width: min(680px, 94vw); width: 100%;
  max-height: 92vh;
  box-shadow: var(--shadow-lg);
}
/* A run drawn as a diagram needs the room. Opt-in, one screen at a time: every
   other dialog in the Studio is a form, and a form 1180px wide is a worse form. */
dialog.dlg-wide { max-width: min(1180px, 96vw); }
dialog.dlg-wide .dbody { max-height: 80vh; }
dialog::backdrop { background: rgba(16, 24, 40, .55); backdrop-filter: blur(2px); }
/* The head and the foot are sticky, so the actions of a long dialog never
   scroll out of reach - #109 §8. */
dialog .dhead {
  display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line);
  background: var(--panel); position: sticky; top: 0; z-index: 2;
}
dialog .dhead strong { font-size: var(--fs-section); }
dialog .dbody { padding: var(--sp-4); max-height: 70vh; overflow: auto; }
dialog .dfoot {
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--line);
  display: flex; gap: var(--sp-2); justify-content: flex-start;
  background: var(--panel-2); position: sticky; bottom: 0;
}

/* ------------------------------------------- the business summary of a step
 *
 * #108. The first block of a step's panel and of the process's own output: what
 * happened, in words, with no JSON. Everything below it - the input, the
 * request, the answer, the technical facts - kept its markup and moved behind a
 * fold, so this is the only thing that has to carry weight visually.
 *
 * A card, not a table: the eye should land on the status and the one identifier
 * that says WHICH document this run was about, and only then walk the pairs. */
.run-summary-card {
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--panel);
  padding: 11px 13px;
  margin-bottom: 12px;
}
.run-summary-card.is-success { border-inline-start-color: var(--ok); }
.run-summary-card.is-error { border-inline-start-color: var(--err); }
.run-summary-card.is-skipped, .run-summary-card.is-partial { border-inline-start-color: var(--warn); }
.rsc-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rsc-meta { font-size: 12px; color: var(--muted); }
.rsc-code { font-size: var(--fs-micro); color: var(--err); }
.rsc-headline { margin: 9px 0 0; font-size: 13.5px; line-height: 1.5; }
/* Two columns, label then value, so a column of Hebrew names reads down the
   page and the values line up beside it instead of wrapping into it. */
.rsc-facts {
  display: grid; grid-template-columns: auto 1fr; gap: 3px 14px;
  margin: 10px 0 0; font-size: var(--fs-label);
}
.rsc-facts dt { color: var(--muted); font-weight: var(--fw-semi); }
.rsc-facts dd { margin: 0; word-break: break-word; }
/* The verdict of a question, in the same pill the diagram and the outline use. */
.step-detail .decision-verdict, .run-summary-card .decision-verdict {
  font-size: 11px; font-weight: var(--fw-bold); padding: 1px 7px; border-radius: var(--radius-pill);
}
.run-summary-card .decision-verdict.yes { background: var(--ok-soft); color: var(--ok); }
.run-summary-card .decision-verdict.no { background: var(--err-soft); color: var(--err); }

/* What the process returned. The same card language, one level up. */
.run-output { padding: 12px 14px; }
.run-output-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.run-output-head h4 { margin: 0; font-size: 14px; }
.run-output > .sect { margin-block-start: 12px; margin-bottom: 0; }

/* The write confirmation, and the client switches the run depends on. Boxed,
   because a checkbox that authorises a real change to a real Priority company
   should not look like one more field in the form above it. */
.write-gate {
  border: 1px solid var(--warn-line); background: var(--warn-soft);
  border-radius: var(--radius-sm);
  padding: var(--sp-3); margin: var(--sp-4) 0;
}
.write-gate label { font-weight: var(--fw-bold); color: var(--warn); }
.gate-switches { margin: 6px 0 0; padding: 0; list-style: none; font-size: var(--fs-small); }
.gate-switches li { margin-top: 3px; display: flex; align-items: center; gap: 5px; }
.gate-switches .sw-server { opacity: .85; }
.gate-switches .ic-svg { width: 14px; height: 14px; }

/* ------------------------------------------------------------- 9. states */

/* EMPTY, LOADING AND ERROR are the same shape: an icon, a sentence in plain
   Hebrew, an explanation, and the one action that resolves it. */
.empty {
  text-align: center; padding: var(--sp-10) var(--sp-5); color: var(--muted);
  display: flex; flex-direction: column; align-items: center; gap: 3px;
}
.empty .big {
  width: 52px; height: 52px; margin-bottom: var(--sp-3); border-radius: var(--radius);
  background: var(--panel-3); color: var(--faint);
  display: inline-flex; align-items: center; justify-content: center;
}
.empty .big .ic-svg { width: 26px; height: 26px; }
.empty .etitle { font-weight: var(--fw-bold); font-size: var(--fs-section); color: var(--text); }

.hidden { display: none !important; }

/* Used only to hold text long enough for document.execCommand('copy') when the
   clipboard API is unavailable. Never visible, never focusable by tabbing. */
.offscreen { position: fixed; inset-block-start: -1000px; inset-inline-start: -1000px; opacity: 0; }

/* --------------------------------------------- 10. feature surfaces
 *
 * Everything below is a SPECIALISATION of the system above - it reuses the
 * tokens and never introduces a colour, a radius or a font size of its own.
 */

/* ---------------------------------------------------- action bank cards */

.action-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-2);
  cursor: pointer; text-align: start;
  border-inline-start: 3px solid var(--line-strong);
  transition: border-color var(--motion), box-shadow var(--motion), transform var(--motion);
}
.action-card:hover { border-color: var(--accent-line); box-shadow: var(--shadow); }
.action-card:focus-visible { outline: none; box-shadow: var(--focus); }
.action-card.danger-write { border-inline-start-color: var(--warn); }
.action-card.danger-destructive { border-inline-start-color: var(--err); }
.action-card.danger-safe { border-inline-start-color: var(--ok); }
.action-card .title { font-weight: var(--fw-bold); font-size: var(--fs-sub); color: var(--text); }
.action-card .tech {
  font-size: var(--fs-micro); color: var(--faint);
  font-family: var(--font-mono); direction: ltr; text-align: start; unicode-bidi: isolate;
}
.action-card .desc { font-size: var(--fs-label); color: var(--muted); flex: 1; line-height: 1.5; }

/* The operation is the loudest thing on the card, because "does this change
   anything" is the question a person needs answered before any other. Colour
   AND icon, never colour alone. */
.action-card.op-create, .action-card.op-upsert { border-inline-start-color: var(--ok); }
.action-card.op-update { border-inline-start-color: var(--warn); }
.action-card.op-delete { border-inline-start-color: var(--err); }
.action-card.op-command { border-inline-start-color: var(--accent); }

.tag.platform-priority { color: var(--accent); background: var(--accent-soft); border-color: var(--accent-line); }
.tag.platform-prilinq { color: var(--ok); background: var(--ok-soft); border-color: var(--ok-line); }
.tag.platform-supabase_fn { color: var(--warn); background: var(--warn-soft); border-color: var(--warn-line); }
.tag.platform-gateway { color: var(--skip); background: var(--skip-soft); border-color: var(--skip-line); }

/* ------------------------------------------------- action bank drill-down
 *
 * Two levels of tiles rather than fifty-five cards. A tile is an icon, a name,
 * one sentence and a count - the count being the thing that makes the choice
 * for you, because "פריוריטי 41 / PrilinQ 7" tells a reader where the answer
 * probably is before they click anything.
 */

.drill-crumbs { display: flex; align-items: center; gap: 6px; font-size: var(--fs-label); flex-wrap: wrap; }
.drill-crumbs .crumb { padding: 0; }
.drill-crumbs .crumb.on { font-weight: var(--fw-bold); }
.drill-crumbs button.crumb {
  background: none; border: 0; color: var(--accent); cursor: pointer;
  font: inherit; padding: 0; min-height: 0; text-decoration: underline;
}
.drill-crumbs button.crumb:hover { background: none; color: var(--accent-hover); }
.drill-crumbs .crumb-sep { color: var(--faint); display: inline-flex; }
.drill-crumbs .crumb-sep .ic-svg { width: 14px; height: 14px; }
.drill-summary { font-size: var(--fs-label); color: var(--muted); }

.drill-levels { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); }
.drill-tile {
  display: flex; align-items: center; gap: var(--sp-3); text-align: start;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-3) var(--sp-4); cursor: pointer; font: inherit; color: inherit;
  min-height: 0;
  transition: border-color var(--motion), box-shadow var(--motion);
}
.drill-tile:hover { background: var(--panel); border-color: var(--accent-line); box-shadow: var(--shadow); }
.drill-glyph {
  width: 34px; height: 34px; border-radius: var(--radius-sm); flex: none;
  background: var(--panel-3); color: var(--muted);
  display: inline-flex; align-items: center; justify-content: center;
}
.drill-glyph .ic-svg { width: var(--ic-lg); height: var(--ic-lg); }
.drill-body { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.drill-title { font-weight: var(--fw-bold); font-size: var(--fs-body); }
.drill-hint { font-size: var(--fs-micro); color: var(--faint); line-height: 1.45; white-space: normal; }
.drill-count {
  flex: none; min-width: 30px; text-align: center;
  background: var(--accent-soft); color: var(--accent);
  border-radius: var(--radius-pill); padding: 2px 9px;
  font-size: var(--fs-small); font-weight: var(--fw-bold);
}

.drill-filters { display: flex; flex-direction: column; gap: var(--sp-2); }
.filter-row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.filter-label { font-size: var(--fs-small); color: var(--muted); flex: none; min-width: 72px; font-weight: var(--fw-semi); }

/* The action panel's opening paragraph: what it is, before what to type. */
.action-brief { margin-bottom: var(--sp-4); }
.action-brief p { margin: 0 0 6px; font-size: var(--fs-body); line-height: 1.55; }

/* Input and output in one table, told apart by more than position. */
.dir-in td:first-child { color: var(--accent); white-space: nowrap; }
.dir-out td:first-child { color: var(--ok); white-space: nowrap; }

/* ------------------------------------------------------------- builder */

.step-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-4); box-shadow: var(--shadow-sm);
}
.step-card .shead { display: flex; align-items: center; gap: var(--sp-3); }
.step-card .num {
  width: 26px; height: 26px; border-radius: 50%; flex: none;
  background: var(--accent-soft); color: var(--accent);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-small); font-weight: var(--fw-bold);
}
.step-card .stitle { font-weight: var(--fw-bold); flex: 1; }
.connector {
  display: flex; justify-content: center; align-items: center;
  color: var(--line-strong); padding: 2px 0;
}
.connector .ic-svg { width: var(--ic); height: var(--ic); }

/* Value-source colour code, used identically everywhere a mapping appears. */
.src {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-small); padding: 2px 8px; border-radius: var(--radius-xs);
  font-weight: var(--fw-semi);
}
.src .ic-svg { width: 14px; height: 14px; }
.src-const { background: var(--skip-soft); color: var(--skip); }
.src-input { background: var(--accent-soft); color: var(--accent); }
.src-step { background: var(--ok-soft); color: var(--ok); }
.src-template { background: #f1e9fc; color: #6b3fa0; }
@media (prefers-color-scheme: dark) { .src-template { background: #2c2140; color: #c4a6f0; } }

.map-line { display: grid; grid-template-columns: 160px 1fr auto; gap: var(--sp-2); align-items: center; padding: 6px 0; }
@media (max-width: 700px) { .map-line { grid-template-columns: 1fr; } }
.map-line .pname { font-weight: var(--fw-bold); font-size: var(--fs-label); }

/* The one line that tells a draft's map apart from a frozen version's. Not a
   warning box: it is true on the common path in the Builder, and a permanent
   amber banner there would be read past within a day. */
.draft-map-note { margin: 0 0 10px; line-height: 1.5; }

/* -------------------------------------------------------------- runner */

.run-step {
  display: grid; grid-template-columns: auto 1fr auto; gap: var(--sp-3);
  align-items: center; padding: 11px var(--sp-4); border-bottom: 1px solid var(--line);
}
.run-step:last-child { border-bottom: 0; }
.run-step .dot { width: 22px; display: flex; align-items: center; justify-content: center; }
.run-step .dot .ic-svg { width: var(--ic); height: var(--ic); }
.run-step .rt { font-weight: var(--fw-semi); }
.run-step .rs { font-size: var(--fs-small); color: var(--muted); }
.run-step.s-success { background: linear-gradient(to inline-start, var(--ok-soft), transparent 60%); }
.run-step.s-success .dot { color: var(--ok); }
.run-step.s-error { background: linear-gradient(to inline-start, var(--err-soft), transparent 60%); }
.run-step.s-error .dot { color: var(--err); }
.run-step.s-skipped { opacity: .72; }
.run-step.s-skipped .dot { color: var(--skip); }
.run-step.s-waiting .dot { color: var(--faint); }
.run-step.s-running { background: linear-gradient(to inline-start, var(--accent-soft), transparent 60%); }
.run-step.s-running .dot { color: var(--accent); }

.step-detail { padding: var(--sp-3) var(--sp-4); background: var(--panel-2); }
.step-detail > .msg { margin-bottom: var(--sp-3); }

/* The address a step actually sent to, on its own line above the body. */
.wire-line {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 6px 9px; margin-bottom: var(--sp-2); font-size: var(--fs-small);
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm);
  word-break: break-all;
}
.wire-line .ic-svg { color: var(--faint); }

.exec-bar { display: flex; gap: 6px; flex-wrap: wrap; }
.exec-head { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.run-history table th { position: static; }

/* THE USER-ACTION MARK on a matrix cell - #103.
 *
 * Five states that used to be five coloured circles and nothing else. Hue alone
 * cannot carry "ממתין לסנכרון" against "שונה בסביבת הלקוח": they were a yellow
 * dot and an orange dot. Each state now has an icon of its own, and the colour
 * only reinforces it. */
.ua-face { display: inline-flex; align-items: center; justify-content: center; }
.ua-ok { color: var(--ok); }
.ua-warn { color: var(--warn); }
.ua-err { color: var(--err); }
.ua-skip { color: var(--skip); }

/* ------------------------------------------------- the client × process grid
   #91. A wide table that must stay readable: the client column and the header
   row both stay put while the grid scrolls, because a cell in the middle of a
   large matrix means nothing once you can no longer see whose row it is. */
.matrix-wrap { overflow: auto; max-height: 70vh; border: 1px solid var(--line); border-radius: var(--radius); }
table.matrix { border-collapse: separate; border-spacing: 0; width: auto; min-width: 100%; }
table.matrix th, table.matrix td { border-block-end: 1px solid var(--line); border-inline-end: 1px solid var(--line); }
table.matrix tbody tr:hover td { background: inherit; }
.matrix-corner {
  position: sticky; inset-block-start: 0; inset-inline-start: 0; z-index: 3;
  background: var(--panel-2); text-align: start; padding: 9px var(--sp-3); white-space: nowrap;
}
.matrix-col {
  position: sticky; inset-block-start: 0; z-index: 2;
  background: var(--panel-2); padding: 9px var(--sp-3); text-align: center;
  font-weight: var(--fw-semi); white-space: nowrap; vertical-align: bottom;
}
.matrix-row {
  position: sticky; inset-inline-start: 0; z-index: 1;
  background: var(--panel); padding: 9px var(--sp-3); text-align: start; white-space: nowrap;
}
.matrix-cell { padding: 0; text-align: center; }
.matrix-btn {
  width: 100%; min-width: 74px; min-height: 0; padding: 9px 6px;
  border: 0; background: none; border-radius: 0;
  font: inherit; cursor: pointer; color: inherit;
  display: inline-flex; align-items: center; justify-content: center; gap: 4px;
}
.matrix-btn:hover:not(:disabled) { background: var(--panel-3); border-color: transparent; }
.matrix-btn:disabled { cursor: not-allowed; opacity: .45; }
.matrix-cell.is-on { background: var(--ok-soft); color: var(--ok); }
.matrix-cell.is-none { color: var(--muted); }
/* An edit nobody has saved yet. The whole screen is free until Save, so the
   difference between "this is how it is" and "this is what I asked for" has to
   be visible without reading a counter. */
.matrix-cell.is-edited { outline: 2px solid var(--accent); outline-offset: -2px; }
table.matrix.compare { min-width: 0; width: 100%; }
table.matrix.compare th, table.matrix.compare td { padding: 9px var(--sp-3); text-align: start; }

/* ---------------------------------------------------------------- island
 *
 * The Visual Builder needs the whole viewport, not the centred reading column
 * the rest of the Studio uses: a process diagram is a map, and a map squeezed
 * into 1440px with padding is a map you cannot read. When the builder is on
 * screen, the shell's main drops its padding and its max-width and becomes a
 * frame. Scoped to #view: the island's own <main> must keep the height its
 * flex layout gives it, and must keep its own overflow.
 */
body.full-bleed #view {
  padding: 0;
  max-width: none;
  height: 100vh;
  overflow: hidden;
}
body.full-bleed #shell { min-height: 100vh; height: 100vh; }
.island { height: 100%; min-height: 0; }
