Files
Project-SDE-WP-Suite/html/work-package-suite-styles.css
n.siegfried 0dcea8d725 T9.5 - C1+S8: the help-tip is real, the audit is written, BL-001 is dead
S8, finished where the plan said it would be: every .help-tip badge is a
<button> - upgraded by the component itself at load (help.js), with
helpTipUpgrade() for late renders, so a badge added tomorrow is born
reachable. The count the task warned about came true: 15 at wave 0, 18 at the
wave 6 exit, 20 at the start of this task - all 20 buttons now, and the fix
being in the component is what stops the number growing again. One
viewport-clamped role=tooltip bubble serves every badge: focus shows it,
Escape hides it, tap toggles it, tap-elsewhere closes it - the touch path
Field View's tablets never had. The injected styles now use theme tokens
(four raw hexes of the S5 kind, gone).

BL-001, CLOSED after three causes and nine waves: the old CSS ::after escaped
its badge to the right and was the creator's last 390px overflow. The clamped
bubble ends it - scrollWidth 390 vs clientWidth 390 - and frame_check's pin
FLIPPED, exactly as designed: it asserted the failure until the fix landed,
and now asserts the fix so a regression reopens the entry loudly.

The audit (docs/reference/accessibility-audit.md), every number probe-backed:
- div/span click handlers: 12/2 at wave 0 -> 0 (the wizard's constraint
  library entries and the dashboard chips became buttons here; the comments
  backdrop stopped pretending to be a control)
- outline:none without replacement: 0 (wp-chrome's one is the documented S12
  exception - its ring is on :focus-within, one ring not two)
- aria-live: every toast system and banner announces
- native dialogs: 79 -> 21, all on surfaces no S1 task named (admin, users,
  launcher) - documented as BL-024 with the T7.9 kit ready for them
- keyboard-only primary flow: covered leg by leg by the probes that dispatch
  real CDP key events, cited in the document

Three stale count-pins re-pointed to the numbers this task reached (stepper's
baseline-minus-10, form_structure's one-span-left, frame_check's BL-001 pin) -
each now pins the TARGET so slack cannot hide a regression.

