There is no iframe in html/ any more. The creator is a top-level document with
the same app bar and the same tab strip as the SOP wizard; the two tabs that
used to swap a frame are links between them.
DEVIATION, stated rather than smuggled. The wave file says "remove the iframe
boundary so the creator renders in the parent document". It renders as its own
document instead. Every done-when is met - no iframe, no cross-frame messaging,
F4 resolved structurally, CR-006 toggles with no special-casing, back and
forward intact with T4.2's URL state - but the route is the other one, and the
reason is in creator-frame.md's own numbers:
merge into parent make it a page
selector collisions to resolve 21 0
script global collisions 9 0
cross-frame call sites to remove 28 28
probe entry points needing rework ~29 2
The 21 and the 9 were never the cost of dissolving the boundary. They are the
cost of MERGING TWO DOCUMENTS, which is a different change the boundary was
hiding. And 29 probe call sites address wp-creation-index.html directly, so a
route that keeps that address keeps all of them. creator-frame.md section 5
records this in full.
What went, and what replaced it:
#wp-frame, applyEmbedLayout, sizeWPFrame, viewportMinusChrome, chromeHeight,
renderWPTab, the resize handler, the ResizeObserver, --wp-chrome-h,
.content-area.embed-full, body.embed-full -> the window sizes the page
?embedded=1, body.embedded, .embed-hide, .embed-first -> nothing. An old
link carrying the param is ignored rather than half-obeyed.
openWpById / showDashboard / showForm / dashApplyFlag / applySopSections
called across the frame -> the URL. ?project= ?view= ?wp= ?flag= were
already read at the creator's own boot (T4.2), which is exactly why those four
could be DELETED rather than migrated. X4 is closed: the surviving path is the
one T5.5 built and proved.
inIframe in auth-guard.js, wp-chrome.js, wp-sidenav.js, help.js and _isTop in
project-data.js -> gone. help.js now reads the explicit WP_HELP_NO_FAB flag
both tool pages set, instead of inferring intent from where it is rendered.
.main-nav / .nav-tab in work-package-suite-styles.css -> wp-chrome.css,
because a tab row only one of two documents can style is the shape that put
the tabs in the parent and the toolbar in the child to begin with.
The three questions creator-frame.md section 4 said no count could answer:
1. The creator gets the app bar. It was the only page loading neither
wp-chrome file. Its header is now the .header-left / .header-right pair the
wizard uses, so the switcher lands in the same place on both.
2. Two sequence components, scoped not merged - confirmed Aug 18 that the
sequence is authored in the SOP and adjustable per package. BL-015 stays.
3. body.embedded is gone. The header it hid is replaced by the app bar; the
sample controls are visible in a new package toolbar (D1); the analytics
button is visible there until T7.10 moves it. The Dashboard BUTTON in that
row became a TAB, which is the one place B7's "fold the toolbar into the
tab row" actually happened.
Old addresses still resolve. ?tab=wp, ?view=dashboard and ?wp=<id> are in
bookmarks, in wp-sidenav's link map, and they are the shape CR-011 and CR-014
were specified against (X1). The wizard forwards them with replace(), so Back
does not bounce. Breaking these silently was the one regression this task could
have shipped that nobody would notice for weeks. frame_check.py section 4 pins
all three.
BEHAVIOUR CHANGE, deliberate. The live cross-frame hand-off showed the creator a
section toggle that had NOT been saved: flip it, look, reload, and the section
came back. What the creator shows now is the SOP that is stored. sections_check
5b pins both halves - an unsaved toggle does not travel, a saved one does.
BEHAVIOUR CHANGE, not deliberate, logged as BL-020. A tab switch is a page exit
now, so leaving the wizard with unsaved SOP edits fires T4.3's unsaved-work
guard. Nothing is lost - the guard writes the draft first and T4.3 recovers it -
but it is friction that did not exist, and suppressing a deliberate guard is a
product decision with its own downside. Logged, not quietly handled here.
tests/frame_check.py, 39 checks, new. Two of them exist because of failures
during this task rather than in it:
- "both documents parse and boot". A const shadowing a function parameter is a
SyntaxError, and work-package-suite-app.js did not parse at all for one run.
Four checks in url_state_check went red and not one said "the script did not
load". Asserting a page's own entry points exist costs nothing.
- "focus emulation is on, so a focus reading means something". An earlier draft
called page.call instead of page.ws.call inside a try/except and measured
nothing, reporting no focus ring anywhere - which looks exactly like a
finding. Trap 5 in reverse, for the second time in this project.
The four backlog entries logged against this file, re-measured rather than
assumed:
BL-001 still reproduces (485px in a 390px viewport) but its RECORDED CAUSE IS
WRONG. --nav-w now computes to 56px, so the injected-style explanation
is spent. The overflow is the creator's data tables - #asset-body's
lays out at 520px with no scroll container. frame_check reports the
offending boxes by selector and skips position:fixed subtrees, because
the comments drawer parked off-screen at right:844 made the first
measurement blame the drawer. Pinned, not fixed: T7.2 lays out the form.
BL-013 CLOSED. It was fixed by S12 in WAVE 4 - wp-creation-styles.css:209
carries the comment naming this entry - and nobody updated it. It was
quoted as a live CLAUDE.md violation while planning wave 7 and had not
been true for four waves. a11y_check walks 120 focusable elements on
the creator and every one rings at >= 3:1.
BL-006 15 by the probe's measure, unchanged; different denominator, stated.
BL-007 68 raw radii by the probe's measure. Nothing has reduced it in four
waves; it is measured every run now instead of once.
BL-018 cost a FOURTH probe. frame_check imports set_sop from sections_check
rather than writing a fifth copy of the workaround. T9.9 owns it.
Probes re-pointed, with reasons in the files: sections_check 5b (drove the live
hand-off), pipeline_check check 2 (read through contentDocument), f_items F4
(drove standalone and embedded; there is one mode now), validation_check
(lost "the wrong tab", gained the SOP gate).
Verified: frame_check 39/39, sections_check 95/95, pipeline_check 44/44,
url_state_check 23/23, validation_check 83/83, a11y_check 22/22,
autosave_check 34/34, aggregates_check 16/16, stepper_check 71/71,
browser_check 71/71, launcher_check 58/58, generalinfo_check 49/49,
rollup_check 63/63, cards_check 44/44, locations_check 58/58.
f_items: F1-F5 fixed, F6 reproduces (T7.2).
Metrics: iframes 1 -> 0, colour literals in rules outside theme-light.css 0,
dialogs 64, <div onclick> 2, .help-tip 18.
Items: B7 D1
Task: T7.1
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
954 lines
32 KiB
CSS
954 lines
32 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. */
|
|
|
|
/* 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; }
|
|
.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; }
|
|
}
|