/* Port adaptations layered over the verbatim prototype stylesheet (pcc.css).
   pcc.css must stay line-identical to PCC:app/globals.css — anything the Django port needs on
   top of it lives here.

   ponytail: pcc.css carries duplicate last-wins rules from prototype iterations; consolidate it
   — dedupe to computed rules, prune selectors with no matching markup, fold this file in, retire
   the line-parity test (tests/test_auth.py). The blocker was the porting work, which ended
   21 Sep 2026, so this is now a live task rather than a deferred one. Until it is done, pcc.css
   stays untouched: the line-parity test still guards it. */

/* Dashboard rows are real links in the port; mirror of `.portfolio-table > button`
   (globals.css .portfolio-head/.portfolio-table blocks). */
.portfolio-head, .portfolio-table > a { display: grid; grid-template-columns: minmax(240px, 1.5fr) minmax(180px, 1fr) minmax(170px, .9fr) minmax(150px, .8fr) 100px; gap: 14px; align-items: center; min-width: 920px; padding: 12px 18px; }
.portfolio-table > a { width: 100%; border-top: 1px solid var(--line); background: #fff; color: var(--ink); text-align: left; cursor: pointer; }
.portfolio-table > a:hover { background: #f7f9fd; }
.portfolio-table > a > span { display: grid; gap: 3px; min-width: 0; }
.portfolio-table > a strong { overflow-wrap: anywhere; font-size: 11px; }
.portfolio-table > a small { color: var(--muted); font-size: 9px; }
.portfolio-table > a span:nth-child(2) > b { width: max-content; border-radius: 999px; padding: 5px 8px; background: #edf3ff; color: var(--navy); font-size: 9px; }
.portfolio-table > a span:nth-child(2) > b.attention { background: #fff0da; color: #8a5816; }
.portfolio-table > a i { display: block; height: 6px; border-radius: 99px; overflow: hidden; background: #e4e9f2; }
.portfolio-table > a i b { display: block; height: 100%; background: var(--navy); }
.portfolio-table > a > span:last-child { color: var(--navy); font-size: 9px; font-weight: 900; text-align: right; }

.tender-trial-toggle:has(input:checked) { border-color: #d3a449; background: #fff9ea; }

/* The prototype moves `.selected` on the feedback-kind cards with React state
   (help-center.tsx:334); the port lets the checked radio drive the same declarations
   (pcc.css:6802-6803). The radio itself is visually hidden, so focus lands on the card. */
.feedback-kind-grid label:has(input:checked) { border-color: var(--navy); background: var(--blue-bg); box-shadow: inset 0 0 0 1px rgba(41, 66, 148, .08); }
.feedback-kind-grid label:has(input:checked) strong { color: var(--navy); }
.feedback-kind-grid label:has(input:focus-visible) { outline: 2px solid var(--navy); outline-offset: 1px; }

/* Feedback attachments (help-center.tsx:337): the input is visually hidden (pcc.css:6813), so
   keyboard focus shows on the box; the file list and the client-side error start hidden and the
   upload island reveals them — pcc.css's display:grid would otherwise beat the UA [hidden] rule. */
.feedback-dropzone:has(input:focus-visible) { outline: 2px solid var(--navy); outline-offset: 1px; }
.feedback-selected-files[hidden], .feedback-error[hidden] { display: none; }

/* Reporter-facing handling-stage track — the go-live prototype's component (owner's screenshot,
   4 Sep 2026), absent from the local checkout, so styled here rather than transcribed. */
.feedback-status-track { display: flex; margin: 0; padding: 2px 0 0; list-style: none; }
.feedback-status-track li { position: relative; display: grid; flex: 1; min-width: 0; justify-items: center; gap: 4px; }
.feedback-status-track li::before { content: ""; position: absolute; top: 5px; right: 50%; left: -50%; height: 1px; background: var(--line); }
.feedback-status-track li:first-child::before { display: none; }
.feedback-status-track li.done::before, .feedback-status-track li.current::before { background: var(--navy); }
.feedback-status-track i { position: relative; width: 11px; height: 11px; border: 1px solid var(--line); border-radius: 50%; background: #fff; }
.feedback-status-track li.done i { border-color: var(--navy); background: var(--navy); }
.feedback-status-track li.current i { border-color: var(--navy); box-shadow: 0 0 0 3px var(--blue-bg); }
.feedback-status-track span { color: var(--muted); font-size: 7px; line-height: 1.3; text-align: center; }
.feedback-status-track li.current span { color: var(--navy); font-weight: 800; }
.feedback-status-note { text-align: right; }

/* Owner request (4 Sep 2026): the prototype's utility-group separator (pcc.css:44) pads the
   item itself, which drops "Tentang PCC" and "Bantuan & Masukan" below the vertical centre every
   other item sits on, and its white hairline is invisible on the port's light sidebar anyway.
   Centre them like the rest and keep one group gap above the first utility item. */
.primary-nav .nav-item.utility { margin-top: 0; padding-top: 0; border-top: 0; }
.primary-nav .nav-item:not(.utility) + .nav-item.utility { margin-top: 13px; }

/* Admin password reset is an inline row form instead of a modal. */
.access-reset-form { display: inline-flex; align-items: center; gap: 6px; }
.access-reset-form input { width: 150px; min-height: 31px; border: 1px solid #ccd4cf; border-radius: 4px; background: #fff; padding: 5px 8px; color: var(--ink); font-size: 10px; }

/* The reopen-revision action is a real form in the port; give it the prototype's `> div` layout. */
.checklist-locked-message > form { display: grid; min-width: 260px; gap: 9px; }

/* The SPA never had a 403 page; the port renders one inside the shell. */
.forbidden-panel { display: grid; gap: 10px; justify-items: start; max-width: 620px; }
.forbidden-panel h1 { margin: 0; color: var(--navy-deep); font-size: 20px; }
.forbidden-panel p { margin: 0; color: var(--muted); font-size: 12px; line-height: 1.55; }

/* Forced password change is a standalone page instead of a modal. */
.account-security-page { display: grid; place-items: center; min-height: 100vh; padding: 26px; background: var(--canvas); }
.account-security-card { display: grid; gap: 12px; width: min(460px, 100%); }
.account-security-card h1 { margin: 0; color: var(--navy-deep); font-size: 22px; }
.account-security-card > p { margin: 0; color: var(--muted); font-size: 12px; line-height: 1.5; }

/* --- Requirement board (Phase 3 Task 3) ---------------------------------------------------- */

/* The row's status toggle and its summary are separate elements in one grid; the port wraps the
   toggle in a form, so the form itself must not become a grid item. */
.requirement-row > form.row-status-form { display: contents; }
a.requirement-summary, a.expand-button { text-decoration: none; }
a.requirement-summary:hover, a.expand-button:hover { background: #f7f9fd; }

/* The document-route card's select is a real form here (the prototype saved on change). */
.document-route-card > form { display: flex; align-items: end; gap: 8px; }
.document-route-card > form > label { display: grid; gap: 4px; }

/* Editing a checklist item was a modal; in the port it is an inline form in the description card. */
.requirement-edit-form, .requirement-add-form { display: grid; gap: 8px; margin-top: 10px; }
.requirement-edit-form label, .requirement-add-form label { display: grid; gap: 4px; color: var(--muted); font-size: 11px; }
/* The UA's small{0.8em} was compounding on a 9px label and landing the field labels at 7.2px. */
.requirement-edit-form label > small, .requirement-add-form label > small { font-size: inherit; }
.requirement-edit-form input, .requirement-edit-form textarea, .requirement-edit-form select,
.requirement-add-form input, .requirement-add-form textarea, .requirement-add-form select { border: 1px solid var(--line); border-radius: 4px; padding: 6px 8px; color: var(--ink); font-size: 13px; }
.requirement-edit-form .requirement-required-toggle,
.requirement-add-form .requirement-required-toggle { display: flex; align-items: center; gap: 6px; }
.requirement-edit-form .requirement-required-toggle input,
.requirement-add-form .requirement-required-toggle input { width: auto; }
.requirement-upload-form { flex-wrap: wrap; }
.requirement-source-trace { display: flex; gap: 10px; border: 1px solid var(--line-soft); border-radius: 5px; background: #fff; padding: 9px 11px; }
.requirement-source-trace small { color: var(--muted); font-size: 8px; }
.requirement-source-trace strong { display: block; color: var(--navy-deep); font-size: 10px; }
.requirement-source-trace em { color: var(--muted); font-size: 9px; }
.board-actions { justify-content: flex-end; }

/* The evidence and batch modals are pages in the port: no backdrop, so give the panel the page. */
.page-section > .modal { width: 100%; max-width: none; }
.evidence-keep-toggle { display: inline-flex; align-items: center; gap: 5px; color: var(--muted); font-size: 9px; }
.evidence-keep-toggle input { width: auto; }
a.evidence-preview-link { text-decoration: underline; }

/* --- Pusat Review (Phase 3 Task 4) ---------------------------------------------------------- */

/* The queue rows are links in the port; mirror of `.review-queue-list > button` in globals.css. */
.review-queue-list > a { display: grid; width: 100%; grid-template-columns: 9px minmax(0, 1fr) auto; align-items: center; gap: 10px; border: 0; border-bottom: 1px solid var(--line-soft); background: transparent; padding: 13px 14px; color: var(--ink); text-align: left; text-decoration: none; }
.review-queue-list > a:hover { background: #f6f8f7; }
.review-queue-list > a.active { background: #eef3f7; box-shadow: inset 3px 0 #527493; }
.review-queue-list > a > span:nth-child(2) { display: flex; min-width: 0; flex-direction: column; }
.review-queue-list > a > span:last-child { display: flex; align-items: flex-end; flex-direction: column; gap: 4px; }

/* The reader modal is a list of tracked links here: each one records the preview when opened,
   and carries hx-get so the pdf.js island renders it in #reader-dialog when JavaScript is on. */
.review-preview-list { display: grid; gap: 5px; margin-top: 11px; }
.review-preview-list > small { color: var(--muted); font-size: 8px; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; }
.review-preview-list > a { overflow-wrap: anywhere; color: var(--navy); font-size: 10px; text-decoration: none; }
.review-preview-list > a:hover { text-decoration: underline; }
.review-preview-list > a.viewed { color: var(--green); }

/* Decision and finding actions are forms in the port, not click handlers; `display: contents`
   keeps the prototype's own `> .review-correction-note` / `> .review-decision-actions` layout. */
.multi-level-decision-bar > form,
.ai-review-summary > form,
.ai-review-empty > form { display: contents; }

/* --- Paket Kirim (Phase 3 Task 6) ------------------------------------------------------------ */

/* The decision, submit, and feedback handlers are forms here; `display: contents` keeps the
   prototype's own `.package-decision-controls > label` / `> div` layout. */
.package-decision-controls > form { display: contents; }

/* The client-side ZIP builder is a download link in the port. */
.export-panel > a.button { text-decoration: none; }

/* --- Brankas Dokumen (Phase 3 Task 7) -------------------------------------------------------- */

/* Filter tabs and pagination are links carrying the query string; mirror of `.filter-tabs button`
   and the pagination buttons in globals.css. */
.filter-tabs > a { display: inline-flex; align-items: center; gap: 6px; border: 0; border-bottom: 2px solid transparent; background: transparent; padding: 8px 12px; color: var(--muted); font-size: 9px; font-weight: 700; text-decoration: none; }
.filter-tabs > a b { min-width: 19px; border-radius: 999px; background: var(--line-soft); padding: 2px 5px; color: inherit; font-size: 8px; text-align: center; }
.filter-tabs > a.active { border-bottom-color: var(--navy); color: var(--navy); }
.filter-tabs > a.active b { background: rgba(31, 61, 139, 0.1); }
.vault-pagination a.button, .table-actions a.button { text-decoration: none; }

/* The row's revoke action and the preview decision are forms here. */
.table-actions > form { display: contents; }
.vault-decision-form > .review-decision-bar { margin-top: 14px; }

/* --- Addendum & Klarifikasi (Phase 3 Task 8) ------------------------------------------------- */

/* The entry tabs are links here; mirror of `.change-tabs button` in globals.css. */
.change-tabs > a { border: 0; border-right: 1px solid var(--line); padding: 13px 18px; background: transparent; color: var(--muted); font-weight: 800; text-decoration: none; }
.change-tabs > a.active { background: #fff; color: var(--navy); box-shadow: inset 0 -3px var(--navy); }

/* Each impact row saves through its own form instead of an onChange handler. */
.impact-list > div > form { display: flex; align-items: center; gap: 7px; }

/* --- Meja Kerja (Phase 3 Task 9) ------------------------------------------------------------- */

/* Workbench rows, revision shortcuts, and task rows are links here; mirrors of
   `.role-task-list > button`, `.revision-command-card > div button`, and `.task-row`. */
.role-task-list > a { display: grid; grid-template-columns: 44px minmax(0, 1fr) 150px 100px; gap: 14px; align-items: center; width: 100%; border: 1px solid var(--line); border-radius: 13px; padding: 13px 14px; background: #fff; color: var(--ink); text-align: left; text-decoration: none; }
.role-task-list > a:hover { border-color: #aebfda; box-shadow: 0 5px 18px rgba(31, 58, 105, .08); }
.role-task-list > a > span:nth-child(2), .role-task-list > a .task-sla { display: grid; gap: 3px; }
.role-task-list > a > b { color: var(--navy); font-size: 9px; text-align: right; }
.revision-command-card > div > a, a.task-row { text-decoration: none; color: inherit; }
.task-filter-tabs > a { text-decoration: none; }
.task-search { display: flex; align-items: center; gap: 6px; }

/* Sprint status, claims, and allocations post instead of firing onChange. */
.sprint-card > header > form { display: flex; align-items: center; gap: 7px; }
.sprint-backlog-list > article > form { display: contents; }

/* --- Dashboard filters and sprint monitor (Phase 3 Task 14) ---------------------------------- */

/* The signal chips are links carrying ?filter=; mirror of `.portfolio-signals button`. */
.portfolio-signals > a { display: grid; gap: 2px; border: 0; border-right: 1px solid var(--line); background: transparent; padding: 13px 15px; color: var(--ink); text-align: left; text-decoration: none; }
.portfolio-signals > a:last-child { border-right: 0; }
.portfolio-signals > a.active { background: #eef3fa; box-shadow: inset 0 -3px var(--navy); }
.portfolio-tools > label { display: flex; align-items: center; gap: 6px; }
.sprint-monitor-detail > a.button { text-decoration: none; }

/* --- Tentang PCC & Bantuan (Phase 3 Task 15) ------------------------------------------------- */

/* PCC:app/about-pcc-actions.css — a component stylesheet in the prototype, not part of globals.css. */
.about-pcc-actions { display: flex !important; align-items: center; gap: 12px; margin-top: 5px; }
.about-pcc-actions .button { background: #fff; color: var(--navy); }
.about-pcc-actions > em { color: #dce6fb; font-size: 9px; font-style: normal; }
@media (max-width: 620px) { .about-pcc-actions { align-items: flex-start; flex-direction: column; } }

/* Role tabs and guide steps are links here; mirrors of `.help-role-tabs button` and the step
   buttons in globals.css. */
.help-role-tabs > a { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--line); border-radius: 999px; background: #fff; padding: 8px 13px; color: var(--muted); font-size: 10px; font-weight: 800; text-decoration: none; }
.help-role-tabs > a.active { border-color: var(--navy); background: #eef3fa; color: var(--navy); }
.help-steps a.button, .help-evidence-guide-body aside a.button { text-decoration: none; }

/* SSR adaptation: the prototype rendered <div class="modal-backdrop"><section class="modal …">;
   here the backdrop is a native <dialog>, so only the UA's own dialog styling is undone and every
   prototype rule (pcc.css:2261 .modal-backdrop, :2273 .modal) applies as written. */
/* Owner decision, 19 Sep 2026: the Brankas detail page carries the viewer inline. In the dialog the
   modal's height bounded the canvas; on a page it needs its own bound, or a long PDF pushes the
   decision bar out of reach. The file list is a GET form there (no-JavaScript switching). */
.vault-viewer { margin-top: 14px; border: 1px solid var(--line); border-radius: 5px; overflow: hidden; }
/* pcc.css centres the shell in the canvas; once the canvas is bounded a tall PDF would overflow above
   the top, unreachable by scrolling — anchor it to the top instead. */
.vault-viewer .attachment-viewer-canvas { max-height: 72vh; align-items: start; }
.vault-viewer form.attachment-preview-list { margin: 0; }
#reader-dialog:not([open]) { display: none; }
#reader-dialog { width: auto; height: auto; max-width: none; max-height: none; border: 0; }
#reader-dialog::backdrop { background: transparent; }

/* SSR adaptation: the prototype tracked upload progress in React state next to each control.
   Here one shell-level bar reports whatever htmx is currently uploading (spec §5, island 2). */
.upload-meter { width: 100%; height: 3px; border: 0; background: #e4e9f2; }
.upload-meter::-webkit-progress-bar { background: #e4e9f2; }
.upload-meter::-webkit-progress-value { background: var(--navy); }
.upload-meter::-moz-progress-bar { background: var(--navy); }

/* --- Bantuan & Masukan triage (Phase 4 Task 7) ------------------------------------------------ */

/* The prototype's status filter was a bare <label> in the header; SSR needs a GET form around it,
   so `display: contents` keeps the header's own layout. The apply button replaces the prototype's
   onChange submit — no inline script, and the filter still works without JavaScript. */
.feedback-history > header > .triage-filter-form { display: contents; }

/* --- Command palette (Phase 4 Task 8) --------------------------------------------------------- */

/* SSR adaptation: the prototype rendered .command-backdrop as a plain div overlay; here it is a
   native <dialog>, and the action rows are anchors rather than buttons, so the prototype's own
   `.command-results button` rules (pcc.css:4972-5011) are restated verbatim for `a`. */
#command-dialog:not([open]) { display: none; }
/* The UA's `dialog { width: fit-content }` would shrink the overlay to its contents, so the
   flex centring in .command-backdrop never gets a viewport to centre in. */
#command-dialog { width: auto; height: auto; max-width: none; max-height: none; border: 0; }
/* .command-backdrop already paints the dim and the 12vh offset (pcc.css:4908); the native
   ::backdrop would double it. */
#command-dialog::backdrop { background: transparent; }

.command-results a {
  display: grid;
  width: 100%;
  min-height: 55px;
  grid-template-columns: 32px minmax(0, 1fr) auto;
  align-items: center;
  gap: 11px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  padding: 8px 10px;
  color: var(--ink);
  text-align: left;
  text-decoration: none;
}

.command-results a:hover,
.command-results a:focus-visible,
.command-results a.active {
  background: #edf2ef;
}

.command-results a > i {
  display: grid;
  width: 30px;
  height: 30px;
  place-items: center;
  border: 1px solid #d5dbd7;
  border-radius: 4px;
  background: #fff;
  color: #49645a;
  font-family: var(--font-mono), monospace;
  font-size: 9px;
  font-style: normal;
  font-weight: 750;
}

.command-results a > span {
  display: flex;
  min-width: 0;
  flex-direction: column;
}

/* --- Document drafting (parity catch-up, 28 Agu 2026) ---------------------------------------- */

/* The prototype submitted the company-template file the moment it was chosen, from an onChange
   handler. Spec §5 allows exactly three JavaScript islands and this is not one of them, so each
   card lives in its own form with a real submit button; the form becomes the grid cell so the card
   and its button stay together in one column. */
.method-choice-grid > form { display: grid; align-content: start; gap: 8px; }
.method-choice-grid > form > .method-choice-card { width: 100%; }

/* The draft editor is a page here, not a modal, so the panel takes the page's width. */
#draft-editor.modal { width: 100%; max-width: none; }
#draft-editor .draft-editor textarea { width: 100%; }

/* --- Administrator allocation table (parity catch-up, 28 Agu 2026) ---------------------------- */

/* The prototype's row is a <div> that saves on every change; the port wraps it in a <form> with an
   explicit save, so the row rules (pcc.css:8099 `.sprint-assignment-table > div`) are restated for
   the form and given a fourth column for the button. */
.sprint-assignment-table > form {
  display: grid;
  grid-template-columns: minmax(260px, 1fr) 210px minmax(180px, .6fr) auto;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
}
.sprint-assignment-table > form:last-child { border-bottom: 0; }
.sprint-assignment-table > form > span { display: grid; gap: 3px; }
.sprint-assignment-table > form > span small { color: var(--muted); }

@media (max-width: 800px) {
  .sprint-assignment-table > form { grid-template-columns: 1fr; }
}

/* --- Intake preview editor (parity catch-up, 29 Agu 2026) ------------------------------------- */

/* The prototype's ReaderReviewEditor lived in the reader modal, which sized its own cards; here the
   same rows sit inside the intake panel, where an inherited rule clips them to the height of a
   collapsed summary and hides the category, route, and required controls entirely. The row must
   size to its content. */
.reader-results > .reader-result-item { height: auto; overflow: visible; }
/* The reading's review page (interface review, 26 Sep 2026): the prototype's list was a 450px
   scrollbox inside a modal, and its card carried a five-field editor. Here the list is the page
   and each row is one line — number, checkbox, name with its description, then the locator and
   the reader's tags. The whole line is the checkbox's label. */
.checklist-gap-analysis .reader-results { max-height: none; overflow: visible; padding: 0; gap: 8px; }
/* pcc.css paints a row without `.selected` dashed and grey; here a row is kept unless its box is
   unticked, and that state is read from the checkbox itself (the :has rule below). */
.checklist-gap-analysis .reader-result-item { border-style: solid; background: #fff; }
.reader-results > .reader-result-item > .reader-result-header {
  display: grid; grid-template-columns: 30px 20px minmax(0, 1fr) auto; align-items: start;
  gap: 8px 12px; min-height: 0; padding: 12px 14px; cursor: pointer;
}
.reader-results > .reader-result-item > .reader-result-header > b {
  padding-top: 2px; color: var(--navy); font-size: 12px; font-variant-numeric: tabular-nums;
}
.reader-results > .reader-result-item > .reader-result-header > input {
  width: 18px; height: 18px; margin: 1px 0 0; accent-color: var(--navy);
}
.reader-row-title { display: grid; gap: 3px; min-width: 0; }
.reader-row-title > strong { color: var(--ink); font-size: 13px; font-weight: 700; line-height: 1.4; }
.reader-row-title > small {
  color: var(--muted); font-size: 11.5px; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Locator and reader tags: one right-aligned group that wraps as a unit, so a long locator never
   pushes a tag past the card edge. */
.reader-row-meta {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 6px;
  max-width: 100%; padding-top: 1px;
}
.reader-row-meta > em { color: var(--muted); font-size: 11px; font-style: normal; line-height: 1.4; text-align: right; }
.reader-results > .reader-result-item:has(> .reader-result-header > input:not(:checked)) {
  border-style: dashed; background: #f7f7f6;
}
.reader-results > .reader-result-item:has(> .reader-result-header > input:not(:checked)) .reader-row-title > strong {
  color: var(--muted); text-decoration: line-through;
}
@media (max-width: 640px) {
  .reader-results > .reader-result-item > .reader-result-header { grid-template-columns: 30px 20px minmax(0, 1fr); }
  .reader-row-meta { grid-column: 3; justify-content: flex-start; }
  .reader-row-meta > em { text-align: left; }
}
.intake-review .page-header .button { flex: 0 0 auto; }
.intake-review .reader-import-footer { margin-top: 4px; }
/* "Batalkan pembacaan ini" is a .button.ghost (pcc.css:576 — transparent border, transparent
   background); beside the primary confirm a neutral outline is enough to read as a button without
   competing with it (task 5 review, 22 Sep 2026). */
.intake-review .reader-import-footer .button.ghost { border-color: var(--line); }
.intake-review .reader-cancel-form { display: contents; }
.reader-import-footer { display: flex; align-items: center; justify-content: space-between; gap: 16px; }


/* The add card is a create form, not a row's edit form: a distinct class keeps a selector for
   "the form on a requirement row" unambiguous — reusing the name made the UAT harness pick the
   collapsed add card instead of a row. */
.requirement-add-card { margin: 12px 0; border: 1px solid var(--line); border-radius: 12px; padding: 10px 14px; background: #fff; }
.requirement-add-card > summary { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 5px 0; cursor: pointer; color: var(--navy); font-size: 11px; font-weight: 800; list-style: none; }
.requirement-add-card > summary::-webkit-details-marker { display: none; }

/* Owner UX round, 18 Sep 2026: on an undecided authoring-choice row the finished-document upload
   leads and the A/B drafting cards wait behind a native <details>. The grid keeps its prototype
   rules; only its top rule is dropped because the details supplies the separator. */
.authoring-fallback { margin-top: 12px; border-top: 1px solid var(--line-soft); padding-top: 10px; }
.authoring-fallback > summary { cursor: pointer; color: var(--navy); font-size: 10px; font-weight: 800; }
.authoring-fallback > .method-choice-grid { margin-top: 10px; border-top: 0; padding-top: 0; }
/* pcc.css:3838 dresses every <small> in .authoring-control as an uppercase kicker; the hint is a sentence. */
/* User feedback, 18 Sep 2026: the names a `.file-button` picker chose, painted by upload-progress.js. */
.row-actions > .chosen-files, .evidence-slot-actions > .chosen-files { flex-basis: 100%; color: var(--ink); font-size: 9px; font-weight: 600; letter-spacing: 0; text-transform: none; overflow-wrap: anywhere; }
.authoring-control .requirement-upload-form > .upload-hint { flex-basis: 100%; color: var(--muted); font-size: 9px; font-weight: 500; letter-spacing: 0; text-transform: none; }

/* The running commit, shown on the login screen and in the sidebar footer. Not in the prototype:
   the prototype had no deploy of its own, and an automated one needs a way to answer "is my push
   live?" without an SSH session. */
.app-version { display: block; margin-top: 10px; color: var(--muted); font-size: 9px; letter-spacing: .04em; }

/* Tender switcher — SSR adaptation of the prototype topbar switcher (v182 §20.2). Same variables
   as .sidebar-persona-switcher (pcc.css) since it sits in the same light sidebar. */
/* Topbar tender switcher — SSR adaptation of the prototype's button+state dropdown
   (PCC:app/tender-workspace.tsx:2163): <details>/<summary> supplies open/close without
   JavaScript, so the .caret.open class becomes the [open] attribute, the option buttons become
   links, and the delete column stays with the deletion screens. */
.tender-switcher-wrap > summary { cursor: pointer; list-style: none; }
.tender-switcher-wrap > summary::-webkit-details-marker { display: none; }
.tender-switcher-wrap[open] .caret { transform: rotate(225deg) translate(-1px, -2px); }
.switcher-menu-head a { color: var(--navy); font-size: 11px; font-weight: 700; text-decoration: none; }
.switcher-option { text-decoration: none; }

/* Work-type chip (v182 §5.3) — mirrors the sidebar label idiom. */
.worktype-chip { font-size: 10px; font-weight: 800; letter-spacing: 0.06em; padding: 1px 6px; border-radius: 999px; border: 1px solid var(--line); color: var(--navy); margin-left: 6px; vertical-align: middle; }

/* Brankas renewal (v182 §7.3/§7.4) — new feature, no prototype source. The distribution/status
   forms are compact row controls; mirror of the .access-reset-form inline idiom above. */
.renewal-status-block { display: grid; gap: 4px; margin-top: 6px; }
.renewal-assign-form { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 4px; }
.renewal-assign-form input, .renewal-assign-form select, .renewal-assign-form textarea { min-height: 28px; border: 1px solid #ccd4cf; border-radius: 4px; background: #fff; padding: 4px 7px; color: var(--ink); font-size: 9px; }
.renewal-assign-form input[type="text"] { width: 120px; }
.renewal-assign-form textarea { width: 180px; min-height: 28px; resize: vertical; }

/* --- Kinerja proses dashboard section (v182 §6, OI-04..OI-06) — new feature, no prototype
   source. The KPI/renewal-count cards reuse the .sprint-monitor-signals idiom's variables and
   proportions (pcc.css:8190-8194); the action lists reuse the .role-task-list link idiom. */
.insights-block { display: grid; gap: 16px; margin-top: 20px; }
.insights-block > header > span { display: grid; gap: 4px; }
.insights-block > header small { color: var(--navy); font-size: 9px; font-weight: 900; letter-spacing: .1em; text-transform: uppercase; }
.insights-block > header h2 { margin: 0; color: var(--navy-deep); font-size: clamp(20px, 2vw, 27px); }
.insights-block > header p { margin: 0; color: var(--muted); font-size: 11px; line-height: 1.55; }

.insights-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 9px; }
.insights-kpis > span { display: grid; gap: 3px; border: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); border-radius: 12px; background: #fff; padding: 12px 14px; }
.insights-kpis > span > small:first-child { color: var(--muted); font-size: 8px; font-weight: 850; letter-spacing: .06em; text-transform: uppercase; }
.insights-kpis > span > strong { color: var(--navy-deep); font-size: 24px; }
.insights-kpis > span > small:last-child { color: var(--muted); font-size: 8px; font-style: normal; }

.renewal-detail { border: 1px solid var(--line); border-radius: 12px; background: #fff; padding: 12px 14px; }
.renewal-detail > summary { cursor: pointer; color: var(--navy); font-size: 10px; font-weight: 800; }
.insights-renewal-counts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 9px; margin-top: 10px; }
.insights-renewal-counts > span { display: grid; gap: 3px; border: 1px solid var(--line-soft); border-radius: 10px; padding: 9px 11px; }
.insights-renewal-counts > span > small { color: var(--muted); font-size: 8px; font-weight: 850; letter-spacing: .06em; text-transform: uppercase; }
.insights-renewal-counts > span > strong { color: var(--navy-deep); font-size: 18px; }

.insights-actions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.insights-action-group h3 { margin: 0 0 8px; color: var(--navy-deep); font-size: 11px; }
.insights-action-list { display: grid; gap: 6px; }
.insights-action-list > a { display: grid; gap: 2px; border: 1px solid var(--line); border-radius: 10px; padding: 9px 11px; color: var(--ink); font-size: 10px; text-decoration: none; }
.insights-action-list > a:hover { border-color: #aebfda; box-shadow: 0 5px 18px rgba(31, 58, 105, .08); }
.insights-action-list > a > small { color: var(--muted); font-size: 8px; }
.insights-empty { margin: 0; color: var(--muted); font-size: 9px; }

@media (max-width: 760px) {
  .insights-kpis, .insights-renewal-counts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .insights-actions { grid-template-columns: 1fr; }
}

/* --- Feedback KPI/SLA dashboard (Phase 4 Task 2, v182 §12.2-§12.4) ----------------------------- */

/* New component, not in the prototype: an Administrator-only dashboard above the existing history
   list. Mirrors the `.insights-kpis` card idiom above (border-top accent, rounded card) but every
   chip is a link — clicking one applies an HTMX filter to #feedback-history (dual-path per FB-03:
   href navigates full-page, hx-get swaps just the panel). */
.feedback-metrics { display: grid; gap: 12px; margin-top: 18px; border-top: 1px solid var(--line); padding-top: 18px; }
.feedback-metrics > header { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.feedback-metrics > header h3 { margin: 0; color: var(--navy-deep); font-size: 16px; }
.feedback-metrics-sla { color: var(--muted); font-size: 9px; }
.feedback-metrics-chips { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 9px; }
.feedback-metrics-chips > a { display: grid; gap: 3px; border: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); border-radius: 12px; background: #fff; padding: 11px 13px; color: var(--ink); text-decoration: none; }
.feedback-metrics-chips > a:hover { border-color: #aebfda; box-shadow: 0 5px 18px rgba(31, 58, 105, .08); }
.feedback-metrics-chips > a.active { border-top-color: var(--red); background: #eef3fa; }
.feedback-metrics-chips > a.risk { border-top-color: var(--red); }
.feedback-metrics-chips > a > small { color: var(--muted); font-size: 8px; font-weight: 850; letter-spacing: .06em; text-transform: uppercase; }
.feedback-metrics-chips > a > strong { color: var(--navy-deep); font-size: 20px; }
.feedback-metrics-breakdowns { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.feedback-metrics-breakdowns h4 { margin: 0 0 6px; color: var(--navy-deep); font-size: 11px; }
.feedback-metrics-breakdowns ul { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
.feedback-metrics-breakdowns a { display: flex; align-items: center; justify-content: space-between; gap: 8px; border: 1px solid var(--line); border-radius: 8px; padding: 6px 9px; color: var(--ink); font-size: 10px; text-decoration: none; }
.feedback-metrics-breakdowns a:hover { border-color: #aebfda; }
.feedback-metrics-breakdowns a.active { border-color: var(--navy); background: #eef3fa; }
.feedback-metrics-breakdowns a > b { color: var(--navy); }
.feedback-metrics-empty { color: var(--muted); font-size: 9px; }
.feedback-history-empty { color: var(--muted); font-size: 10px; }

@media (max-width: 760px) {
  .feedback-metrics-chips { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .feedback-metrics-breakdowns { grid-template-columns: 1fr; }
}

/* --- Multi-user status strip (Phase 4 Task 3, v182 §13: SYNC-01/02/03/05/06) -------------------- */

/* New component, not in the prototype (the SPA had no multi-tab conflict story). Read-only,
   self-polling flex row of label/value pairs — mirrors the `.workflow-status-card` idiom above.
   Never wraps a form: another user's activity can never replace typed text (SYNC-02). */
.status-strip { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin-bottom: 14px; border: 1px solid var(--line); border-radius: 12px; background: #fff; padding: 10px 16px; }
.status-strip > span { display: grid; gap: 2px; }
.status-strip > span > small { color: var(--muted); font-size: 9px; font-weight: 850; letter-spacing: .06em; text-transform: uppercase; }
.status-strip > span > strong { color: var(--navy-deep); font-size: 12px; }
.status-strip-notice { color: var(--navy); font-size: 10px; font-style: normal; font-weight: 700; }

/* --- Checklist review composer save-lock (v207 parity, Task 9 fix round 1) --------------------- */

/* v207's feedbackSaving disables both decision buttons and swaps the helper text while the
   composer's own feedback POST is in flight; the port has no client state to hold that flag, so
   htmx's own in-flight signal stands in for it (hx-indicator on #checklist-review-form, toggled by
   the "Tambahkan feedback" button in checklist_panel.html). */
#checklist-review-form .decision-helper-saving { display: none; }
#checklist-review-form.htmx-request .decision-helper-saving { display: block; }
#checklist-review-form.htmx-request .decision-helper:not(.decision-helper-saving) { display: none; }

/* Known, accepted cosmetic quirk (not a bug): htmx also stamps `htmx-request` on the element that
   ISSUED the request, not only the one an hx-indicator names. For the decision POST, the button's
   `form="checklist-review-form"` makes that issuing element #checklist-review-form itself — the
   same selector above — so clicking "Kembalikan ke Staff" / "Selesaikan review & teruskan" also
   flashes the "Tunggu sampai feedback tersimpan…" helper for a moment. It is sub-second and the
   panel swaps in the server's response right after, so nothing is left showing the wrong helper.
   A clean fix needs a separate indicator element scoped only to the feedback POST; not done here
   because the visible defect is nil. */

/* --- Reviewer completion confirmation (Change A, v207 parity) ----------------------------------- */

/* v207 keeps manualChecked as passive client state and disables "Selesaikan review & teruskan" in
   the DOM the moment it changes (checklist-workflow-panel.tsx:142,204). The port renders server-side
   and has no such state, so workspace.permissions.can_review_checklist is the real gate (the
   confirmation now posts with #checklist-review-form); this :has() rule is only a same-page visual
   hint, not a blocker — no pointer-events:none, so the button stays reachable by mouse and keyboard
   and a click still reaches the server, which is what tests/test_review_single_form.py proves. */
.reviewer-completion-panel:not(:has(input[name="confirmed"]:checked)) .checklist-decision-actions button[value="approved"] {
  opacity: 0.55;
  cursor: not-allowed;
}
.reviewer-completion-panel:has(input[name="confirmed"]:checked) .decision-helper-confirm { display: none; }

/* --- UAT 14 Sep 2026: Brankas + Checklist feedback ---------------------------------------------- */

/* In-flight feedback on every htmx form (Brankas 1, Checklist 2). htmx stamps `htmx-request` on the
   element that issued the request — the form, or a button posting on its own — for the life of
   the request; the submit button then spins and stops taking clicks. A second submit is already
   dropped by htmx's default hx-sync, so this is the visible half, not the guard. */
form.htmx-request, form.submitting { cursor: progress; }
form.htmx-request button[type="submit"], form.submitting button[type="submit"], button.htmx-request { position: relative; pointer-events: none; opacity: .7; padding-right: 30px; }
form.htmx-request button[type="submit"]::after, form.submitting button[type="submit"]::after, button.htmx-request::after { content: ""; position: absolute; top: 50%; right: 9px; width: 12px; height: 12px; margin-top: -7px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: pcc-spin .7s linear infinite; }
@keyframes pcc-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { form.htmx-request button[type="submit"]::after, form.submitting button[type="submit"]::after, button.htmx-request::after { animation-duration: 2s; } }

/* Round 9, item 5 retired on 16 Sep 2026: the baseline list itself is gone. It restated the board —
   number, name, wajib, source locator — across a third of a very long page, above the rows it
   duplicated. */
/* Round 9, item 6: the reason a prominent button is disabled, under the button. */
.action-reason { display: block; margin-top: 4px; color: var(--muted); font-size: 9px; text-align: right; }
/* Round 9, item 7: a pending version that a newer upload has replaced. */
.approval-label.superseded { background: #eef1f6; color: var(--muted); }
/* Round 9, item 9: the audit view toggle reuses the filter tabs. */
.audit-view-toggle { margin-bottom: 14px; }

/* The upload's answer (Brankas 1): the prototype's toast, as a status line above the header. */
#vault-panel { scroll-margin-top: 100px; }  /* hx-swap "show:top" lands under the sticky topbar otherwise */
.requirement-row { scroll-margin-top: 100px; }  /* the upload form's show:#requirement-<id>:top, same reason */
.login-alert.success { margin-bottom: 14px; border-left-color: var(--line); background: #eef7f1; color: #1f5a3f; box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }

/* The upload form folded under the prototype's header button (Brankas 2). <details> keeps the
   no-JavaScript route: the summary is the button, the form is the modal card rendered inline. */
.vault-form-fold { margin: 0 0 18px; }
.vault-form-fold > summary { display: inline-flex; list-style: none; cursor: pointer; }
.vault-form-fold > summary::-webkit-details-marker { display: none; }
.vault-form-fold[open] > summary { margin-bottom: 12px; }
.vault-form-fold .vault-form-card { width: 100%; max-height: none; animation: none; border: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.vault-form-fold .vault-form-card > header { border-bottom: 1px solid var(--line); }

/* OCR queue state on a Brankas row and its detail page (Brankas 4). */
.vault-ocr-hint { color: var(--navy); font-weight: 700; }
.vault-returned-note .button { margin-left: 8px; }

/* The prototype's compact-upload inside the addendum grid (Checklist 1): that grid styles every
   label > span as a small uppercase caption (pcc.css:6724 `.change-form-grid label>span`); the
   upload label keeps its own voice. The matching `.access-form` half died when the tender-creation
   form stopped carrying that class (Task 1) and the intake panel never did (Task 4) — removed
   (whole-branch review, 22 Sep 2026); `.intake-files` needs no such override, since nothing scopes
   an uppercase-caption rule to `.tender-intake-form`. */
.change-form-grid .compact-upload { display: flex; }
.change-form-grid .compact-upload > span { color: inherit; font-size: inherit; font-weight: inherit; letter-spacing: normal; text-transform: none; }
.change-form-grid .compact-upload > small { font-size: 11px; }
.change-form-grid .compact-upload input { width: 100%; min-height: 0; border: 0; padding: 0; }

/* The next-step banner was merged into the submit card on 16 Sep 2026; its override went with it.
   Both blocks stay scroll-anchored, because the returned-checklist note still links to
   #checklist-submit and that id now sits on the card. */
.checklist-approval-message, .checklist-submit-card { scroll-margin-top: 16px; }

/* --- Interface review, 21 Sep 2026 ------------------------------------------------------------
   Findings from the better-interface pass over the checklist-creation flow. pcc.css untouched. */

/* The intake radios are visually hidden (pcc.css:8232), so the app-wide focus outline was landing
   on a 1x1 box — the same problem the feedback cards solved at the top of this file. */
.intake-mode-options > label:has(input:focus-visible) { outline: 2px solid var(--navy); outline-offset: 1px; }

/* pcc.css:2322 paints the dialog's close glyph #9da4af on white — 2.51:1 against the 4.5:1 it
   needs. --muted measures 7.5:1; :hover keeps its own navy on specificity. */
.modal-close { color: var(--muted); }

/* One live region per page for the reader's progress, outside the polled panel so its text can be
   updated in place (htmx swaps #intake-panel wholesale, which would recreate a region inside it). */
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* pcc.css:5592 paints a file picker that is merely empty in the danger palette (#fcf3f4 on
   #cf9ca2, text #9c3e47) — the pair of `.complete`'s green. Nothing has gone wrong when the page
   first renders, so the two loudest things on the intake panel were two ordinary file inputs.
   The base `.compact-upload` (pcc.css:3249) is already the right neutral dashed target; this puts
   its three colours back and keeps only the geometry the modifier adds. Same fix reaches the
   Brankas form, which wears the same class. */
.compact-upload.required-upload { border-color: #b9c5d8; background: #f7f9fc; color: var(--navy); }

/* Task 5 review, 22 Sep 2026: the two pickers in .intake-files (intake_files.html) are one group —
   the customer document and the optional Staff checklist — but only the document's carries
   `.required-upload`, and pcc.css:5592 is where its taller, stacked-label layout actually comes
   from (`flex-direction: column; gap: 5px; min-height: 76px`), not from anything `.required-upload`
   itself means semantically. Without this the optional picker fell back to base `.compact-upload`'s
   centred single row, so its label and hint ran together with no gap and the two controls read as
   different components stacked in the same group. `.required-upload` stays what the fix above
   already made it — a palette, not a layout — so this reaches every picker in the group instead. */
.intake-files .compact-upload { min-height: 76px; flex-direction: column; gap: 5px; }

/* Owner request (26 Sep 2026): the document card itself shows the upload happening, not just the
   submit button's spinner. Two states, choreographed on the card that holds the customer document:
   while the bytes travel a 3px stripe along the card's bottom edge fills to the real progress
   (htmx forms — upload-progress.js writes --upload and data-upload); with no progress to report
   (the plain-post creation form, and the server-side read after the bytes have landed) the stripe
   sweeps instead. The optional checklist card and the notes step back so the eye stays on the
   document. Linear timing on purpose: the sweep is continuous, not an entrance. */
form.submitting .intake-files, form.htmx-request .intake-files { pointer-events: none; }
form.submitting .intake-files > :not(:has(input[name="file"])),
form.htmx-request .intake-files > :not(:has(input[name="file"])) { opacity: .45; transition: opacity .2s ease-out; }
form.submitting .intake-files .compact-upload:has(input[name="file"]),
form.htmx-request .intake-files .compact-upload:has(input[name="file"]) {
  overflow: hidden; border-style: solid; border-color: var(--navy); background: #fff; color: var(--navy);
  box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent);
}
form.submitting .intake-files .compact-upload:has(input[name="file"])::after,
form.htmx-request .intake-files .compact-upload:has(input[name="file"])::after {
  content: "Mengunggah dan membaca dokumen…"; font-size: 11px; font-weight: 700; color: var(--navy);
  animation: pcc-fade-in .18s ease-out;
}
form[data-upload="progress"] .intake-files .compact-upload:has(input[name="file"])::after { content: "Mengunggah dokumen…"; }
form[data-upload="done"] .intake-files .compact-upload:has(input[name="file"])::after { content: "Terunggah · sedang dibaca…"; }
form.submitting .intake-files .compact-upload:has(input[name="file"])::before,
form.htmx-request .intake-files .compact-upload:has(input[name="file"])::before {
  content: ""; position: absolute; left: 0; bottom: 0; height: 3px; width: 40%;
  background: linear-gradient(90deg, transparent, var(--navy), transparent);
  animation: pcc-sweep 1.1s linear infinite;
}
form[data-upload="progress"] .intake-files .compact-upload:has(input[name="file"])::before {
  width: var(--upload, 0%); background: var(--navy); animation: none; transition: width .15s linear;
}
@keyframes pcc-sweep { from { transform: translateX(-100%); } to { transform: translateX(250%); } }
@keyframes pcc-fade-in { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  form.submitting .intake-files .compact-upload:has(input[name="file"])::before,
  form.htmx-request .intake-files .compact-upload:has(input[name="file"])::before { width: 100%; background: var(--navy); animation: none; }
  form.submitting .intake-files .compact-upload:has(input[name="file"])::after,
  form.htmx-request .intake-files .compact-upload:has(input[name="file"])::after { animation: none; }
  form.submitting .intake-files > :not(:has(input[name="file"])),
  form.htmx-request .intake-files > :not(:has(input[name="file"])) { transition: none; }
}

/* Whole-branch review, 22 Sep 2026: with no `.access-form`/`.change-form-grid` ancestor to inherit
   from, the label span fell to `.compact-upload`'s own 9px (pcc.css:3249) — smaller than the 11px
   hint right below it (`.compact-upload small` two rules down), so "Dokumen PQ customer" read as
   less prominent than its own hint text. The label is the primary line; give it back that role. */
.intake-files .compact-upload > span { font-size: 13px; }

/* pcc.css:5594 sets 8px; the `.change-form-grid` rule above outranks it wherever a picker actually
   renders, so the floor is raised there. This catches the rest. */
.compact-upload small { font-size: 11px; }

/* A note, not a field: no box, no shadow — see the exclusion note on the accent block above. */
.intake-format-note { border-left: 0; box-shadow: none; padding: 4px 0 0; }

/* The resumable reading (the reader's own note promises the result survives closing the page). */
.reader-resume { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin: 14px 0; padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface-soft); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.reader-resume > span { display: grid; gap: 3px; min-width: 0; }
.reader-resume small { color: var(--navy); font-size: 9px; font-weight: 900; letter-spacing: 0.08em; text-transform: uppercase; }
.reader-resume strong { color: var(--navy-deep); font-size: 15px; }
.reader-resume em { color: var(--muted); font-size: 12px; font-style: normal; line-height: 1.5; }

/* Work creation as a page (owner decision, 21 Sep 2026). The form kept its `.form-stack` and
   `.tender-meta-fields` from the modal; only the frame changed, so this is the frame. */
.new-tender-page > .page-header + form { display: grid; gap: 22px; margin-top: 22px; }
.new-tender-page .form-footer { display: flex; justify-content: flex-end; gap: 12px; border-top: 1px solid var(--line-soft); padding-top: 18px; }
@media (max-width: 640px) { .new-tender-page .form-footer { flex-direction: column-reverse; } .new-tender-page .form-footer > * { width: 100%; } }

/* Focus indicator — owner preference (2 Sep 2026): the prototype's 3px translucent rounded halo
   (pcc.css:117) reads "AI-product chic"; replaced app-wide with a crisp thin brand outline.
   Accessibility holds: this is HIGHER contrast than the 0.22-alpha ring it overrides — never
   remove the indicator, only restyle it. */
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
a:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--navy);
  outline-offset: 1px;
  box-shadow: none;
}

/* Accent language — owner decision (3 Sep 2026): no single-edge accent bars (left/top border or
   inset-shadow bars, "ChatGPT-style"). Every accent is now one uniform 1px border plus a colored
   soft shadow carrying the former bar color. Generated mechanically from every pcc.css rule with
   a >=2px border-left/top, a border-*-color variant, or an inset side bar (triangle/caret border
   hacks excluded); pcc.css itself stays untouched. Regenerate rather than hand-edit on a pcc.css
   refresh.

   One exclusion, added 21 Sep 2026: an element whose *only* border is the accent bar has no box
   to put a uniform border on, so the substitution turned a plain note into something that reads
   as a disabled input. `.intake-format-note` was the case that surfaced it; keep such elements
   out of a regenerated block and give them a borderless treatment below. */
.staff-guided-action { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.nav-item.active { box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.onboarding-steps article { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--blue-soft) 30%, transparent); }
.workflow-step { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--line) 30%, transparent); }
.workflow-step.done { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.workflow-step.current { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.staff-revision-alert { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.reader-error { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.batch-mapping-list article.mapped { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.batch-mapping-list article.unmapped { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.document-summary-card { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.document-summary-card.compact { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--blue-soft) 30%, transparent); }
.document-summary-role-focus { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.requirement-row.vault-locked { box-shadow: 0 2px 10px color-mix(in srgb, #5b846f 30%, transparent); }
.draft-source-banner.tender-template { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--blue-soft) 30%, transparent); }
.draft-source-banner.company-template { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #a07934 30%, transparent); }
.draft-source-banner.ai-assisted { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #77598b 30%, transparent); }
.reader-source-assurance p { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--blue-soft) 30%, transparent); }
.requirement-description-card { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.route-note { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #718b7f 30%, transparent); }
.hero-panel { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.priority-brief { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.milestone-card { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.metric-card.green { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.metric-card.amber { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.metric-card.red { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.metric-card.blue { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.document-control-strip { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.deletion-validation { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.review-queue-list > button.active { box-shadow: 0 2px 10px color-mix(in srgb, #527493 30%, transparent); }
.ai-finding-list > article { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #6685a1 30%, transparent); }
.ai-finding-list > article.warning { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #b1882b 30%, transparent); }
.ai-finding-list > article.critical { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #b54c56 30%, transparent); }
.identity-check-card { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #4f745f 30%, transparent); }
.identity-check-card.unreadable { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #9a721d 30%, transparent); }
.identity-check-card.mismatch { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.identity-check-card > blockquote { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, rgba(68, 91, 78, 0.2) 30%, transparent); }
.identity-check-legacy { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #9a721d 30%, transparent); }
.attachment-preview-list > button.active { box-shadow: 0 2px 10px color-mix(in srgb, #567765 30%, transparent); }
.role-model-card { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #6e9483 30%, transparent); }
.role-model-card.reviewer { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #577b9a 30%, transparent); }
.role-model-card.admin { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #7a608b 30%, transparent); }
.draft-source-banner.ai-assisted { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.role-model-card.admin { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.about-pcc-purpose { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.evidence-slot-card { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #d3dbe9 30%, transparent); }
.evidence-slot-card.complete { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #2d8b68 30%, transparent); }
.evidence-manual-override,.review-manual-override { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.addendum-readiness { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.addendum-list>article.open { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #bd7c20 30%, transparent); }
.addendum-list>article.resolved { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #23815d 30%, transparent); }
.help-purpose { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.help-evidence-guide-body aside p { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.help-faq p { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #9fb1d6 30%, transparent); }
.feedback-thanks { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.feedback-prompts blockquote { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #94a8d0 30%, transparent); }
.feedback-admin-kpis button.attention { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.feedback-admin-kpis button.risk { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.admin-triage article.critical { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.feedback-user-status p { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--blue) 30%, transparent); }
.feedback-progress .closed-message { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--muted) 30%, transparent); }
.renewal-trace { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #c68a1b 30%, transparent); }
.template-approval-trace { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.login-loading span { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.login-alert { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.login-alert.warning { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.checklist-ai-findings article { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--blue-soft) 30%, transparent); }
.checklist-ai-findings article.critical { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.checklist-ai-findings article.warning { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.delivery-readiness-grid > span { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #aab7c8 30%, transparent); }
.delivery-readiness-grid > span.ready { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.delivery-readiness-grid > span.attention { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.delivery-readiness-grid > span.blocked { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.package-version-chip { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #aab7c8 30%, transparent); }
.package-version-chip.submitted { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.package-version-chip.approved { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.package-version-chip.changes { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.package-release-gate { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #aab7c8 30%, transparent); }
.package-release-gate.submitted { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.package-release-gate.approved { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.package-release-gate.changes, .package-release-gate.blocked { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.delivery-receipt.recorded { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.inline-error { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.learning-status-chip { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #aab7c8 30%, transparent); }
.learning-status-chip.submitted { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.learning-status-chip.approved { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.learning-status-chip.changes { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.learning-gate-note { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.learning-metrics > span { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.learning-review-panel { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.learning-review-panel.approved { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.insight-engine { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--blue) 30%, transparent); }
.organization-playbook article { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.manual-checklist-gate { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #29479b 30%, transparent); }
.review-feedback-list article { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #3153a5 30%, transparent); }
.gap-findings article { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #7887a8 30%, transparent); }
.gap-findings article.critical { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.gap-findings article.warning { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #bd7c20 30%, transparent); }
.manual-gap-summary { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.manual-gap-summary > div > b { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #8290a8 30%, transparent); }
.manual-gap-summary > div > b.matched { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.manual-gap-summary > div > b.adjustment { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #bd7c20 30%, transparent); }
.manual-gap-summary > div > b.addition { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.manual-gap-summary > div > b.manual-only { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #8c6672 30%, transparent); }
.manual-gap-group article blockquote { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #d4deef 30%, transparent); }
.manual-gap-group.adjustment { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #bd7c20 30%, transparent); }
.manual-gap-group.addition { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.gap-decision-summary { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.sprint-monitor-signals > span { border-top: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #8190aa 30%, transparent); }
.sprint-monitor-signals > span.claimed { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.sprint-monitor-signals > span.attention { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #bd7c20 30%, transparent); }
.sprint-monitor-signals > span.ready { border-top-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.sprint-monitor-list > article { border-left: 1px solid var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #98a5bb 30%, transparent); }
.sprint-monitor-list > article.active { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.sprint-monitor-list > article.attention:not(.active) { border-left-color: var(--line); box-shadow: 0 2px 10px color-mix(in srgb, #bd7c20 30%, transparent); }
/* Account actions inside the mobile menu (Codex retest F01): the sidebar footer that carries them
   on desktop is hidden by pcc.css under 900px. */
.nav-account { display: none; }
@media (max-width: 900px) { .nav-account { display: block; } .nav-account .nav-item { width: 100%; } }
@media (max-width: 900px) { .nav-item.active { box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); } }

/* Design audit, 15 Sep 2026 — refinements that keep the prototype's markup and copy. */
/* Review Centre: the decision bar's status line ("Status approval" / "Keputusan Manager") is not the
   bar's first child in the multi-level layout, so pcc.css:5437 never stacked it and the label ran
   into its text. */
.multi-level-decision-bar > span { display: flex; min-width: 0; flex-direction: column; }
/* Meja Kerja: the Staff workbench sat flush against the priority list (0px in the prototype too). */
.staff-workbench { margin-top: 24px; }
/* Sidebar: Manager, Direksi and Admin carry eleven nav items, which pushed the account card below
   a 768–900px viewport with nothing to say the sidebar scrolls. The nav scrolls instead; the
   account card stays reachable. */
.sidebar { overflow: hidden; }
.primary-nav { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
/* Review Centre KPI numerals: pcc.css:5312 sets the mono stack; every other KPI on the site uses
   the sans, so the four cards read as a different typeface. */
.review-metric-grid strong { font-family: inherit; }
/* Dashboard signals: seven chips in a five-column grid left a ragged 5 + 2 second row. */
.portfolio-signals { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
/* Phones: the portfolio table and the Brankas filter strip scroll inside their box; a narrow
   fade on the right edge says so. */
@media (max-width: 900px) {
  .portfolio-table, .filter-tabs { mask-image: linear-gradient(90deg, #000 calc(100% - 24px), transparent); }
}
/* Akses & Peran: the inline reset form pushed every row to three lines; buttons on the first,
   the reset field on its own second line. */
.access-row-actions .access-reset-form { flex-basis: 100%; order: 1; }
/* Ubah kata sandi: "Keluar dari PCC" takes its own line so Batal and the primary button share one. */
.account-security-card .modal-footer .danger-text { flex-basis: 100%; }
.account-security-card .modal-footer .modal-footer-spacer { display: none; }
/* Identity check card: the Reviewer's manual validation line (owner UX allowance, 14 Sep 2026). */
.identity-check-card .identity-manual-validation { display: flex; align-items: center; gap: 8px; margin: 8px 0 0; color: var(--green); font-size: 10px; font-weight: 800; }
.identity-check-card .identity-manual-validation i { width: 8px; height: 8px; border-radius: 50%; background: var(--green); }
/* Tender switcher: the prototype's per-row delete is a <button>; here it is a link to the deletion
   screen (PCC:app/tender-workspace.tsx:2369), so it needs the centring a button had for free, and
   the waiting state is aria-disabled on a <span> because :disabled is button-only. */
.switcher-delete { display: flex; align-items: center; justify-content: center; text-align: center; text-decoration: none; }
.switcher-delete[aria-disabled="true"] { cursor: not-allowed; background: #f7f8fa; color: #7a8290; }
.switcher-delete[aria-disabled="true"]:hover { background: #f7f8fa; }
/* "Buat pekerjaan baru" (PCC:app/tender-intake-modal.tsx) as SSR. One React-computed thing stays
   a selector on this markup — the modal's own :target left with the page it opened on (21 Sep
   2026) — `selected` is :has(:checked), and the labels that followed workType are sibling spans,
   one of which each pair of :has() rules reveals. The checklist-route pair (intakeMode) left with
   the route question itself (21 Sep 2026): one submit now takes both files, so the submit button
   carries no route-dependent label. */
.intake-mode-options > label:has(input:checked) { border-color: var(--navy); box-shadow: 0 0 0 3px rgba(41, 71, 155, .1); background: linear-gradient(145deg, #fff, #f3f6fc); }
.tender-trial-toggle:has(input:checked) { border-color: #d3a449; background: #fff9ea; }
.tender-intake-form i { font-style: normal; }
.tender-intake-form .for-pq, form:has([name="work_type"][value="pq"]:checked) .tender-intake-form .for-tender { display: none; }
form:has([name="work_type"][value="pq"]:checked) .tender-intake-form .for-pq { display: inline; }
/* Ringkasan (PCC:app/tender-workspace.tsx:3126-3232): the prototype's blocker rows were <button
   onClick={navigate}>, so pcc.css:1000 scopes them to `button`. Here they navigate, which is what
   an anchor is for; these three rules give the anchor the button's box. */
.blocker-list > a { display: grid; grid-template-columns: 20px minmax(0, 1fr) 10px; align-items: center; gap: 12px; border: 0; border-top: 1px solid var(--line-soft); background: transparent; padding: 14px 0; color: inherit; text-align: left; text-decoration: none; }
.blocker-list > a:first-child { border-top: 0; padding-top: 0; }
.blocker-list > a > span:nth-child(2) { display: flex; flex-direction: column; }
/* Same swap for the category rows and the readiness/priority links. */
.category-list > a.category-progress, .priority-brief > a.button, .milestone-card > a.button { text-decoration: none; }
/* The Ringkasan hero's red tab (pcc.css:6043) is a 4px single-edge accent bar on the right edge —
   the same thing the generated block above removes everywhere else. The generator skips pseudo-
   elements, so it is dropped here by hand; the hero already carries its soft navy shadow. */
.hero-panel::after { display: none; }
/* PDF zoom (PCC:app/pdf-attachment-viewer.tsx:31): pcc.css caps a page at the container width, which
   is right until the reader deliberately zooms past it. */
.pdf-preview-pages[data-zoom] { overflow-x: auto; }
.pdf-preview-pages[data-zoom] .pdf-preview-page { max-width: none; }
/* Batch upload progress (PCC:app/batch-document-upload.tsx:148) — the prototype's `busy` state is
   htmx's own in-flight class here, so the panel needs no script of its own. */
.batch-progress { display: none; }
form.htmx-request .batch-progress, form.submitting .batch-progress { display: flex; }
.batch-progress .spinner { width: 16px; height: 16px; flex: 0 0 auto; border: 2px solid var(--navy); border-right-color: transparent; border-radius: 50%; animation: pcc-spin .7s linear infinite; }
@media (prefers-reduced-motion: reduce) { .batch-progress .spinner { animation-duration: 2s; } }
/* The official-checklist gate (PCC:app/checklist-approval-panel.tsx:180) had one control on its
   right: a <label> styled as a button that uploaded on change. Without JavaScript the picker and
   the submit are two controls, so they share that one slot as a stack. */
.manual-checklist-gate .gate-upload { display: grid; flex: 0 0 auto; gap: 6px; justify-items: stretch; min-width: 210px; }
.manual-checklist-gate .compact-upload { flex-direction: column; gap: 1px; padding: 6px 12px; }
/* Owner UX round, 18 Sep 2026: the repair form's optional picker gets the required picker's
   presence (pcc.css:5592) in the accent language of port.css, so it reads as the place to act. */
.compact-upload.repair-upload { min-height: 76px; flex-direction: column; gap: 5px; padding: 10px 14px; border: 1px dashed var(--navy); background: #f3f6fc; text-align: center; }
.compact-upload.repair-upload > span { font-size: 10px; }
.compact-upload.repair-upload > small { color: var(--muted); }
.manual-checklist-gate .compact-upload small { color: var(--muted); font-weight: 600; }
/* Checklist board stage note (owner UX report, 15 Sep 2026). Modelled on the prototype's
   `.manual-order-notice` box but kept a separate class: same shape, different component — reusing
   that one would dress this message in rules written for the manual-order lane. */
.checklist-stage-note { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; border: 1px solid var(--line); border-radius: 12px; background: var(--blue-bg); padding: 13px 16px; box-shadow: 0 2px 10px color-mix(in srgb, var(--navy) 30%, transparent); }
.checklist-stage-note > span { display: grid; width: 28px; height: 28px; flex: 0 0 auto; place-items: center; border-radius: 50%; background: var(--navy); color: #fff; font-size: 10px; font-weight: 900; }
.checklist-stage-note > p { display: grid; flex: 1 1 auto; gap: 3px; margin: 0; min-width: 0; }
.checklist-stage-note strong { color: var(--navy-deep); font-size: 12px; }
.checklist-stage-note small { color: var(--muted); font-size: 10px; line-height: 1.5; }
.checklist-stage-note > .button { flex: 0 0 auto; }
@media (max-width: 720px) { .checklist-stage-note { align-items: stretch; flex-direction: column; } }
.checklist-stage-note.attention { border-color: #efc3cc; background: var(--red-bg); box-shadow: 0 2px 10px color-mix(in srgb, var(--red) 30%, transparent); }
.checklist-stage-note.attention > span { background: var(--red); }

/* --- Evidence folder upload (owner decision, 16 Sep 2026) --------------------------------------- */
/* SSR adaptation: the prototype had no folder picker, so this note is the port's own element. */
.evidence-folder-note { margin: -4px 0 0; color: var(--muted); font-size: 11px; line-height: 1.6; }

/* --- Validation gate rows lead somewhere (owner request, 16 Sep 2026) --------------------------- */
/* SSR adaptation: the prototype's rows were <button>s (pcc.css:7519). An anchor navigates without
   JavaScript, so it repeats those rules rather than re-styling the panel. */
.checklist-validation-panel > div > a {
  display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 11px; border: 1px solid rgba(197, 48, 48, 0.2); border-radius: 9px;
  background: #fff; color: inherit; cursor: pointer; text-decoration: none;
}
.checklist-validation-panel > div > a span { display: grid; gap: 2px; }
.checklist-validation-panel > div > a:hover,
.checklist-validation-panel > div > a:focus-visible {
  border-color: rgba(197, 48, 48, 0.48); box-shadow: 0 0 0 3px rgba(197, 48, 48, 0.08);
}
.checklist-validation-panel > div > a:focus-visible { outline: 2px solid var(--navy); outline-offset: 1px; }

/* --- Validation gate rows lead somewhere (owner request, 16 Sep 2026) --------------------------- */
/* SSR adaptation: the prototype's rows were <button>s (pcc.css:7519). An anchor navigates without
   JavaScript, so it repeats those rules rather than re-styling the panel. */
.checklist-validation-panel > div > a {
  display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 11px; border: 1px solid rgba(197, 48, 48, 0.2); border-radius: 9px;
  background: #fff; color: inherit; cursor: pointer; text-decoration: none;
}
.checklist-validation-panel > div > a span { display: grid; gap: 2px; }
.checklist-validation-panel > div > a:hover,
.checklist-validation-panel > div > a:focus-visible {
  border-color: rgba(197, 48, 48, 0.48); box-shadow: 0 0 0 3px rgba(197, 48, 48, 0.08);
}
.checklist-validation-panel > div > a:focus-visible { outline: 2px solid var(--navy); outline-offset: 1px; }

/* --- Bulk compliance assist (owner request, 16 Sep 2026) ---------------------------------------- */
/* Not a prototype screen, so it carries its own names rather than borrowing a pcc.css component. */
.compliance-assist-intro { display: grid; margin-bottom: 14px; padding: 13px 15px; border: 1px solid var(--line); border-radius: 12px; background: var(--navy-bg); }
.compliance-assist-intro small { color: var(--muted); font-size: 8.5px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; }
.compliance-assist-intro strong { color: var(--navy-deep); font-size: 11px; }
.compliance-assist-intro em { color: var(--muted); font-size: 9.5px; font-style: normal; line-height: 1.6; }
.compliance-assist-list { display: grid; gap: 8px; }
.compliance-assist-list > article { display: grid; grid-template-columns: 92px minmax(0, 1fr) auto; align-items: start; gap: 12px; padding: 11px 13px; border: 1px solid var(--line); border-radius: 11px; background: #fff; }
.compliance-assist-list > article.found { border-color: #c7dbc9; box-shadow: 0 2px 10px color-mix(in srgb, var(--green) 30%, transparent); }
.compliance-assist-list > article.missing { border-color: #e7d6ae; box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.compliance-assist-list > article > div { display: grid; min-width: 0; gap: 4px; }
.compliance-assist-list strong { color: var(--navy-deep); font-size: 10.5px; }
.compliance-assist-list small { color: var(--muted); font-size: 8.5px; }
.compliance-assist-clause { margin: 0; color: var(--ink); font-size: 9.5px; }
.compliance-assist-clause.none { color: var(--amber-ink, var(--muted)); }
.compliance-assist-clause em { color: var(--muted); font-style: normal; }
.compliance-assist-list blockquote { margin: 0; padding: 6px 9px; border-left: 1px solid var(--line); border-radius: 0 7px 7px 0; background: #f8fafc; color: var(--muted); font-size: 8.5px; line-height: 1.6; }
.compliance-assist-pick { display: grid; grid-template-columns: 15px auto; align-items: center; gap: 7px; color: var(--navy-deep); font-size: 9px; font-weight: 800; }
.compliance-assist-footer { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-top: 14px; }
.compliance-assist-footer p { max-width: 62ch; margin: 0; color: var(--muted); font-size: 9px; line-height: 1.6; }
@media (max-width: 720px) {
  .compliance-assist-list > article { grid-template-columns: minmax(0, 1fr); }
  .compliance-assist-footer { align-items: stretch; flex-direction: column; }
}
.checklist-validation-bulk { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; }
.checklist-validation-bulk small { color: var(--muted); font-size: 8.5px; line-height: 1.6; }
.compliance-assist-found { color: var(--navy-deep); font-size: 8.5px; line-height: 1.6; }
.compliance-assist-found.none { color: var(--muted); }

/* --- Clause reconciliation (owner request, 16 Sep 2026) ----------------------------------------
   Own names, not a pcc.css component's: this screen has no prototype counterpart, and reusing a
   component's classes for a different structure dresses it in that component's rules. The two-side
   row deliberately reads like the Review Centre's source grid, because it is the same act — our
   checklist on the left, the customer's document on the right. The citation is the one thing this
   page exists to confirm, so it is the one thing set in the system's mono face. */
.clause-source-bar { display: flex; align-items: baseline; justify-content: space-between; gap: 18px; flex-wrap: wrap; margin-bottom: 14px; padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface-soft); }
.clause-source-bar span { display: grid; min-width: 0; gap: 3px; }
.clause-source-bar small { color: var(--muted); font-size: 8px; font-weight: 800; letter-spacing: 0.07em; text-transform: uppercase; }
.clause-source-bar strong { overflow: hidden; color: var(--navy-deep); font-size: 11px; text-overflow: ellipsis; }
.clause-source-bar.empty { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; border-color: #e7d6ae; background: var(--amber-bg); box-shadow: 0 2px 10px color-mix(in srgb, var(--amber) 30%, transparent); }
.clause-source-bar.empty p { grid-column: 1; margin: 4px 0 0; color: var(--muted); font-size: 9px; line-height: 1.6; }
.clause-source-bar.empty > .button { grid-column: 2; grid-row: 1 / span 2; }

.clause-group { margin-bottom: 14px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--panel); overflow: hidden; }
.clause-group > header { display: flex; min-height: 44px; align-items: center; gap: 12px; padding: 0 16px; border-bottom: 1px solid var(--line); background: #fafbfc; }
.clause-group > header h2 { margin: 0; color: var(--navy-deep); font-size: 12px; }
.clause-group > header > i { height: 1px; flex: 1; background: var(--line); }
.clause-group > header > span:last-child { color: var(--muted); font-family: var(--font-mono), monospace; font-size: 9px; }
.clause-group-code { display: grid; width: 22px; height: 22px; place-items: center; border-radius: var(--radius-sm); background: var(--navy); color: #fff; font-size: 8px; font-weight: 800; }

.clause-head { display: grid; grid-template-columns: 52px minmax(0, 1fr); border-bottom: 1px solid var(--line); background: #fafbfc; }
.clause-head > div { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); }
.clause-head span { padding: 7px 15px; color: var(--muted); font-size: 8px; font-weight: 800; letter-spacing: 0.07em; text-transform: uppercase; }
.clause-head > span { border-right: 1px solid var(--line-soft); padding-inline: 4px; font-size: 7px; text-align: center; }

.clause-row { display: grid; grid-template-columns: 52px minmax(0, 1fr); align-items: stretch; border-top: 1px solid var(--line-soft); }
.clause-group > .clause-row:first-child { border-top: 0; }
.clause-accept { display: grid; place-items: center; border-right: 1px solid var(--line-soft); background: var(--surface-soft); }
.clause-accept input { width: 14px; height: 14px; cursor: pointer; }
.clause-pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); min-width: 0; background: #fafaf7; }
.clause-pair > article { min-width: 0; padding: 11px 15px; }
.clause-pair > article:first-child { border-right: 1px solid var(--line); background: var(--panel); }
/* The column heading carries these words once; the per-row copies return when the pair stacks. */
.clause-pair small { display: none; color: var(--muted); font-size: 8px; font-weight: 800; letter-spacing: 0.07em; text-transform: uppercase; }
.clause-pair strong { display: block; color: var(--navy-deep); font-size: 10.5px; line-height: 1.45; }
.clause-pair strong a { color: inherit; text-decoration: none; }
.clause-pair strong a:hover { text-decoration: underline; }
.clause-pair p { margin: 5px 0 0; color: var(--muted); font-size: 9px; line-height: 1.55; }
.clause-pair em { display: block; margin-top: 5px; color: var(--muted); font-size: 8.5px; font-style: normal; }
.clause-file { margin-top: 4px !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The citation — the address this page exists to confirm, and the only mono on the screen. It is a
   field, because a proposal Staff cannot correct is a verdict, and this screen does not give those. */
.clause-cite { width: 100%; margin-top: 4px; border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 6px 8px; background: var(--panel); color: var(--navy-deep); font-family: var(--font-mono), monospace; font-size: 10.5px; letter-spacing: -0.01em; }
.clause-cite::placeholder { color: #9aa3b2; font-family: var(--font-sans), "Open Sans", sans-serif; }
.clause-cite[readonly] { background: var(--surface-soft); color: var(--muted); }
.clause-pair b { color: var(--green); font-size: 8.5px; }
.clause-none { color: var(--amber); }
.clause-pair > article:first-child > .button { margin-top: 7px; }
.clause-row.unmatched .clause-pair { background: var(--amber-bg); }

/* The confirmation after applying — a result, not a refusal. */
.clause-applied { margin-bottom: 14px; border: 1px solid rgba(35, 97, 72, 0.22); border-radius: var(--radius-lg); padding: 12px 16px; background: var(--green-bg); }
.clause-applied strong { color: var(--ink); font-size: 11px; }
.clause-applied p { margin: 4px 0 0; color: var(--muted); font-size: 9px; line-height: 1.6; }

.clause-apply-bar { position: sticky; bottom: 0; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 16px; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--panel); box-shadow: var(--shadow); }
.clause-apply-bar p { max-width: 68ch; margin: 0; color: var(--muted); font-size: 9px; line-height: 1.6; }

@media (max-width: 860px) {
  .clause-head { display: none; }
  .clause-pair small { display: block; }
  .clause-pair strong { margin-top: 4px; }
  .clause-pair { grid-template-columns: minmax(0, 1fr); }
  .clause-pair > article:first-child { border-right: 0; border-bottom: 1px solid var(--line); }
  .clause-apply-bar { align-items: stretch; flex-direction: column; }
  .clause-source-bar.empty { grid-template-columns: minmax(0, 1fr); }
  .clause-source-bar.empty > .button { grid-column: 1; grid-row: auto; justify-self: start; }
}

/* --- One card for the next step (owner request, 16 Sep 2026) ------------------------------------
   Replaces four blocks that each answered "what do I do now": the next-step banner, the draft half
   of the board's stage note, the validation list and the submit block. Own names, because no
   prototype component had this shape. */
.checklist-submit-card { display: grid; gap: 11px; padding: 15px 20px; border-top: 1px solid var(--line); background: var(--surface-soft); }
.checklist-submit-card.attention { background: #fff8f8; }
.checklist-submit-head { display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 11px; align-items: start; }
.checklist-submit-head > span:first-child { display: grid; width: 30px; height: 30px; place-items: center; border-radius: 50%; background: var(--navy); color: #fff; font-size: 11px; font-weight: 850; }
.checklist-submit-card.attention .checklist-submit-head > span:first-child { background: var(--red); }
.checklist-submit-head > span:last-child { display: grid; gap: 4px; min-width: 0; }
.checklist-submit-head small { color: var(--muted); font-size: 9px; font-weight: 800; letter-spacing: 0.07em; text-transform: uppercase; }
.checklist-submit-head strong { color: var(--ink); font-size: 15px; }
.checklist-submit-head p { margin: 0; color: var(--muted); font-size: 12px; line-height: 1.6; }
.checklist-issue-list { border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--panel); }
.checklist-issue-list > summary { display: flex; align-items: center; justify-content: space-between; padding: 9px 12px; color: var(--navy); cursor: pointer; font-size: 11px; font-weight: 700; list-style: none; }
.checklist-issue-list > summary::-webkit-details-marker { display: none; }
.checklist-issue-list[open] > summary { border-bottom: 1px solid var(--line-soft); }
.checklist-issue-list .checklist-validation-panel { padding: 11px 12px; border-bottom: 0; background: transparent; }
.checklist-issue-list .checklist-validation-panel > div { padding-left: 0; }
.checklist-submit-actions { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.checklist-submit-note { color: var(--muted); font-size: 10px; line-height: 1.6; }
/* The row carries its own flag now that the list is a disclosure. */
.reference-missing-tag { border: 1px solid #efc3cc; border-radius: 999px; padding: 1px 7px; background: var(--red-bg); color: var(--red); font-size: 8px; font-style: normal; font-weight: 800; }
/* Akurasi design §5 (25 Sep 2026): a Staff row the reading could not pair is flagged, never
   deleted. Amber, not red — it is a question for Staff, not a defect. Same pill as the
   reference-missing tag so the two read as one family. */
.reference-flag-tag { border: 1px solid color-mix(in srgb, var(--amber) 45%, white); border-radius: 999px; padding: 1px 7px; background: var(--amber-bg); color: var(--amber); font-size: 8px; font-style: normal; font-weight: 800; }

/* --- The board as a verification ledger (owner review, 16 Sep 2026) ----------------------------
   While a checklist is under review, a reviewing role's row carries the customer clause instead
   of Staff's execution line, in the same mono address form the clause page uses — what Staff
   filled in and what the Reviewer verifies share one shape. */
.row-clause { font-family: var(--font-mono), monospace; letter-spacing: -0.01em; }
.requirement-summary > small .row-clause { color: var(--navy-deep); }
.row-clause.none { color: var(--amber); font-family: var(--font-sans), "Open Sans", sans-serif; }
/* A status mark for a reader who cannot change it: same look, no button affordance. */
.status-button.static { cursor: default; }

/* Stage 2 (port copy — no prototype component): the AI reader's markers on preview rows. */
.reader-ai-badge {
  display: inline-block; padding: 1px 7px; border: 1px solid var(--navy); border-radius: 4px;
  font-size: 10px; font-weight: 700; letter-spacing: .02em; line-height: 16px; color: var(--navy);
  white-space: nowrap;
}
.reader-ai-badge.corroborated { border-color: var(--green); color: var(--green); }
.reader-ai-badge.required { border-color: var(--line); color: var(--muted); }
.reader-unmatched .reader-result-header { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 12px 14px; min-height: 0; cursor: default; }
.reader-unmatched .reader-result-header:hover { background: transparent; }
.reader-unmatched .reader-result-header strong { font-weight: 600; font-size: 12px; }
.reader-unmatched .reader-result-header em { color: var(--muted); font-size: 11px; font-style: normal; }
/* The model's caveats about the whole reading: a quiet aside in the same voice as the gap checks
   (flat white card, 1px line), not a shadowed panel of its own. */
.reader-ai-notes {
  display: grid; gap: 6px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px;
  background: #fff;
}
.reader-ai-notes > small { color: var(--navy); font-size: 9px; font-weight: 900; letter-spacing: .08em; text-transform: uppercase; }
.reader-ai-notes ul { margin: 0; padding-left: 16px; list-style: disc; }
.reader-ai-notes li { color: var(--ink); font-size: 12px; line-height: 1.5; }
.reader-ai-notes li + li { margin-top: 3px; }

/* The materials section, shared by the creation page and the intake panel's recovery state. */
.intake-files { display: grid; gap: 8px; }
.intake-files > .compact-upload + .intake-files-note { margin: 0 0 14px; color: var(--muted); font-size: 13px; line-height: 1.5; }
/* The tender/PQ word inside the shared partial. port.css:804-805 scopes the existing pair to
   `.tender-intake-form`, so the same spans rendered anywhere else show BOTH words ("Dokumen
   tenderPQ customer"). Two sources decide it: on the creation page the radio on the same form,
   through :has(); in the intake panel the tender's stored type, which Task 4 puts on the form as
   `work-tender` / `work-pq`. */
.intake-files i { font-style: normal; }
.intake-files .for-pq,
form:has([name="work_type"][value="pq"]:checked) .intake-files .for-tender,
.work-pq .intake-files .for-tender { display: none; }
form:has([name="work_type"][value="pq"]:checked) .intake-files .for-pq,
.work-pq .intake-files .for-pq { display: inline; }