Verification (each probe run alone): NEW tests/helptip_check.py 13/13.
Regressions: a11y_check 22/22, stepper_check 71/71, form_structure_check
50/51 (BL-022's product question), pipeline_check 44/44, frame_check 38/38.

Items: C1, S8 (BL-001 closed, BL-024 opened)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 13:37:10 -07:00

975 lines
33 KiB
CSS

/* Names only. Every value comes from theme-light.css, which this page loads
first — this sheet declares nothing of its own (T3.2 / S5 / C3). The names
stay because work-package-suite-app.js reads ten of them from JavaScript,
where a rename fails silently. See docs/reference/tokens.md section 9. */
:root {
--primary: var(--cds-interactive-01);
--primary-light: var(--cds-highlight);
--success: var(--cds-support-success);
--warning: var(--wp-status-warning-text);
--warning-bg: var(--wp-status-warning-bg);
--danger: var(--cds-support-error);
--text: var(--cds-text-primary);
--text-light: var(--cds-text-secondary);
--text-dim: var(--cds-text-helper); /* S11: was --cds-ui-04 (#8d8d8d, 3.32:1) */
--border: var(--cds-border-subtle);
--border-strong: var(--cds-border-strong);
--bg: var(--cds-background);
--bg-card: var(--cds-layer);
--appbar: var(--wp-appbar-bg);
--shadow: var(--wp-shadow-none);
/* Neutral, where the creator's --shadow-lg is cool-tinted. Same geometry in
both — 0 4px 16px — so the two can be merged later without moving anything;
merging is still a rendered change, so not here. tokens.md section 8-F. */
--shadow-lg: var(--wp-shadow-lg-neutral);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: var(--wp-font-sans);
color: var(--text);
background: var(--bg);
line-height: 1.5;
}
.app-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
/* HEADER — dark UI Shell bar
Four groups share this bar, not two: .header-left and .header-right are in the
markup, and wp-chrome.js injects the project switcher and search between them
while auth-guard.js appends the user menu after. At 1024px that is more than
fits on one 48px line, and a fixed height meant the overflow had nowhere to go
but on top of its neighbours. min-height + wrap lets the bar grow instead.
One row still measures exactly 48px, so nothing moves at desk width. */
.header {
background: var(--appbar);
color: var(--wp-appbar-fg);
padding: 0 16px;
min-height: 48px;
display: flex;
flex-wrap: wrap;
row-gap: 6px;
justify-content: space-between;
align-items: center;
}
/* Sizes to its content and never narrower than the logo.
This was `flex: 1; min-width: 0`, which put it in a dead tie with the chrome
wp-chrome.js injects as its sibling (`flex: 1 1 auto; min-width: 0`): both
claimed the same run of the bar, both were allowed to shrink to nothing, and
the chrome's wider content won every time. .header-left computed to 0 while
its `flex-shrink: 0` logo kept its 106px and overflowed — so the project
switcher rendered straight across the logo (F3).
Now the chrome is the only one that grows, and `min-width: auto` restores the
content-based floor, which is the logo plus the gap: the inner title block
keeps its own `min-width: 0`, so the project name still gives way first, by
the ellipsis .header-subtitle already carries. Truncation policy itself is
B2's, not this task's. */
.header-left {
flex: 0 1 auto;
display: flex;
align-items: center;
gap: 14px;
min-width: auto;
}
/* Prime logo (white-background wordmark) sits in a white chip on the dark bar */
.logo {
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--cds-ui-01);
padding: 4px 8px;
border-radius: 4px;
text-decoration: none;
transition: opacity 0.2s;
flex-shrink: 0;
}
.logo:hover { opacity: 0.92; }
.logo img { height: 24px; width: auto; display: block; }
.logo-icon {
width: 36px;
height: 36px;
background: var(--primary-light);
border-radius: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: 700;
}
.header-title {
font-size: 15px;
font-weight: 600;
margin-bottom: 0;
color: var(--wp-appbar-fg);
white-space: nowrap;
}
.header-subtitle {
font-size: 12px;
color: var(--wp-appbar-fg-dim);
min-height: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Holds its natural size. Left to the default `flex-shrink: 1` it was squeezed
narrower than its buttons, so "Load Sample" ran underneath "Feedback". The
chrome between the two groups is the only flexible one, which is what gives
the three a defined order of giving way: right keeps its buttons, left keeps
its logo, the middle absorbs the difference. */
.header-right {
display: flex;
align-items: center;
gap: 8px;
flex: 0 0 auto;
}
.header-button {
padding: 7px 14px;
background: transparent;
color: var(--wp-appbar-fg);
border: 1px solid var(--wp-appbar-border);
border-radius: 0;
cursor: pointer;
font-size: 14px;
font-weight: 400;
transition: background 0.15s, border-color 0.15s;
}
.header-button:hover {
background: var(--wp-appbar-hover);
border-color: var(--wp-appbar-border);
}
/* .step-counter — the "1 / 10" pill — is gone with A4/S9. The step rail carries
position, progress and reachability; a badge in the app bar carried one third
of that and was the only thing on the page that did. */
/* MAIN NAVIGATION - moved to wp-chrome.css by B7/T7.1.
The strip is chrome, and dissolving the creator's iframe made it a page that
has to draw the same strip. A tab row only one of the two documents can style
is the shape that put the tabs in the parent and the toolbar in the child. */
.tab-icon { font-size: 16px; }
/* CONTENT AREA
The SOP wizard reads better with a bound on line length, but 1000px on a 1920
screen wasted half the display. The wider cap stays; what it used to also have
to accommodate - the creator embedded in here, squeezed into a ~930px column
with its own scrollbar inside the page's - is a separate page since T7.1. */
.content-area {
flex: 1;
padding: 2rem;
max-width: 1700px;
margin: 0 auto;
width: 100%;
}
/* B7/T7.1: the creator's iframe was sized here - #wp-frame, .content-area.embed-full,
body.embed-full and the --wp-chrome-h custom property that carried the measured
app-bar height across. All of it existed so one document could fill the window
inside another and scroll with a single scrollbar. The creator is a page now;
the window sizes it. */
.tool {
display: none;
}
.tool.active {
display: block;
}
/* ══ STEP RAIL — vertical stepper (A4 / S9 / C1) ═══════════════════════════════
Replaces `.step-nav > .steps-container > .step-item`: ten <div onclick> chips
in a horizontal scroller. They were unreachable by keyboard, said nothing
about progress, and the "1 / 10" pill in the app bar was doing the one job
they should have been doing.
The rail is a column beside the form rather than a strip above it, so all ten
steps and their states are visible while you work in one. Below 900px there is
no room for a second column, so it collapses — see the breakpoint at the foot
of this block.
Spacing comes from --wp-s* (the suite's only real spacing scale) rather than
fresh literals; type does not, because there is no type token to consume yet.
That is BL-010 and it is unchanged in kind by this task. */
#tool-sop.active {
display: grid;
grid-template-columns: 15rem minmax(0, 1fr);
gap: var(--wp-s5);
align-items: start;
}
.step-rail {
grid-column: 1;
grid-row: 1 / span 2; /* beside the form AND its navigation bar */
/* Step 5 is roughly three screens tall. A rail that scrolls away is a rail you
have to hunt for, which is how the app ended up with a counter in the bar. */
position: sticky;
top: var(--wp-s4);
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 0;
padding: var(--wp-s3);
}
.step-rail-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
.step-rail-item { margin: 0; }
/* Every step is a real button, in the tab order, activated by Enter and Space
for free. Arrow keys, Home and End are added in work-package-suite-app.js. */
.step-btn {
width: 100%;
display: flex;
align-items: center;
gap: var(--wp-s3);
padding: var(--wp-s2);
background: none;
border: none;
border-left: 3px solid transparent;
border-radius: 0;
text-align: left;
font: inherit;
color: var(--text-light);
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.step-btn:hover { background: var(--bg); color: var(--text); }
.step-btn-marker {
flex: none;
width: var(--wp-ctl-sm);
height: var(--wp-ctl-sm);
border: 1px solid var(--border-strong);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 600;
line-height: 1;
}
.step-btn-body { min-width: 0; display: flex; flex-direction: column; }
.step-btn-label { font-size: 13px; font-weight: 500; }
/* The state in words. C1: completed / current / unavailable must be tellable
apart without colour, so each carries a label here AND a marker shape — filled
disc, filled disc with a tick, dashed outline — not a hue alone. */
.step-btn-state { font-size: 11px; color: var(--text-light); }
.step-btn-state:empty { display: none; }
.step-btn.is-current {
background: var(--primary-light);
border-left-color: var(--primary);
color: var(--text);
font-weight: 600;
}
.step-btn.is-current .step-btn-marker {
background: var(--primary);
border-color: var(--primary);
color: var(--cds-text-on-color);
}
/* Green because this is a status, not an action — see tokens.md section 12.
Green never fills a button in this suite; it fills the badge on one. */
.step-btn.is-complete .step-btn-marker {
background: var(--success);
border-color: var(--success);
color: var(--cds-text-on-color);
}
/* Still focusable and still in the tab order: a control removed from the tab
order is a control a keyboard user cannot even discover, and "you cannot go
there yet, here is why" is worth reaching. aria-disabled, not disabled. */
.step-btn.is-locked { cursor: not-allowed; }
.step-btn.is-locked .step-btn-marker { border-style: dashed; }
.step-rail-msg {
margin: var(--wp-s2) 0 0;
font-size: 12px;
color: var(--danger);
}
.step-rail-msg:empty { display: none; }
/* Wide widths: the list IS the control, so the disclosure has nothing to do. */
.step-rail-toggle { display: none; }
/* STEP CONTENT */
.step-content {
grid-column: 2;
grid-row: 1;
background: var(--bg-card);
padding: 2rem;
border: 1px solid var(--border);
border-radius: 0;
}
.step { display: none; }
.step h2 {
font-size: 22px;
font-weight: 400;
letter-spacing: -0.01em;
margin-bottom: 0.75rem;
color: var(--text);
}
.notice {
font-size: 13px;
color: var(--text-light);
background: var(--primary-light);
padding: 0.75rem 1rem;
border-radius: 0;
margin-bottom: 1.5rem;
border-left: 4px solid var(--primary);
}
/* FIELDS
The wizard's fields were one per row, which looked right in a 1000px column but
stretches a text input across the screen now that the content area is wide. Flow
them into as many ~340px columns as fit; `.col1` still forces a single column for
the fields that genuinely want the width (long text, textareas). */
.field-grid {
display: grid;
gap: 1.5rem;
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
}
.field-grid.col1 { grid-template-columns: 1fr; }
.field {
display: flex;
flex-direction: column;
}
.field label {
font-size: 13px;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--text);
}
/* An editable field is white on --border-strong, which is what an editable field
is on every other page in the suite. It used to be filled with var(--bg) — this
sheet's PAGE BACKGROUND, #f4f4f4 — so an empty required field was indistinguishable
from a locked one and people did not type in them (F5/A3).
The cause was the wizard declaring its own token set and therefore never seeing
--cds-field, which theme-light.css had been supplying to this page all along.
T1.5 reached past the local tokens with a fallback; T3.2 removed the local tokens
entirely, so the fallback is now dead code and the canonical token is simply
consumed. No field-specific colour is declared on this page. */
.field input,
.field select,
.field textarea {
padding: 0.75rem;
border: 1px solid var(--border-strong);
border-radius: 0;
font-size: 14px;
font-family: inherit;
color: var(--text);
background: var(--cds-field);
transition: border-color 0.2s;
}
/* Editable fields being white means genuinely locked ones have to say so, or the
signal inverts rather than being fixed. --cds-field-02 is the theme's own
secondary field surface and matches .locked-field in the creator.
The border is deliberately the same on both states: identical --border-strong is
what makes a field read as a field, and the fill is what carries locked versus
editable. Verified as #ffffff vs #f4f4f4 against an identical border. */
.field input:disabled,
.field select:disabled,
.field textarea:disabled,
.field input[readonly],
.field textarea[readonly] {
background: var(--cds-field-02);
color: var(--text-light);
cursor: not-allowed;
}
/* One focus ring for every control on this page (BL-002).
All three focus rules here removed the outline. Two replaced it with a 3px
var(--primary-light) glow — #edf5ff against a #ffffff field, which is a 1.05:1
edge: a faint halo on the card, invisible on the field itself. The third,
.seq-step input.seq-label, removed the outline with no replacement at all.
CLAUDE.md allows outline:none only with a replacement of at least equal
visibility, so two were marginal and one was a straight violation.
This is Carbon's ring and the one console.css:69 and wp-chrome.css:206 already
draw — 2px of --cds-focus inset over the control's own edge — so it is the app's
existing idiom rather than a fourth. border-color stays as a second cue and as
the fallback if outline is ever suppressed. */
.field input:focus,
.field select:focus,
.field textarea:focus,
.user-pick:focus,
.seq-step input.seq-label:focus {
outline: 2px solid var(--cds-focus);
outline-offset: -2px;
border-color: var(--primary);
}
/* Helper text under a field, by either of the two names the markup uses: <small>
inside a .field, and .field-hint anywhere. They were two rules with byte-identical
declarations — the duplication that produced the bug this block used to carry a
comment about, in miniature. One rule now, so they cannot drift apart again.
The colour is deliberately left as --text-dim. #8d8d8d on white measures 3.3:1,
under the 4.5:1 floor, and console.css:103 already rejected it for exactly this
job — but propagating that fix is T4.6, which re-measures rather than inheriting
either published figure. Changing it here would be a rendered change in a wave
that must produce none, and would take T4.6's decision away from it. */
.field small,
.field-hint {
font-size: 12px;
color: var(--text-dim);
margin-top: 0.25rem;
}
.field-hint strong { color: var(--text-light); }
/* The sign-off name pickers sit outside .field, so they got no form styling at all.
They carried the F5/A3 defect too, and were missed because the review counted
inputs inside .field: --bg fill on a --border hairline is the grey-on-grey that
made empty required fields read as locked. These are enabled selects and now look
like every other enabled control in the suite — --cds-field on --border-strong.
Focus is the shared ring above. */
.user-pick {
padding: 0.75rem; border: 1px solid var(--border-strong); border-radius: 0;
font-size: 14px; font-family: inherit; color: var(--text); background: var(--cds-field);
}
/* ROLES */
.required-roles {
display: flex;
flex-direction: column;
gap: 1rem;
}
.role-required {
display: flex;
align-items: center;
padding: 1rem;
background: var(--bg);
border-radius: 0;
border: 1px solid var(--border);
}
.role-checkbox {
display: flex;
align-items: center;
gap: 0.5rem;
}
.role-checkbox input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.role-checkbox label {
margin: 0;
font-weight: 600;
cursor: pointer;
}
/* WP TYPES */
#wp-types-table { overflow-x: auto; }
.wp-type-row {
display: grid;
grid-template-columns: 1.1fr 80px 1.3fr 1.6fr 1.2fr;
gap: 0.85rem;
align-items: center;
padding: 0.75rem 1rem;
background: var(--bg);
border-radius: 0;
margin-bottom: 0.5rem;
border: 1px solid var(--border);
}
.wp-types-header {
display: grid;
grid-template-columns: 1.1fr 80px 1.3fr 1.6fr 1.2fr;
gap: 0.85rem;
padding: 0.5rem 1rem;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--text-light);
margin-bottom: 0.5rem;
}
/* BUTTONS — roles are documented in docs/reference/tokens.md section 12 (A5) */
/* primary */
.add-btn {
padding: 0.7rem 1.25rem;
background: var(--wp-btn-primary-bg);
color: var(--wp-btn-primary-fg);
border: none;
border-radius: 0;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.add-btn:hover { background: var(--wp-btn-primary-hover); }
/* secondary */
.nav-btn {
padding: 0.7rem 1.4rem;
background: var(--wp-btn-secondary-bg);
border: 1px solid var(--wp-btn-secondary-border);
border-radius: 0;
font-size: 14px;
font-weight: 600;
color: var(--wp-btn-secondary-fg);
cursor: pointer;
transition: all 0.15s;
}
.nav-btn:hover { border-color: var(--wp-btn-secondary-hover-fg); color: var(--wp-btn-secondary-hover-fg); background: var(--bg); }
/* A5: this was green — the loudest control in the suite, on a button whose job
is to save a form. The completion it announced is still announced, on the
launcher card's border and status line, which is where a status belongs. */
.nav-btn.primary {
background: var(--wp-btn-primary-bg);
color: var(--wp-btn-primary-fg);
border-color: var(--wp-btn-primary-bg);
}
.nav-btn.primary:hover { background: var(--wp-btn-primary-hover); border-color: var(--wp-btn-primary-hover); color: var(--wp-btn-primary-fg); }
.nav-btn:disabled { opacity: 0.5; cursor: not-allowed; }
/* ══ LOCATION LIST — CR-005 (step 11) ═════════════════════════════════════════
Building / floor / sector, configured per project. The code beside each name
is the thing CR-018 rolls cost up by, and it is shown rather than hidden so it
is obvious that renaming a value does not move it. */
.loc-actions { display: flex; gap: var(--wp-s2); flex-wrap: wrap; margin-top: var(--wp-s3); }
.loc-report { margin-top: var(--wp-s3); font-size: 13px; }
.loc-report:empty { display: none; }
.loc-report-line { margin: 0 0 var(--wp-s2); }
.loc-report-group { margin-top: var(--wp-s3); }
.loc-report-title { font-size: 12px; font-weight: 600; color: var(--text-light); }
.loc-report-list { margin: var(--wp-s1) 0 0; padding-left: var(--wp-s5); }
.loc-report-list li { margin-bottom: var(--wp-s1); color: var(--text-light); }
.loc-line { font-weight: 600; color: var(--text); }
/* A report that lost rows says so in its own right, not only by wording. */
.loc-report.is-problem { border-left: 3px solid var(--warning); padding-left: var(--wp-s3);
background: var(--warning-bg); padding-top: var(--wp-s2); padding-bottom: var(--wp-s2); }
#loc-list { display: flex; flex-direction: column; margin-top: var(--wp-s3); }
.loc-row {
display: flex;
align-items: center;
gap: var(--wp-s3);
padding: var(--wp-s2);
border-bottom: 1px solid var(--border);
flex-wrap: wrap;
}
/* Indentation is the hierarchy, and it is not the only cue — .loc-level names
the level in words beside it (C1: never colour or position alone). */
.loc-depth-1 { padding-left: var(--wp-s5); }
.loc-depth-2 { padding-left: var(--wp-s6); }
.loc-level {
flex: none; min-width: 68px;
font-size: 11px; font-weight: 600; letter-spacing: .04em;
color: var(--text-light);
}
.loc-name {
flex: 1 1 180px; min-width: 0;
padding: var(--wp-s1) var(--wp-s2);
border: 1px solid var(--border-strong);
border-radius: 0;
background: var(--cds-field);
color: var(--text);
font: inherit;
font-size: 13px;
}
.loc-code {
flex: none;
font-family: var(--wp-font-mono);
font-size: 12px;
color: var(--text-light);
}
.loc-toggle { display: flex; align-items: center; gap: var(--wp-s1); font-size: 12px; color: var(--text-light); }
/* A deactivated value is still listed — that is how it is brought back — but it
reads as out of use, by strikethrough as well as by the unticked box. */
.loc-row.is-off .loc-name { text-decoration: line-through; color: var(--text-light); }
.loc-row.is-off .loc-code { text-decoration: line-through; }
.loc-addrow {
display: flex; align-items: center; gap: var(--wp-s2); flex-wrap: wrap;
margin-top: var(--wp-s4);
}
.loc-addlabel { font-size: 12px; font-weight: 600; color: var(--text-light); }
.loc-addrow select, .loc-addrow input {
padding: var(--wp-s2);
border: 1px solid var(--border-strong);
border-radius: 0;
background: var(--cds-field);
color: var(--text);
font: inherit;
font-size: 13px;
}
.loc-addrow input { flex: 1 1 200px; min-width: 0; }
/* ══ SECTION TOGGLES — CR-006 (step 12) ═══════════════════════════════════════
Ten rows, each a real <label> wrapping a real checkbox, so the whole row is a
target and the browser gives the keyboard and screen-reader behaviour for
free. The state is said in words as well as by the tick (C1). */
#section-toggles { display: flex; flex-direction: column; margin-top: var(--wp-s3); }
.section-toggle {
display: flex;
align-items: flex-start;
gap: var(--wp-s3);
padding: var(--wp-s3);
border: 1px solid var(--border);
border-left: 3px solid var(--success);
background: var(--bg-card);
margin-bottom: -1px; /* collapse the hairline between rows */
cursor: pointer;
}
.section-toggle:hover { background: var(--bg); }
.section-toggle input { flex: none; width: 18px; height: 18px; margin-top: 2px; }
.section-toggle-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--wp-s1); }
.section-toggle-name { font-size: 14px; font-weight: 600; color: var(--text); }
.section-toggle-note { font-size: 12px; color: var(--text-light); }
.section-toggle-state {
flex: none;
font-size: 11px;
font-weight: 600;
color: var(--text-light);
white-space: nowrap;
}
/* Off is said three ways: the box is unticked, the row says "Not used", and the
rule down its left edge goes from green to grey. Colour is the one nobody has
to be able to see. */
.section-toggle.is-off { border-left-color: var(--border-strong); }
.section-toggle.is-off .section-toggle-name { color: var(--text-light); }
/* CR-002: a field inside a section, not an eleventh section. Indented and
smaller so the nesting is visible, and it inherits every other rule above so
the two cannot look like different kinds of control. */
.field-toggle {
display: flex;
align-items: flex-start;
gap: var(--wp-s3);
padding: var(--wp-s2) var(--wp-s3) var(--wp-s2) var(--wp-s6);
border: 1px solid var(--border);
border-left: 3px solid var(--success);
background: var(--bg);
margin-bottom: -1px;
cursor: pointer;
}
.field-toggle:hover { background: var(--cds-layer-hover); }
.field-toggle input { flex: none; width: 16px; height: 16px; margin-top: 2px; }
.field-toggle .section-toggle-name { font-size: 13px; }
.field-toggle.is-off { border-left-color: var(--border-strong); }
.field-toggle.is-off .section-toggle-name { color: var(--text-light); }
/* ══ ANNOUNCEMENTS — S1 / T5.8 ════════════════════════════════════════════════
Where the wizard's thirteen native dialogs went. Fixed to the bottom of the
viewport so it is visible from any scroll position on a step that is three
screens tall, and never over the step navigation, which is where somebody's
attention is when a validation message fires. */
.wp-toast {
position: fixed;
left: 50%;
bottom: var(--wp-s5);
transform: translateX(-50%);
z-index: 1400; /* over the drawer scrim, under a modal */
display: flex;
align-items: flex-start;
gap: var(--wp-s3);
max-width: min(640px, calc(100vw - 2 * var(--wp-s4)));
padding: var(--wp-s3) var(--wp-s4);
background: var(--cds-ui-05);
color: var(--cds-text-on-color);
box-shadow: var(--wp-shadow-toast);
font-size: 13px;
line-height: 1.5;
}
.wp-toast[hidden] { display: none; }
.wp-toast-text { flex: 1 1 auto; min-width: 0; }
/* An error looks different, not just louder: a red rule down its edge, and it
stays until dismissed where a confirmation times out (C1 — never colour
alone, and the persistence is the second channel). */
.wp-toast.is-alert { border-left: 4px solid var(--danger); }
.wp-toast-action,
.wp-toast-close {
flex: none;
background: none;
border: 1px solid var(--wp-appbar-border);
border-radius: 0;
color: var(--cds-text-on-color);
font: inherit;
font-size: 12px;
font-weight: 600;
padding: var(--wp-s1) var(--wp-s2);
cursor: pointer;
}
.wp-toast-close { border-color: transparent; padding: var(--wp-s1); }
.wp-toast-action:hover,
.wp-toast-close:hover { background: var(--wp-appbar-hover); }
/* .field-error is declared once, in theme-light.css — the launcher's create form
and T5.8's step validation use the same component. */
/* The constraint-library entries (C1/T9.5): real buttons, block layout. */
.constraint-option { display:block; width:100%; text-align:left; padding:0.75rem;
background:var(--bg); border:1px solid var(--border); border-radius:6px;
margin-bottom:0.5rem; cursor:pointer; font:inherit; color:inherit; transition:all .2s; }
.constraint-option:hover, .constraint-option:focus-visible {
border-color:var(--primary); background:var(--primary-light); }
/* NAVIGATION
B6 / T7.8: sticky, the creator's pattern. On the Constraints and Sequence
steps the proposal's beside-the-fields actions meant scrolling to save; the
bar now rides the bottom of the viewport while a tall step scrolls, and sits
in its natural place on short ones. position:sticky (not the creator's
fixed) because the bar lives inside the wizard's grid column: sticky keeps
its slot in the flow, so it can never obscure a field at any width - there
is nothing to pad around. The T4.4 save-state indicator already mounts in
this bar (B5) and rides along. */
.step-navigation {
grid-column: 2;
grid-row: 2;
position: sticky;
bottom: 0;
z-index: 30;
display: flex;
gap: 1rem;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
background: var(--bg-card);
border-top: 1px solid var(--border);
box-shadow: var(--wp-shadow-sticky);
border-radius: 0;
box-shadow: var(--shadow);
}
/* COMMENTS */
.comments-section {
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 2px solid var(--border);
}
.comments-toggle {
padding: 0.5rem 1rem;
background: var(--primary-light);
color: var(--primary);
border: 1px solid var(--primary);
border-radius: 0;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.comments-toggle:hover { background: var(--primary); color: var(--cds-text-on-color); }
/* CONSTRUCTION SEQUENCE (drag & drop) */
#sequence-list { display: flex; flex-direction: column; gap: 8px; }
.seq-step {
display: flex; align-items: center; gap: 12px; padding: 11px 14px;
background: var(--bg-card); border: 1px solid var(--border); border-radius: 0;
box-shadow: var(--shadow); transition: border-color .12s, box-shadow .12s, opacity .12s;
}
.seq-step:hover { border-color: var(--primary); }
.seq-step.dragging { opacity: .35; border-style: dashed; }
.seq-step.drag-over { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-light); }
.seq-handle { cursor: grab; color: var(--text-dim); font-size: 16px; line-height: 1; user-select: none; flex-shrink: 0; }
.seq-handle:active { cursor: grabbing; }
.seq-num {
width: 24px; height: 24px; flex-shrink: 0; border-radius: 50%; background: var(--primary-light); color: var(--primary);
font-size: 11px; font-weight: 600; display: flex; align-items: center; justify-content: center;
}
.seq-step input.seq-label {
border: 1px solid transparent; background: transparent; font-size: 14px; padding: 5px 8px; color: var(--text); flex: 1; border-radius: 4px;
}
/* Fill only — the ring itself is the shared rule above, which this used to opt out
of entirely with a bare `outline: none`. */
.seq-step input.seq-label:focus { background: var(--bg); }
/* danger, filled: a 28px square is too small for an outline to read */
.seq-del {
background: var(--wp-btn-danger-fill-bg); color: var(--wp-btn-danger-fill-fg); border: none; border-radius: 4px;
width: 28px; height: 28px; cursor: pointer; font-weight: 600; flex-shrink: 0;
}
.seq-arrow { text-align: center; color: var(--text-dim); font-size: 13px; line-height: .4; margin: -2px 0; }
.seq-step.gate { border-color: var(--warning); background: var(--warning-bg); border-style: dashed; }
.seq-step.gate .seq-label { color: var(--warning); font-weight: 500; }
.seq-gate-badge {
flex-shrink: 0; padding: 3px 9px; border-radius: 20px; background: var(--warning); color: var(--cds-text-on-color);
font-size: 9px; font-weight: 600; letter-spacing: .08em; white-space: nowrap;
}
/* STEP COMMENTS DROPDOWN */
.comments-dropdown {
position: fixed;
top: 80px;
right: 2rem;
width: 360px;
max-width: calc(100vw - 2rem);
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 0;
box-shadow: var(--shadow-lg);
padding: 1.25rem;
z-index: 1200;
}
.comments-dropdown-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.comments-dropdown-close {
background: none;
border: none;
font-size: 16px;
cursor: pointer;
color: var(--text-light);
line-height: 1;
}
.comments-dropdown-close:hover { color: var(--text); }
/* MODAL */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--wp-scrim-neutral);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: var(--bg-card);
border-radius: 0;
padding: 2rem;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
box-shadow: var(--shadow-lg);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--border);
padding-bottom: 1rem;
}
.modal-header h3 { font-size: 18px; margin: 0; }
.modal-close {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: var(--text-dim);
}
.modal-close:hover { color: var(--text); }
/* UTILITY */
.sub-heading {
font-size: 14px;
font-weight: 700;
color: var(--text);
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* RESPONSIVE */
/* Below 900px there is no room for a rail beside the form. Ten vertical rows
stacked above the fields is most of a 390px screen before you reach the first
input, so the rail becomes a disclosure: where you are, tap to see the rest.
It is still the same ten buttons — nothing is hidden from the keyboard that is
not also hidden from the mouse. */
@media (max-width: 899px) {
#tool-sop.active { grid-template-columns: minmax(0, 1fr); gap: var(--wp-s4); }
.step-rail,
.step-content,
.step-navigation { grid-column: 1; grid-row: auto; }
/* The disclosure button carries the box at this width; the rail itself is only
a wrapper, and two nested borders read as two controls. */
.step-rail { position: static; background: none; border: 0; padding: 0; }
.step-rail:not(.is-collapsed) .step-rail-list {
background: var(--bg-card);
border: 1px solid var(--border);
padding: var(--wp-s2);
}
.step-rail-toggle {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
gap: var(--wp-s2);
padding: var(--wp-s3);
background: var(--bg-card);
border: 1px solid var(--border-strong);
border-radius: 0;
font: inherit;
font-size: 13px;
font-weight: 600;
color: var(--text);
cursor: pointer;
}
.step-rail-toggle-chev { transition: transform 0.15s; }
.step-rail.is-collapsed .step-rail-list { display: none; }
.step-rail:not(.is-collapsed) .step-rail-toggle { margin-bottom: var(--wp-s2); }
.step-rail:not(.is-collapsed) .step-rail-toggle-chev { transform: rotate(180deg); }
/* Gloved hands on a tablet: 44px minimum, which the 26px marker plus 8px of
padding does not reach on its own. */
.step-btn { min-height: 44px; }
}
@media (max-width: 768px) {
.header { height: auto; flex-direction: column; align-items: stretch; text-align: center; gap: 0.75rem; padding: 12px 16px; }
.content-area { padding: 1rem; }
.step-content { padding: 1rem; }
.wp-type-row { grid-template-columns: 1fr; }
.wp-types-header { display: none; }
.step-navigation { flex-direction: column; }
}