The team concluded these two are noise on a field work package: cost codes are
effectively constant on a job and the Acumatica task mapping is a PM concern.
The cost visibility they actually want is by building and floor, which is CR-004
and CR-018.
Hidden, not removed. CLAUDE.md: "Removed fields are hidden, not deleted (CR-002,
CR-016). Retain the data and the model." So this is a second, narrower toggle
list beside T5.5's sections - two fields inside General Information rather than
two more sections, because a section is a block of the document and these are
two rows in one.
no migration the values live in the work package's JSON data blob,
which nothing here writes to. The probe greps every
migration for a drop_column touching either.
no model change server/models.py is untouched by this task
no code change to the toggles are SOP data. Another project turns them
re-enable back on from step 12 and both fields return, values
included
A field is on only if its own toggle is on AND the section holding it is. Asked
as one question (WPSections.fieldOn) so no caller has to remember to ask both -
a field showing inside a hidden section is not a state worth reasoning about,
and the probe checks that case explicitly.
html/wp-sections.js FIELDS, fieldOn, normalizeFields
html/work-package-suite-app.js field rows nested under their section
html/work-package-suite-styles.css .field-toggle
html/wp-creation-index.html ids on the two .field wrappers
html/wp-creation-app.js WP_FIELD_NODES; both document rows conditional
tests/sections_check.py +22 checks (53 -> 75)
Done when
[x] neither field appears in the form, detail view or PDF export when off
[x] existing records still hold their values - a package EDITED while both are
off comes back through collectPackage() with both intact
[x] the fields can be re-enabled for another SOP without a code change
[x] no schema migration drops data - checked against every migration in the
tree, not just the ones this wave added
The whole .field wrapper is hidden, not the input: a bare label over nothing is
worse than either state.
Raised, not fixed
BL-019 A cost code that has left COST_CODES is silently blanked on edit.
wp_cost is a <select>, and setting .value to something with no matching
<option> does nothing at all - so opening such a package clears the
field and the next save writes the blank back. The same bug was fixed
once already for gov_wosize (work-package-suite-app.js:490-495) by
adding the stored value as an option; cost code never got it.
Found the honest way: a probe here used an invented cost code to prove
hiding a field does not delete its value, and the value came back
empty. That looked exactly like the toggle eating data. It was not, and
the probe now uses a real code and says why in a comment - a probe that
fails for a reason other than the one it names is worse than no probe.
Verified one at a time
sections_check 75/75 (53 + 22 for CR-002)
browser_check 71/71
stepper_check 70/70
a11y 22/22
url_state 23/23
autosave 34/34
locations_check 58/58
Question for the PR, per CLAUDE.md: BL-000b asks whether General Information
wants per-field toggles generally. This is not that - it is the two fields
CR-002 names, and the list is deliberately closed. If a third field wants one,
that is the general question and it needs the product answer BL-000b is holding.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
971 lines
31 KiB
CSS
971 lines
31 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 — underline tabs */
|
|
.main-nav {
|
|
display: flex;
|
|
gap: 0;
|
|
padding: 0 16px;
|
|
background: var(--bg-card);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
|
|
.nav-tab {
|
|
padding: 13px 18px;
|
|
background: none;
|
|
border: none;
|
|
border-bottom: 3px solid transparent;
|
|
border-radius: 0;
|
|
cursor: pointer;
|
|
font-size: 15px;
|
|
font-weight: 400;
|
|
color: var(--text-light);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
transition: background 0.15s, color 0.15s;
|
|
}
|
|
|
|
.nav-tab:hover {
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
}
|
|
|
|
.nav-tab.active {
|
|
background: none;
|
|
color: var(--text);
|
|
border-bottom-color: var(--primary);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.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 — and it also squeezed the embedded Work Package
|
|
Creator (an iframe living in here) into a ~930px column with its own scrollbar
|
|
inside the page's. Wider cap for the wizard; the embedded tools go full-bleed
|
|
(see .content-area.embed-full below). */
|
|
.content-area {
|
|
flex: 1;
|
|
padding: 2rem;
|
|
max-width: 1700px;
|
|
margin: 0 auto;
|
|
width: 100%;
|
|
}
|
|
|
|
/* Work Package Creation / Dashboard: the iframe fills the window below the app
|
|
chrome and owns the only scrollbar, so the creator's sticky save bar and
|
|
navigator drawer position against a real viewport instead of scrolling away. */
|
|
.content-area.embed-full {
|
|
/* `flex: none` matters: .content-area is a column flex item with `flex: 1`, whose
|
|
flex-basis:0% overrides `height` and leaves the used height INDEFINITE — so a
|
|
child's `height:100%` resolves to auto and the iframe collapses to its 150px
|
|
default. Opting out of flex sizing makes the height definite. */
|
|
flex: none;
|
|
max-width: none;
|
|
padding: 0;
|
|
height: calc(100vh - var(--wp-chrome-h, 96px));
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.content-area.embed-full > .tool.active {
|
|
flex: 1 1 auto;
|
|
min-height: 0; /* let it shrink instead of overflowing the shell */
|
|
height: 100%;
|
|
}
|
|
#wp-frame {
|
|
width: 100%;
|
|
border: 0;
|
|
min-height: calc(100vh - 200px);
|
|
}
|
|
#wp-frame.fill {
|
|
display: block;
|
|
height: 100%;
|
|
min-height: 0;
|
|
}
|
|
/* No page scrollbar while a full-bleed tool is open — the iframe scrolls. */
|
|
body.embed-full { overflow: hidden; }
|
|
|
|
.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); }
|
|
|
|
/* .field-error is declared once, in theme-light.css — the launcher's create form
|
|
and T5.8's step validation use the same component. */
|
|
|
|
/* NAVIGATION */
|
|
.step-navigation {
|
|
grid-column: 2;
|
|
grid-row: 2;
|
|
display: flex;
|
|
gap: 1rem;
|
|
justify-content: space-between;
|
|
padding: 1.5rem;
|
|
background: var(--bg-card);
|
|
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; }
|
|
.main-nav { flex-wrap: wrap; }
|
|
.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; }
|
|
}
|