/* Foundation tokens live in the A1 block below. Existing layout rules consume
   compatibility aliases; there is no separate legacy palette. */
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font-size:1rem;line-height:1.5}button,input,select,textarea{font:inherit}button,a,input,select,textarea{-webkit-tap-highlight-color:transparent}[hidden]{display:none!important}a{color:var(--accent)}textarea{min-height:88px;resize:vertical}input::placeholder{color:var(--muted);opacity:1}label{display:block;font-size:.9375rem;font-weight:600}label>input,label>select,label>textarea{margin-top:.4rem}h1,h2,h3,p{margin-top:0}.account,.actions{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.workspace-nav{display:flex;align-items:center;justify-content:space-between;padding:1.2rem 0 1.6rem;color:var(--muted);font-size:.875rem}.workspace-nav strong{color:var(--ink);font-weight:500}.search-field{flex:1}.filters label:not(.search-field){min-width:190px}.filters button{margin-bottom:0}.reference{font-size:.8125rem;color:var(--muted);font-weight:500;display:block;margin-bottom:.3rem}progress{display:block;width:100%;height:5px;border:0;background:var(--line);accent-color:var(--accent)}progress::-webkit-progress-bar{background:var(--line)}progress::-webkit-progress-value{background:var(--accent)}.state button{margin-top:1rem}.error{overflow-wrap:anywhere}.notice{position:fixed;bottom:1.5rem;right:3.5%;max-width:min(440px,93vw);background:var(--ink);color:var(--surface);padding:1rem 1.3rem;border-radius:6px;z-index:4}.history-section{margin-top:2.5rem}.history-item{display:grid;grid-template-columns:140px 1fr auto;gap:1.2rem;padding:1rem 0;border-bottom:1px solid var(--line)}.history-item p{margin:.25rem 0}.history-item time{font-size:.875rem;color:var(--muted)}.history-item .reason{color:var(--muted);font-size:.9375rem;white-space:pre-wrap;overflow-wrap:anywhere}.history-action{align-self:start}.history-heading{display:flex;justify-content:space-between;gap:1rem;align-items:center}.login-view{display:grid;grid-template-columns:1.2fr 1fr;gap:7%;align-items:center;min-height:70vh;max-width:1050px;margin:auto;padding:3rem 0}.login-intro h1{font-size:clamp(2.7rem,5vw,4.1rem);font-weight:500;line-height:1.15;margin:1.1rem 0 1.4rem}.login-intro h1 em{font:italic 1.08em var(--font-ui);color:var(--accent)}.login-intro>p:not(.eyebrow){max-width:370px;color:var(--muted)}.process-label{font-size:.875rem;padding-top:1.1rem;border-top:1px solid var(--line);display:flex;flex-wrap:wrap;gap:.6rem;color:var(--muted)}.process-label span{color:var(--accent)}.login-form{background:var(--surface);padding:2.1rem}.login-form>p{color:var(--muted)}.login-form .primary{width:100%;margin-top:.6rem}.login-form input{margin-top:.5rem}dialog{background:var(--surface);color:var(--ink);padding:1.5rem;width:min(650px,calc(100% - 2rem));max-height:90dvh}dialog::backdrop{background:#1119}.dialog-heading{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:1rem}.dialog-heading h2{margin:0}.form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1rem}.form-grid>*{min-width:0;overflow-wrap:anywhere}.form-grid .full{grid-column:1/-1}fieldset{padding:0;border:0;margin:0;min-width:0}.form-actions{display:flex;justify-content:flex-end;gap:.75rem;margin-top:1.5rem}.form-info{background:var(--bg);padding:1rem;font-size:.9375rem;margin-bottom:1rem;white-space:pre-wrap}.line-input{display:grid;grid-template-columns:minmax(0,1fr) 100px auto;gap:.5rem;align-items:end;margin:.75rem 0}.line-input>button{padding:.6rem}.product-list{max-height:260px;overflow:auto;margin-bottom:1.2rem}.product-item{display:flex;justify-content:space-between;gap:1rem;border-bottom:1px solid var(--line);padding:.6rem 0;overflow-wrap:anywhere}.product-item strong{display:block}.skip-link{position:absolute;top:-80px;background:var(--surface);padding:1rem;z-index:10}.skip-link:focus{top:0}
.check-field{display:flex;align-items:center;gap:.75rem;min-height:var(--touch-target-min);padding:.55rem .7rem;border:1px solid var(--line);border-radius:4px}.check-field input{width:22px;min-height:22px;height:22px;margin:0;flex:0 0 22px;accent-color:var(--accent)}
@media(max-width:650px){.workspace-nav{padding:1rem 0;font-size:.8125rem}.page-heading>.actions{width:100%}.page-heading .primary{flex:1}.filters{display:grid;grid-template-columns:1fr auto}.search-field{grid-column:1/-1}.filters label:not(.search-field){min-width:0}.pagination{align-items:start;flex-direction:column}.pagination .actions{width:100%;justify-content:space-between}.history-item{grid-template-columns:1fr;gap:.25rem}.history-action{justify-self:start}.history-heading{align-items:start;flex-direction:column}.login-view{grid-template-columns:1fr;gap:1.8rem;padding:2rem 0}.login-intro h1{font-size:2.4rem}.login-intro h1 br{display:none}.login-intro h1 br::after{content:' '}.login-intro h1 em{display:block}.login-form{padding:1.4rem}.login-intro>p:not(.eyebrow),.process-label{display:none}.form-grid{grid-template-columns:minmax(0,1fr)}.line-input{grid-template-columns:minmax(0,1fr) 80px auto}.line-input label{font-size:.8125rem}.line-input>button{font-size:.8125rem}dialog{padding:1rem}.form-actions button{flex:1}.form-actions{flex-wrap:wrap}.notice{bottom:1rem}.history-heading button{align-self:start}}.issue-item{padding:20px 0;border-bottom:1px solid var(--line);overflow-wrap:anywhere}.issue-heading{display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:space-between}.issue-item p{margin:10px 0}

.workspace-nav .actions{flex-wrap:wrap;justify-content:flex-end}.activity-item{grid-template-columns:90px minmax(0,1fr)}.activity-item p{margin:8px 0}.activity-item .order-title{font-size:inherit}.activity-item{overflow-wrap:anywhere}@media(max-width:600px){.workspace-nav{align-items:flex-start;gap:12px;flex-wrap:wrap}.activity-item{grid-template-columns:1fr}}
@media(max-width:650px){#activity-form{grid-template-columns:minmax(0,1fr)}#activity-form button{width:100%}}

#activity-form{flex-wrap:wrap}.activity-item{grid-template-columns:135px minmax(0,1fr)}@media(max-width:600px){.activity-item{grid-template-columns:1fr}}

/* A6.1: the Produksi filter form was a three-column stacked grid here. It is removed rather than overridden because `#search-form` is an id and would have out-specified the A6 `.command-bar` it is now composed from, keeping the old grid alive underneath the command bar. Every selector in the deleted block was scoped to `#search-form`, which only the Produksi board has ever used. */
.material-event{padding:1rem 0;border-bottom:1px solid var(--line);overflow-wrap:anywhere}.material-event p{margin:.4rem 0}.material-event .reason{white-space:pre-wrap}
.ai-prompts{margin:1rem 0}.ai-prompts button{font-size:.875rem}.ai-assumptions{margin:1rem 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:.75rem 0}.ai-assumptions summary{cursor:pointer;font-weight:600}.ai-assumptions .form-grid{margin-top:1rem}.ai-answer{margin:1.5rem 0;padding:1.2rem;background:var(--accent-bg);border-top:3px solid var(--accent);overflow-wrap:anywhere}.ai-answer h3{margin-bottom:.35rem}.ai-answer p:last-child{margin-bottom:0}
.command-attention{border-left:3px solid var(--edge);padding:1rem 0 1rem 1.2rem;overflow-wrap:anywhere}.command-attention.critical{border-left-color:var(--danger)}.command-attention.warning{border-left-color:var(--accent)}.command-attention p{margin:.35rem 0}.command-snapshot{min-width:0}.command-snapshot h3{margin-bottom:.35rem}.command-values dt{color:var(--muted);min-width:0}.command-values dd{margin:0;text-align:right;font-variant-numeric:tabular-nums;min-width:0;overflow-wrap:anywhere}@media(max-width:650px){#command-center-summary{grid-template-columns:minmax(0,1fr)}#command-center-summary dd{overflow-wrap:anywhere}.command-values{grid-template-columns:minmax(0,1fr)}.command-values dd{text-align:left}}
.audit-json{white-space:pre-wrap;overflow-wrap:anywhere;background:var(--bg);border:1px solid var(--line);padding:1rem;max-height:300px;overflow:auto;font:0.8125rem/1.5 ui-monospace,SFMono-Regular,Consolas,monospace}.audit-event{padding:1rem 0;border-bottom:1px solid var(--line);overflow-wrap:anywhere}.audit-event p{margin:.35rem 0}
.bundle-label{display:grid;grid-template-columns:150px minmax(0,1fr);gap:1rem;align-items:center;border:2px solid var(--ink);padding:1rem;margin:1rem 0;background:#fff;color:#111}.bundle-label img{display:block;width:150px;height:150px}.bundle-label strong,.bundle-label span{display:block;overflow-wrap:anywhere}.bundle-label strong{font-size:1.25rem}.bundle-label .bundle-label-qty{font-size:1.1rem;font-weight:600;margin:.35rem 0}@media(max-width:450px){.bundle-label{grid-template-columns:minmax(0,1fr);text-align:center}.bundle-label img{margin:auto}}
@media print{body>*{display:none!important}#dialog[open]{display:block!important;position:static;width:auto;max-height:none;margin:0;padding:0;border:0;box-shadow:none}#dialog[open]>*{display:none!important}#dialog[open] #dialog-content{display:block!important}#dialog-content>*{display:none!important}#dialog-content .bundle-label{display:grid!important;width:80mm;margin:0;border:1.5pt solid #000;padding:5mm;break-inside:avoid}/* A printed label is a static artefact: the entry animation must not freeze the surface at
   opacity 0 in the snapshot, and an in-flight close must not be captured half-faded. */
#dialog[open],#dialog[open]::backdrop{animation:none!important;transition:none!important;opacity:1!important;transform:none!important}}
.dialog-heading{flex-wrap:wrap}.dialog-heading h2,.form-info{overflow-wrap:anywhere}
.bom-line{display:grid;grid-template-columns:minmax(0,1fr) 130px auto;gap:.6rem;align-items:end;margin-bottom:1rem}.bom-line>div{min-width:0}.requirement-values{margin:.6rem 0}.requirement-values>div{display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap}.requirement-values dd{margin:0;font-variant-numeric:tabular-nums}.product-item{flex-wrap:wrap}.product-item>div{min-width:0}@media(max-width:650px){.bom-line{grid-template-columns:minmax(0,1fr)}.bom-line button{justify-self:start}}
.workforce-heading{display:flex;align-items:start;justify-content:space-between;gap:1rem;margin-bottom:1.25rem}.workforce-heading p{margin-bottom:.25rem}.workforce-summary{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));border-top:2px solid var(--ink);border-bottom:1px solid var(--line);margin:1.5rem 0}.workforce-summary div{padding:1rem;border-right:1px solid var(--line);min-width:0}.workforce-summary div:last-child{border-right:0}.workforce-summary dt{color:var(--muted);font-size:.8125rem}.workforce-summary dd{font-size:1.35rem;font-weight:600;margin:.25rem 0 0;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.workforce-row{display:grid;grid-template-columns:minmax(0,1fr) minmax(190px,auto);gap:1.25rem;padding:1.15rem 0;border-bottom:1px solid var(--line);overflow-wrap:anywhere}.workforce-row h3,.workforce-row p{margin-bottom:.3rem}.workforce-row-actions{display:flex;align-items:center;justify-content:flex-end;gap:.5rem;flex-wrap:wrap}.workforce-master{max-height:60dvh}dialog:has(.workforce-screen){width:min(960px,calc(100% - 2rem))}@media(max-width:800px){.workforce-summary{grid-template-columns:repeat(3,minmax(0,1fr))}.workforce-summary div:nth-child(3){border-right:0}.workforce-summary div:nth-child(-n+3){border-bottom:1px solid var(--line)}}@media(max-width:650px){.workforce-heading,.workforce-row{grid-template-columns:minmax(0,1fr);display:grid}.workforce-heading .actions,.workforce-row-actions{justify-content:flex-start}.workforce-row-actions button{flex:1}.workforce-summary{grid-template-columns:repeat(2,minmax(0,1fr))}.workforce-summary div:nth-child(3){border-right:1px solid var(--line)}.workforce-summary div:nth-child(2n){border-right:0}.workforce-summary div:nth-child(-n+4){border-bottom:1px solid var(--line)}}



/* ==========================================================================
   Apple-27 A1 foundation. DESIGN.md defines the current design authority.
   Existing workspace composition retains the historical Shopeers layout.
   Materials are solid by default; A4 may enhance functional chrome later.
   ========================================================================== */
:root{
  --font-ui:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,"Noto Sans",sans-serif;
  font-family:var(--font-ui);
  color-scheme:light;font-synthesis:none;
  --type-page-title:1.75rem;--type-section-title:1.125rem;--type-card-title:1rem;
  --type-body:.9375rem;--type-control:.875rem;--type-caption:.8125rem;
  --type-data:2rem;--type-metadata:.75rem;
  --color-canvas:#f5f5f7;--color-content:#fff;--color-content-subtle:#f0f0f3;
  --color-content-raised:#fff;--color-content-sunken:#e9e9ee;
  --color-label-primary:#1d1d1f;--color-label-secondary:#48484d;--color-label-tertiary:#63636b;
  --color-separator:#dedee3;--color-separator-strong:#c4c4cc;--color-control-border:#85858f;
  --color-accent:#0064d1;--color-accent-hover:#0055b3;--color-accent-soft:#e8f1ff;
  --color-on-accent:#fff;--color-focus:#0064d1;
  --color-danger:#b42326;--color-danger-soft:#fff0ef;--color-danger-edge:#c65353;
  --color-warning:#805000;--color-warning-soft:#fff4df;--color-warning-edge:#aa7723;
  --color-success:#216b3b;--color-success-soft:#eaf6ed;--color-success-edge:#44885a;
  --material-canvas:var(--color-canvas);--material-content:var(--color-content);
  --material-content-subtle:var(--color-content-subtle);--material-raised:var(--color-content-raised);
  --material-functional-chrome-solid:var(--color-content);--material-floating-solid:var(--color-content-raised);
  /* A1 reserved four optical chrome roles; A4 activates them and adds only the parameters the
     material actually needs. `--chrome-border` stays the opaque structural edge shared with the
     notice and the dialog, so the translucent boundary is its own role rather than a redefinition.
     `--chrome-tint` is now what its name always promised: the translucent foreground of functional
     chrome. Nothing outside the @supports block below consumes it, so an engine without backdrop
     filtering never sees a translucent surface. Every colour role here is tuned separately for
     dark; the blur radius and saturation are deliberately theme-invariant, like the motion tokens,
     so a theme change can never re-animate or snap the optical filter. These are Beeloft tuning
     values, not Apple constants. */
  --chrome-tint:#ffffffd1;--chrome-tint-dense:#ffffffeb;
  --chrome-border:var(--color-separator);--chrome-glass-edge:#1d1d1f24;
  --chrome-highlight:#ffffffb3;--chrome-shadow:var(--shadow-raised);
  --chrome-blur:20px;--chrome-saturation:1.3;
  /* The selection lens is the one prominent glass object: a smaller surface that also moves, so
     it gets a smaller blur budget and a stronger tint/edge than the chrome it sits on. The `-cta`
     variants are the same material in a denser grade, for the one context whose backdrop is the
     dark saturated approval gradient instead of the calm navigation plane. */
  --lens-glass-tint:#e6f0ffd6;--lens-glass-edge:#0064d13d;
  --lens-glass-highlight:#ffffffe0;--lens-glass-shadow:0 1px 2px #1d1d1f12,0 4px 12px #0064d114;
  --lens-glass-blur:14px;--lens-glass-saturation:1.4;
  --lens-glass-cta-tint:#eff5fff2;--lens-glass-cta-edge:#ffffff70;--lens-glass-cta-highlight:#ffffffe8;
  --radius-prominent:24px;--radius-content:20px;--radius-control:12px;
  --radius-compact:8px;--radius-pill:999px;
  --shadow-content:0 1px 2px #1d1d1f06;
  --shadow-raised:0 2px 8px #1d1d1f08,0 8px 24px #1d1d1f06;
  --shadow-floating:0 4px 12px #1d1d1f0d,0 20px 48px #1d1d1f1a;
  --control-height:40px;--control-height-compact:36px;--touch-target-min:44px;
  --control-radius:var(--radius-control);--control-padding-x:.875rem;
  /* Compatibility aliases: selectors migrate by role without a second palette. */
  --canvas:var(--material-canvas);--app-bg:var(--material-canvas);--bg:var(--material-canvas);
  --surface:var(--material-content);--surface-subtle:var(--material-content-subtle);--surface-sunken:var(--color-content-sunken);
  --line:var(--color-separator);--line-strong:var(--color-separator-strong);
  --edge:var(--color-control-border);--control-line:var(--color-control-border);
  --ink:var(--color-label-primary);--ink-2:var(--color-label-secondary);--muted:var(--color-label-tertiary);
  --primary:var(--color-accent);--primary-hover:var(--color-accent-hover);--primary-soft:var(--color-accent-soft);--primary-edge:var(--color-accent);
  --accent:var(--color-accent);--accent-bg:var(--color-accent-soft);--focus:var(--color-focus);
  --danger:var(--color-danger);--danger-bg:var(--color-danger-soft);--danger-edge:var(--color-danger-edge);
  --warning:var(--color-warning);--warning-bg:var(--color-warning-soft);--warning-edge:var(--color-warning-edge);
  --success:var(--color-success);--success-bg:var(--color-success-soft);--success-edge:var(--color-success-edge);
  --sidebar-w:256px;--header-h:72px;--pad-x:32px;--gap:22px;
  --r-card:var(--radius-content);--r-nav:var(--radius-control);--r-chip:var(--radius-compact);--r-pill:var(--radius-pill);
  --shadow-sm:var(--shadow-content);--shadow-md:var(--shadow-raised);--shadow-lg:var(--shadow-floating);
  --shadow:var(--shadow-md);
  /* Motion tokens: the single source for every timing and easing in the product, so
     component rules never carry a raw 150ms/0.3s again. These are theme-invariant, so they
     are declared once here and deliberately not repeated in the dark override. */
  --motion-instant:80ms;--motion-fast:120ms;--motion-base:180ms;
  --motion-enter:220ms;--motion-dialog:260ms;--motion-slow:320ms;
  --ease-standard:cubic-bezier(.22,1,.36,1);--ease-enter:cubic-bezier(.16,1,.3,1);--ease-exit:cubic-bezier(.4,0,1,1);
}
:root[data-theme=dark]{
  color-scheme:dark;
  --color-canvas:#151517;--color-content:#202023;--color-content-subtle:#29292d;
  --color-content-raised:#303034;--color-content-sunken:#19191c;
  --color-label-primary:#f5f5f7;--color-label-secondary:#c6c6ce;--color-label-tertiary:#a6a6b0;
  --color-separator:#3b3b42;--color-separator-strong:#53535d;--color-control-border:#82828d;
  --color-accent:#78b4ff;--color-accent-hover:#a3ccff;--color-accent-soft:#22354e;
  --color-on-accent:#151517;--color-focus:#a3ccff;
  --color-danger:#ffaaa5;--color-danger-soft:#3c2426;--color-danger-edge:#c97673;
  --color-warning:#edc17b;--color-warning-soft:#382e20;--color-warning-edge:#b18b49;
  --color-success:#89d6a1;--color-success-soft:#20352a;--color-success-edge:#619c75;
  /* Dark glass is tuned, not inverted. The tint is the dark content colour held slightly off full
     opacity so the canvas reads through it as depth rather than as smoke; the edge and highlight
     flip to light hairlines because that is how a dark material catches light, and the lens keeps a
     genuinely blue body so it stays distinct from both the hover state and the chrome it sits on. */
  --chrome-tint:#202023d1;--chrome-tint-dense:#202023f0;--chrome-glass-edge:#ffffff24;
  --chrome-highlight:#ffffff14;
  --lens-glass-tint:#2a3f5ccc;--lens-glass-edge:#78b4ff52;
  --lens-glass-highlight:#ffffff1f;--lens-glass-shadow:0 1px 2px #00000033,0 4px 12px #00000040;
  --lens-glass-cta-tint:#22354ef0;--lens-glass-cta-edge:#ffffff4d;--lens-glass-cta-highlight:#ffffff52;
  --shadow-content:0 1px 2px #0000001f;
  --shadow-raised:0 2px 8px #00000024,0 8px 24px #0000001f;
  --shadow-floating:0 4px 12px #00000033,0 20px 48px #0000004d;
}

/* ---------- base ---------- */
body{margin:0;background:var(--bg);color:var(--ink);font-size:var(--type-body);line-height:1.5;-webkit-font-smoothing:antialiased}
main{max-width:none;margin:0;padding:0}
h1,h2,h3{color:var(--ink);letter-spacing:-.02em}
h1{font-size:var(--type-page-title);font-weight:700;line-height:1.2;letter-spacing:-.025em;margin-bottom:.25rem}
h2{font-size:var(--type-section-title);font-weight:650;letter-spacing:-.015em;margin-bottom:.35rem}
h3{font-size:var(--type-card-title);font-weight:650;letter-spacing:-.01em}
.hint{color:var(--muted);font-size:var(--type-caption)}
.eyebrow{margin-bottom:.22rem;color:var(--primary);font-size:var(--type-metadata);font-weight:700;letter-spacing:.04em;text-transform:uppercase}
[hidden]{display:none!important}
::selection{background:var(--color-accent-soft);color:var(--color-label-primary)}

/* ---------- icon language: one 18px stroked set, 1.7 stroke, round joins ---------- */
.icon-sprite{position:absolute;width:0;height:0;overflow:hidden}
.icon{width:18px;height:18px;flex:0 0 auto;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke}
.icon-sm{width:15px;height:15px;stroke-width:1.8}
.icon-lg{width:22px;height:22px;stroke-width:1.6}

/* ---------- shared control geometry ---------- */
button{
  display:inline-flex;align-items:center;justify-content:center;gap:.5rem;
  min-height:var(--control-height);padding:.5rem var(--control-padding-x);
  border:1px solid var(--control-line);border-radius:var(--control-radius);
  background:var(--surface);color:var(--ink);
  box-shadow:var(--shadow-content);
  font-size:var(--type-control);font-weight:600;letter-spacing:-.005em;cursor:pointer;
}
button:hover{border-color:var(--edge);background:var(--surface-subtle)}
button:disabled{opacity:.45;cursor:not-allowed}
.primary{background:var(--primary);border-color:var(--primary);color:var(--color-on-accent);box-shadow:var(--shadow-content)}
.primary:hover{background:var(--primary-hover);border-color:var(--primary-hover);color:var(--color-on-accent)}
.quiet{border-color:transparent;background:transparent;box-shadow:none;color:var(--ink-2)}
.quiet:hover{border-color:transparent;background:var(--surface-subtle);color:var(--ink)}
/* Focus is immediate and follows each control's shape without changing its radius. */
:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
input,select,textarea{
  width:100%;min-height:var(--control-height);padding:.5rem var(--control-padding-x);
  border:1px solid var(--control-line);border-radius:var(--control-radius);
  background:var(--surface);color:var(--ink);font-size:var(--type-control);
}
input:focus,select:focus,textarea:focus{border-color:var(--focus);outline:2px solid var(--focus);outline-offset:2px;box-shadow:none}
label{font-size:.8125rem;font-weight:600;color:var(--ink-2)}
.icon-button{width:var(--control-height);height:var(--control-height);min-height:var(--control-height);padding:0;border:1px solid var(--line);border-radius:var(--r-pill);background:var(--surface-subtle);color:var(--ink-2)}
.icon-button:hover{background:var(--surface-sunken);border-color:var(--line-strong)}

/* ---------- masthead: 72px, brand occupies the sidebar column with a shared rule ---------- */
.masthead{
  position:sticky;top:0;z-index:20;
  display:grid;grid-template-columns:var(--sidebar-w) minmax(0,1fr) auto;
  align-items:stretch;gap:0;min-height:var(--header-h);padding:0;
  border-bottom:1px solid var(--chrome-border);background:var(--material-functional-chrome-solid);
}
.masthead-brand{display:flex;align-items:center;gap:.5rem;padding:0 20px 0 24px;border-right:1px solid var(--line)}
.brand{display:flex;align-items:center;gap:10px;min-width:0;text-decoration:none;color:var(--ink)}
.brand-mark{display:grid;place-items:center;width:26px;height:26px;flex:0 0 auto;border-radius:10px;background:var(--primary);color:var(--color-on-accent)}
.brand-mark .icon{width:16px;height:16px;stroke-width:2}
.brand-word{font:700 1.3125rem/1 var(--font-ui);letter-spacing:-.04em;white-space:nowrap}
.brand-dot{color:var(--primary)}
.brand small{display:none}
.masthead-tools{display:flex;align-items:center;min-width:0;padding:0 1rem 0 var(--pad-x)}
.top-context{
  display:inline-flex;align-items:center;gap:.45rem;min-width:0;height:40px;padding:0 .95rem;
  border:1px solid var(--line);border-radius:12px;background:var(--surface-subtle);
  color:var(--muted);font-size:.8125rem;white-space:nowrap;overflow:hidden;
}
.top-context strong{color:var(--ink);font-weight:650}
.account{display:flex;align-items:center;gap:.5rem;flex-wrap:nowrap;padding:0 var(--pad-x) 0 1rem}
.account>span#account-name{display:block;flex:0 1 auto;min-width:0;max-width:15rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--muted);font-size:.8125rem}
.account button{min-height:38px;padding:.35rem .9rem;border-color:var(--line-strong);background:var(--surface);color:var(--ink-2);font-size:.8125rem}
.account button:hover{border-color:var(--edge);background:var(--surface-subtle);color:var(--ink)}
.avatar{display:grid;place-items:center;width:40px;height:40px;flex:0 0 auto;border-radius:var(--r-pill);background:var(--surface-subtle);box-shadow:inset 0 0 0 1px var(--line);color:var(--muted)}
.menu-toggle{display:none}

/* ---------- shell ---------- */
.app-shell{display:grid;grid-template-columns:var(--sidebar-w) minmax(0,1fr);align-items:start;min-height:calc(100dvh - var(--header-h))}
.app-sidebar{
  position:sticky;top:var(--header-h);align-self:start;
  display:flex;flex-direction:column;gap:1rem;
  width:100%;height:calc(100dvh - var(--header-h));padding:14px 16px 16px;
  overflow:auto;border-right:1px solid var(--chrome-border);background:var(--material-functional-chrome-solid);
  scrollbar-width:thin;isolation:isolate;
}
/* left/top 0 menjangkarkan lens di tepi padding konteks posisinya, sehingga transform yang
   ditulis pengendali gerak A3 adalah koordinat lokalnya sendiri. Perjalanan dimiliki fisika,
   bukan CSS: tidak ada transition maupun animation di sini, dan A3 tidak menambahkannya. */
.nav-selection-lens{
  position:absolute;left:0;top:0;z-index:0;pointer-events:none;
  background:var(--color-accent-soft);border:1px solid var(--color-separator-strong);
  border-radius:var(--radius-control);transition:none;animation:none;
}
.sidebar-nav{display:flex;flex-direction:column;gap:0}
.nav-group{display:flex;flex-direction:column;gap:5px;padding:6px 0}
.nav-group+.nav-group,.nav-group+.nav-collapse,.nav-collapse+.nav-group{border-top:1px solid var(--line)}
.nav-group-tail{margin-top:auto}
.nav-item{
  position:relative;justify-content:flex-start;gap:12px;width:100%;
  min-height:40px;padding:0 14px;
  border:0;border-radius:var(--r-nav);background:transparent;box-shadow:none;
  color:var(--ink-2);font-size:.9375rem;font-weight:500;letter-spacing:-.005em;text-align:left;
}
.nav-item .icon{color:var(--muted)}
.nav-item:hover{background:var(--surface-subtle);color:var(--ink)}
.nav-item:hover .icon{color:var(--ink-2)}
.nav-item[aria-current=page]{background:var(--primary-soft);color:var(--primary);font-weight:650}
.nav-item[aria-current=page] .icon{color:var(--primary)}
.nav-item[aria-current=page]::before{
  content:"";position:absolute;left:-16px;top:1px;bottom:1px;width:3px;
  border-radius:0 3px 3px 0;background:var(--primary-edge);
}
.nav-collapse{padding:6px 0}
.nav-summary{
  display:flex;align-items:center;gap:12px;min-height:40px;padding:0 14px;
  border-radius:var(--r-nav);color:var(--ink-2);font-size:.9375rem;font-weight:500;
  cursor:pointer;list-style:none;
}
.nav-summary::-webkit-details-marker{display:none}
.nav-summary:hover{background:var(--surface-subtle);color:var(--ink)}
.nav-summary>span{flex:1}
.nav-summary .icon{color:var(--muted)}
.nav-caret{margin-left:auto;transform:rotate(-180deg)}
.nav-collapse:not([open]) .nav-caret{transform:rotate(0)}
.nav-children{display:flex;flex-direction:column;gap:2px;margin-top:2px}
.nav-children .nav-item{min-height:34px;padding-left:44px;font-size:.875rem;color:var(--muted)}
.nav-children .nav-item:hover{color:var(--ink)}
.nav-children .nav-item[aria-current=page]{color:var(--primary);background:var(--primary-soft)}
.sidebar-actions{position:sticky;bottom:-16px;display:block;margin-top:10px;padding:10px 0 16px;border:0;background:var(--surface)}
.sidebar-actions::before{content:"";position:absolute;left:0;right:0;top:-16px;height:16px;background:linear-gradient(to bottom,transparent,var(--surface));pointer-events:none}
.sidebar-cta{
  position:relative;overflow:hidden;padding:16px 18px 18px;border-radius:18px;
  background:linear-gradient(157deg,#3b6bff 0%,#2b53d8 34%,#1d3382 72%,#131c44 100%);
  color:#fff;
}
.sidebar-cta::after{
  content:"";position:absolute;inset:0 0 auto auto;width:96px;height:96px;
  background-image:radial-gradient(#ffffff40 1px,transparent 1px);background-size:8px 8px;
  opacity:.5;pointer-events:none;
}
.sidebar-cta-mark{position:relative;display:grid;place-items:center;width:28px;height:28px;border-radius:10px;background:#fff;color:var(--primary)}
.sidebar-cta-title{position:relative;margin:13px 0 4px;font-size:.9375rem;font-weight:700;letter-spacing:-.01em}
.sidebar-cta-copy{position:relative;margin:0;color:#ffffffc4;font-size:.8125rem;line-height:1.5}
.sidebar-cta-button{position:relative;width:100%;margin-top:13px;background:#2f5efb;border-color:#2f5efb;box-shadow:0 2px 10px #0b0d1733}
.sidebar-cta-button:hover{background:#4a74ff;border-color:#4a74ff;color:#fff}
:root[data-theme=dark] .sidebar-cta-button{color:#fff}
#approvals[aria-current=page]{
  background:var(--color-accent-soft);border-color:var(--color-separator-strong);
  color:var(--color-accent);font-weight:650;
}
.app-sidebar.nav-lens-ready .nav-item[aria-current=page]{background:transparent}
.app-sidebar.nav-lens-ready .nav-item[aria-current=page]::before{content:none}
.app-sidebar.nav-lens-ready #approvals[aria-current=page]{
  background:transparent;border-color:transparent;box-shadow:none;z-index:2;
}
.sidebar-cta>.nav-selection-lens{z-index:1}

/* ---------- content area ---------- */
.workspace-main{min-width:0;width:100%;padding:24px var(--pad-x) 40px;background:var(--canvas)}
.workspace-main>section{min-width:0}
.page-heading{display:flex;align-items:flex-end;justify-content:space-between;gap:1.5rem;margin:0 0 var(--gap)}
.page-heading .actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:flex-end}
.pill-hint{display:inline-flex;align-items:center;height:40px;padding:0 .95rem;border:1px solid var(--line);border-radius:var(--r-pill);background:var(--surface);color:var(--muted);white-space:nowrap}
.back{min-height:30px;margin:0 0 9px;padding:0 .6rem 0 0;color:var(--muted);font-size:.8125rem;font-weight:600}
.back:hover{background:transparent;color:var(--primary)}

/* ---------- cards ---------- */
.card{border:1px solid var(--line);border-radius:var(--r-card);background:var(--surface);box-shadow:var(--shadow-sm)}
.card{padding:22px}
.card-raised{background:var(--material-raised);box-shadow:var(--shadow-raised)}
.state{padding:2rem 1rem;border:1px solid var(--line);border-radius:var(--r-card);background:var(--surface);color:var(--muted);text-align:center;font-size:.875rem}
.state h3{margin-bottom:.35rem;color:var(--ink)}
.error{border-radius:12px;background:var(--danger-bg);color:var(--danger);padding:.8rem 1rem;font-size:.875rem}
.session-warning{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;margin:1.2rem 0 0;border:1px solid var(--warning-edge);border-left:3px solid var(--warning-edge);border-radius:12px;background:var(--warning-bg);color:var(--warning);padding:.8rem 1rem;font-size:.875rem}
.session-warning p{margin:0;min-width:0;overflow-wrap:anywhere}
.visually-hidden{position:absolute!important;width:1px;height:1px;margin:0;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
.section-heading{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:14px}
.section-heading h2,.section-heading p{margin-bottom:0}
.hero-panel .card-head h2{margin:0}
.card-head{display:flex;align-items:center;justify-content:space-between;gap:.75rem;margin-bottom:16px}
/* A card head is a flex row of a heading and a trailing glyph. At 200% text the heading's longest
   word can exceed the card's content box; a flex item's default `min-width:auto` then grows the row
   instead of wrapping it, and the glyph is pushed past the viewport edge. Measured at 320px/200% on
   the command centre's marketplace comparison card. */
.card-head>*{min-width:0}
.card-head h3{margin:0;overflow-wrap:anywhere}
.card-head .icon{color:var(--muted)}

/* ---------- KPI row: 4 x 263x130, r16, icon top-right, 32px number ---------- */
.summary{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gap);margin:0 0 var(--gap);padding:0;border:0}
.summary>div,.summary>div:first-child,.summary>div:last-child{
  display:flex;flex-direction:column;min-width:0;min-height:130px;padding:20px 22px;
  border:1px solid var(--line);border-radius:var(--r-card);background:var(--surface);box-shadow:var(--shadow-sm);
}
.summary dt{display:flex;align-items:center;justify-content:space-between;gap:.5rem;color:var(--ink);font-size:.9375rem;font-weight:650;letter-spacing:-.008em}
.summary dt .icon{color:var(--primary)}
.summary dd{margin:auto 0 0;font-size:var(--type-data);font-weight:800;letter-spacing:-.035em;line-height:1.1;font-variant-numeric:tabular-nums;color:var(--ink)}
.summary dd small{margin-left:.3rem;color:var(--muted);font-size:.8125rem;font-weight:500;letter-spacing:0}
.kpi-value{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.kpi-foot{margin:8px 0 0;color:var(--muted);font-size:.78125rem;line-height:1.4;overflow-wrap:anywhere}
.chip{display:inline-flex;align-items:center;gap:4px;min-height:24px;padding:0 8px;border-radius:var(--r-chip);font-size:.75rem;font-weight:700;letter-spacing:-.005em;white-space:nowrap}
.chip .icon{width:13px;height:13px;stroke-width:2.2}
.chip-neutral{background:var(--surface-sunken);color:var(--muted)}
.chip-danger{background:var(--danger-bg);color:var(--danger)}
.chip-warning{background:var(--warning-bg);color:var(--warning)}
.chip-success{background:var(--success-bg);color:var(--success)}
.chip-primary{background:var(--primary-soft);color:var(--primary)}

/* ---------- Command Center presentation primitives ---------- */
.hero-panel{min-width:0;padding:22px;border:1px solid var(--line);border-radius:var(--r-card);background:var(--surface);box-shadow:var(--shadow-sm)}
.hero-body{display:grid;grid-template-columns:minmax(0,34fr) minmax(0,66fr);gap:22px;align-items:center}
.hero-figure{min-width:0}
.hero-figure .hero-value{margin:0;font-size:2.125rem;font-weight:800;letter-spacing:-.04em;line-height:1.1;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.hero-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:10px 0 0}
.hero-caption{margin:8px 0 0;color:var(--muted);font-size:.8125rem;line-height:1.45}
.hero-figure .hero-value small{margin-left:.4rem;color:var(--muted);font-size:.9375rem;font-weight:500;letter-spacing:0}
.hero-chart-note{margin:10px 0 0;color:var(--muted);font-size:.75rem;text-align:center}
.hero-chart{display:flex;flex-direction:column;gap:9px;min-width:0}
.hero-bar{display:grid;grid-template-columns:minmax(0,1fr);gap:4px;min-width:0}
.hero-bar-top{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;min-width:0}
.hero-bar-label{color:var(--muted);font-size:.78125rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hero-bar-value{color:var(--ink);font-size:.78125rem;font-weight:650;font-variant-numeric:tabular-nums;white-space:nowrap}
.hero-bar-track{height:8px;border-radius:var(--r-pill);background:var(--surface-sunken);overflow:hidden}
.hero-bar-fill{display:block;height:100%;min-width:3px;border-radius:var(--r-pill);background:var(--primary)}
.hero-bar-fill.is-muted{background:#9db4f7}
.hero-bar-fill.is-danger{background:var(--danger-edge)}
.hero-bar-fill.is-success{background:var(--success-edge)}
.hero-split{margin-top:16px;border:1px solid var(--line);border-radius:14px;background:var(--surface);overflow:hidden}
.hero-split-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;background:var(--line)}
.hero-split-cell{padding:13px 16px 14px;background:var(--surface);min-width:0}
.hero-split-value{display:flex;align-items:center;gap:8px;color:var(--ink);font-size:1.25rem;font-weight:750;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.hero-split-value .icon{width:16px;height:16px}
.hero-split-cell.is-critical .icon{color:var(--danger)}
.hero-split-cell.is-warning .icon{color:var(--warning)}
.hero-split-cell.is-info .icon{color:var(--primary)}
.hero-split-label{margin:2px 0 0;color:var(--muted);font-size:.78125rem}
.hero-split-bars{display:flex;gap:6px;padding:0 16px 13px;background:var(--surface)}
.hero-split-bar{height:5px;border-radius:var(--r-pill);min-width:6px}
.hero-split-bar.is-critical{background:var(--danger-edge)}
.hero-split-bar.is-warning{background:var(--warning-edge)}
.hero-split-bar.is-info{background:var(--primary)}

/* ---------- decision queue rendered as the reference's list/table surface ---------- */
.attention-panel{min-width:0;padding:22px}
.decision-list{display:flex;flex-direction:column;min-width:0}
.command-attention.decision-row{
  display:grid;grid-template-columns:36px minmax(0,1fr) auto;gap:14px;align-items:center;
  padding:13px 0;border:0;border-bottom:1px dashed var(--line);background:transparent;
  overflow-wrap:anywhere;
}
.command-attention.decision-row:last-child{border-bottom:0;padding-bottom:2px}
.decision-mark{display:grid;place-items:center;width:36px;height:36px;border-radius:12px;background:var(--surface-sunken);color:var(--muted)}
.decision-row.critical .decision-mark{background:var(--danger-bg);color:var(--danger)}
.decision-row.warning .decision-mark{background:var(--warning-bg);color:var(--warning)}
.decision-row.info .decision-mark{background:var(--primary-soft);color:var(--primary)}
.decision-body{min-width:0}
.decision-title{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;min-width:0}
.decision-row h3{margin:0;font-size:.9375rem;font-weight:650;letter-spacing:-.01em}
.decision-row .decision-detail{margin:3px 0 0;color:var(--muted);font-size:.8125rem;line-height:1.4}
.decision-row .status-label{flex:0 0 auto}
.decision-action{justify-self:end}
.decision-action button{min-height:var(--control-height-compact);padding:.35rem .85rem;font-size:.8125rem}

/* ---------- snapshot cards ---------- */
.command-snapshot.snapshot-card{min-width:0;padding:18px 20px;box-shadow:var(--shadow-sm)}
.command-snapshot.snapshot-card h3{font-size:var(--type-card-title)}
.snapshot-card .card-head{margin-bottom:12px}
.command-values{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,auto);gap:0;margin:0}
.command-values dt,.command-values dd{padding:4px 0;font-size:.8125rem;min-width:0}
.command-values dt{color:var(--muted)}
.command-values dd{margin:0;text-align:right;color:var(--ink);font-weight:650;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.command-source{margin:11px 0 0;padding-top:11px;border-top:1px dashed var(--line);color:var(--muted);font-size:.75rem;line-height:1.45}
.snapshot-card>button:last-child{width:100%;margin-top:13px}
.snapshot-card .state{padding:1rem;font-size:.8125rem}

/* gauge (reference: Repeat Customer Rate) */
.gauge{display:flex;flex-direction:column;align-items:center;margin:0 0 10px}
.gauge svg{display:block;width:100%;max-width:184px;height:auto}
.gauge-tick{stroke-width:3.4;stroke-linecap:round}
.gauge-tick.is-off{stroke:var(--surface-sunken)}
.gauge-tick.is-on{stroke:var(--success-edge)}
.gauge-tick.is-on.is-warn{stroke:var(--warning-edge)}
.gauge-tick.is-on.is-bad{stroke:var(--danger-edge)}
.gauge-value{margin:-28px 0 0;font-size:1.6875rem;font-weight:800;letter-spacing:-.035em;font-variant-numeric:tabular-nums;color:var(--ink)}
.gauge-note{margin:2px 0 0;color:var(--muted);font-size:.75rem;text-align:center}

/* meter (reference: progress treatment) */
.meter{margin:0 0 12px}
.meter-top{display:flex;align-items:baseline;justify-content:space-between;gap:.75rem;margin-bottom:7px}
.meter-label{color:var(--muted);font-size:.78125rem}
.meter-figure{color:var(--ink);font-size:.875rem;font-weight:700;font-variant-numeric:tabular-nums}
.meter-track{height:9px;border-radius:var(--r-pill);background:var(--surface-sunken);overflow:hidden}
.meter-fill{display:block;height:100%;min-width:4px;border-radius:var(--r-pill);background:var(--primary)}
.meter-fill.is-over{background:var(--danger-edge)}
.meter-fill.is-tight{background:var(--warning-edge)}
.meter-note{margin:7px 0 0;color:var(--muted);font-size:.75rem}

/* three-up split inside snapshot cards (reference: Customers breakdown) */
.split3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:0 0 12px}
.split3-cell{min-width:0}
.split3-value{color:var(--ink);font-size:1.125rem;font-weight:750;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.split3-label{margin:1px 0 7px;color:var(--muted);font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.split3-bar{height:5px;border-radius:var(--r-pill);background:var(--surface-sunken);overflow:hidden}
.split3-bar>span{display:block;height:100%;min-width:3px;border-radius:var(--r-pill)}
.split3-cell:nth-child(1) .split3-bar>span{background:var(--primary)}
.split3-cell:nth-child(2) .split3-bar>span{background:var(--success-edge)}
.split3-cell:nth-child(3) .split3-bar>span{background:var(--warning-edge)}

/* AI context uses the same quiet utility surface as other source panels. */
.ai-card{text-align:left}
.ai-card p{margin:0 0 4px}
.ai-copy{color:var(--muted);font-size:.8125rem;line-height:1.5}

/* ---------- status labels / badges ---------- */
.status-label,.badge{
  display:inline-flex;align-items:center;gap:5px;min-height:22px;max-width:100%;padding:1px 8px;
  border:0;border-radius:var(--r-chip);background:var(--primary-soft);color:var(--primary);
  font-size:.6875rem;font-weight:700;letter-spacing:.01em;line-height:1.25;
  white-space:normal;overflow-wrap:anywhere;
}
.chip{max-width:100%}
.status-label.late,.badge-danger{background:var(--danger-bg);color:var(--danger)}
.status-label.done,.badge-success{background:var(--success-bg);color:var(--success)}
.badge-warning{background:var(--warning-bg);color:var(--warning)}

/* ---------- filters, tables, lists ---------- */
.filters,.filter-bar{display:flex;gap:12px;align-items:end;flex-wrap:wrap;margin:0 0 var(--gap);padding:16px;border:1px solid var(--line);border-radius:var(--r-card);background:var(--surface);box-shadow:var(--shadow-sm)}
.filters button{margin-bottom:0}
.table-shell{overflow:auto;border:1px solid var(--line);border-radius:var(--r-card);background:var(--surface)}
.data-table{width:100%;border-collapse:collapse;text-align:left;font-size:.875rem}
.data-table th,.data-table td{padding:.75rem 1rem;border-bottom:1px solid var(--line);vertical-align:top}
.data-table th{background:var(--surface-subtle);color:var(--muted);font-size:.6875rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase}
.data-table tr:last-child td{border-bottom:0}
.order-row{padding:1.1rem;border:1px solid var(--line);border-top:0;background:var(--surface)}
.order-row:last-child{border-radius:0 0 var(--r-card) var(--r-card)}
/* display:block is load-bearing: the reference span is nested inside this button, so an
   inline-flex button would lay the reference and the title out side by side. */
.order-title{display:block;min-height:auto;padding:0;border:0;background:transparent;box-shadow:none;color:var(--ink);font-size:.9375rem;font-weight:650;text-align:left;justify-content:flex-start}
.order-title:hover{background:transparent;color:var(--primary);text-decoration:underline}
.pagination{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-top:16px}
progress{height:6px;border-radius:var(--r-pill);overflow:hidden}
.notice{border:1px solid var(--chrome-border);border-radius:var(--radius-control);background:var(--material-floating-solid);color:var(--ink);box-shadow:var(--shadow-floating)}
/* State, not motion: a container whose data is being replaced in place stays readable while it
   is dimmed, and the dimming itself is the same in reduced-motion mode. The fade into and out of
   it lives with the other transitions below. */
.is-refreshing{opacity:.78}
/* Same reasoning for the scan feedback: the tint is the state a valid scan reports, so it is
   declared here and stays visible with every transition disabled. Only its release is animated,
   in the `no-preference` query below. */
.scan-result.is-scan-ok{background:var(--success-bg)}
dialog{border:1px solid var(--chrome-border);border-radius:var(--radius-prominent);background:var(--material-floating-solid);box-shadow:var(--shadow-floating)}
.login-form{border:1px solid var(--line);border-top:3px solid var(--primary);border-radius:var(--r-card);box-shadow:var(--shadow-md)}
.login-view{padding:clamp(2rem,6vw,4rem) 4%}
.skip-link{border-radius:0 0 12px 0}

/* ---------- responsive ---------- */
@media(max-width:1240px){
  :root{--pad-x:24px;--gap:18px}
}
@media(max-width:1040px){
  .summary{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media(max-width:980px){
  .masthead{display:flex;flex-wrap:wrap;justify-content:space-between;min-height:64px}
  .masthead-brand{padding:0 14px;border-right:0}
  .account{max-width:100%;flex-wrap:wrap;margin-left:auto}
  .menu-toggle:not([hidden]){display:inline-flex;min-height:38px;padding:.3rem .75rem}
  .masthead-tools{display:none}
  .app-shell{grid-template-columns:minmax(0,1fr)}
  .app-sidebar{position:relative;top:auto;display:none;height:auto;padding:14px 16px 18px;border-right:0;border-bottom:1px solid var(--line)}
  .nav-open .app-sidebar{display:flex}
  .nav-item{min-height:var(--touch-target-min)}
  .nav-summary{min-height:var(--touch-target-min)}
  .nav-children .nav-item{min-height:var(--touch-target-min)}
  .nav-item[aria-current=page]::before{left:-16px}
  .workspace-main{padding:20px var(--pad-x) 32px}
  button,input,select,textarea{min-height:var(--touch-target-min)}
  .account button,.menu-toggle:not([hidden]){min-height:var(--touch-target-min)}
  .icon-button{width:var(--touch-target-min);height:var(--touch-target-min)}
}
@media(max-width:650px){
  :root{--pad-x:16px;--gap:14px}
  .masthead{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-height:0;padding:10px 12px}
  .masthead-brand{flex:1 1 auto;min-width:0;flex-wrap:wrap;row-gap:6px;padding:0;gap:10px}
  .brand,.masthead-brand .brand{flex-direction:row;align-items:center;gap:9px}
  .brand>span.brand-word{font-size:1.125rem}
  .brand-mark{width:24px;height:24px}
  .account{width:100%;max-width:none;padding:0;justify-content:flex-start;flex-wrap:wrap;gap:6px}
  .account>span#account-name{order:-1;flex:1 1 100%;max-width:none;text-align:left}
  .account>span.avatar{flex:0 0 auto;width:34px;height:34px;margin-left:auto}
  .account button{flex:0 1 auto;min-width:0}
  .avatar{width:34px;height:34px}
  .app-shell{min-height:auto}
  .workspace-main{padding:16px var(--pad-x) 28px}
  .summary{grid-template-columns:minmax(0,1fr)}
  .summary>div,.summary>div:first-child,.summary>div:last-child{min-height:0;padding:16px 18px}
  .summary dd{font-size:1.75rem}
  .page-heading{flex-direction:column;align-items:flex-start;gap:12px}
  .page-heading .actions{width:100%;justify-content:flex-start}
  .workspace-main .page-heading>div:first-child{width:100%;max-width:none}
  .pill-hint{height:36px;font-size:.75rem}
  .card,.attention-panel,.hero-panel{padding:16px}
  .command-snapshot.snapshot-card{padding:16px}
  .hero-figure .hero-value{font-size:1.75rem}
  .hero-split-grid{grid-template-columns:minmax(0,1fr)}
  .decision-head{display:none}
  .command-attention.decision-row{grid-template-columns:32px minmax(0,1fr);gap:12px;padding:14px 0}
  .decision-mark{width:32px;height:32px}
  .decision-action{grid-column:2;justify-self:stretch}
  .decision-action button{width:100%;min-height:var(--touch-target-min)}
  .pagination{align-items:flex-start}
  .filters,.filter-bar{padding:12px}
  .order-row{border-top:1px solid var(--line);border-radius:var(--r-card);margin-bottom:10px}
  .command-values{grid-template-columns:minmax(0,1fr)}
  .command-values dd{text-align:left;padding-top:0}
  .command-values dt{padding-bottom:1px}
}

/* ---------- A4 functional glass: progressive enhancement over the solid A1 chrome ----------
   Everything above is the baseline and stays the baseline. The solid declarations on `.masthead`,
   `.app-sidebar`, `.sidebar-actions` and `.nav-selection-lens` are not conditional on anything and
   are a finished, intentional rendering on their own; an engine that cannot filter a backdrop
   simply never reads this section. That order matters: defining translucency first and patching it
   afterwards would make the fallback the accident instead of the foundation.

   This block sits after the responsive rules on purpose, so the drawer's own geometry at <=980px is
   already settled before the material lands on top of it, and it is the *whole* runtime optical
   system — there is no JavaScript renderer, no optical animation frame, no pointer tracking and no
   canvas. The A3 spring moves the lens; A4 only decides what the moving object is made of.

   The gate names both the standard and the -webkit property, because `or` here is a support test
   rather than a vendor preference, and both properties are then declared on every glassed surface.

   What this is not: this is a CSS approximation of a translucent optical material. It is not
   Apple's compositor, not the Liquid Glass renderer, and there is no refraction, dispersion or
   chromatic aberration anywhere in it. */
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  /* The thin translucent plane at the top of the shell. Sticky, z-index, grid, height and every
     control inside it are untouched: only the material changes. It is the one glass surface with a
     genuinely changing backdrop, because workspace content scrolls underneath it. */
  .masthead{
    background:var(--chrome-tint);border-bottom-color:var(--chrome-glass-edge);
    -webkit-backdrop-filter:blur(var(--chrome-blur)) saturate(var(--chrome-saturation));
    backdrop-filter:blur(var(--chrome-blur)) saturate(var(--chrome-saturation));
    box-shadow:inset 0 1px 0 var(--chrome-highlight),var(--chrome-shadow);
  }
  /* One material container for the whole navigation column — not one material per row. Hover stays
     a local, opaque-token affordance and the selected state stays the lens. The sidebar's backing
     surface is the neutral canvas, so this reads more quietly than the masthead; that is the honest
     result of the application's real painting hierarchy and no wallpaper is invented to hide it.
     It carries no separation shadow: it is flush against the content column, where a shadow would
     be noise rather than depth. The inset highlight sits just inside its own right edge, so the
     boundary reads as material thickness instead of a drawn line. */
  .app-sidebar{
    background:var(--chrome-tint);border-right-color:var(--chrome-glass-edge);
    -webkit-backdrop-filter:blur(var(--chrome-blur)) saturate(var(--chrome-saturation));
    backdrop-filter:blur(var(--chrome-blur)) saturate(var(--chrome-saturation));
    box-shadow:inset -1px 0 0 var(--chrome-highlight);
  }
  /* The sticky approval plate. Its solid `var(--surface)` was invisible against solid chrome and
     would read as a broken opaque rectangle against glass, so it becomes the dense grade of the
     same tint — and so does the short fade above it, which is the same declaration it always was
     with the same colour role substituted. Deliberately *no* second backdrop-filter: one blurred
     surface per column. The dense tint is what keeps scrolled navigation labels from showing
     through the approval area, which is the whole reason this plate exists. */
  .sidebar-actions{background:var(--chrome-tint-dense)}
  .sidebar-actions::before{background:linear-gradient(to bottom,transparent,var(--chrome-tint-dense))}
  /* The prominent glass object. Still the same single node the A2 measurement and the A3 integrator
     own, still anchored at left/top 0, still `transition:none; animation:none` — appearance is
     state here, never a second movement system, and nothing below is written per frame by script.

     It deliberately carries NO backdrop filter in the navigation column, and that is a measured
     decision rather than an omission. The sidebar above is itself a filtered surface, so it is a
     backdrop root: a filter here would sample that root's flat tint and produce no visible blur at
     all, while forcing the whole 256px column to be re-sampled on every frame the lens moves. That
     cost was observable — it dropped one frame early in each travel, which was enough to collapse
     A3's velocity-continuity measurement from ~15px of coast to under 1px. Removing it restored
     that measurement in four consecutive runs and cost nothing visually, because the tint, the
     optical edge, the inset highlight, the shadow and A3's velocity deformation are what give the
     lens its material here. This is also what §25 of the phase brief asks for: one blurred surface
     per column, not nested blur surfaces. */
  .nav-selection-lens{
    background:var(--lens-glass-tint);border-color:var(--lens-glass-edge);
    box-shadow:inset 0 1px 0 var(--lens-glass-highlight),var(--lens-glass-shadow);
  }
  /* Same lens, same physics, one scoped material — and the one place the lens is actually filtered.
     The approval CTA keeps its own opaque gradient because it is business content inside the chrome,
     so the lens that travels into it needs a denser grade of its material to keep the selected label
     at 4.5:1 over a dark saturated backdrop. Here the blur has something real to work on: the lens
     is painted after the CTA gradient and its dot texture, so it genuinely refracts them. This is a
     tint swap plus a scoped filter on the existing node: no duplicate lens, no ghost, no second
     highlight object. */
  .sidebar-cta>.nav-selection-lens{
    background:var(--lens-glass-cta-tint);border-color:var(--lens-glass-cta-edge);
    -webkit-backdrop-filter:blur(var(--lens-glass-blur)) saturate(var(--lens-glass-saturation));
    backdrop-filter:blur(var(--lens-glass-blur)) saturate(var(--lens-glass-saturation));
    box-shadow:inset 0 1px 0 var(--lens-glass-cta-highlight),var(--lens-glass-shadow);
  }
  @media(max-width:980px){
    /* The drawer is a horizontal panel rather than a column, so the same 1px highlight moves to the
       edge that now reads as its lit top. Its open/close lifecycle, display-driven visibility,
       inert synchronisation and focus handling are untouched: the material only exists while the
       panel is painted, exactly as before. */
    .app-sidebar{box-shadow:inset 0 1px 0 var(--chrome-highlight)}
  }
  /* Transparency and motion are separate accessibility dimensions, and this is the transparency
     one: where the preference is available it returns every surface above to the A1 solid
     materials. It is deliberately not the only way to get solid chrome — availability is limited,
     so the real guarantee is the unconditional baseline outside this @supports block. */
  @media(prefers-reduced-transparency:reduce){
    .masthead,.app-sidebar{
      background:var(--material-functional-chrome-solid);
      -webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:none;
    }
    .masthead{border-bottom-color:var(--chrome-border)}
    .app-sidebar{border-right-color:var(--chrome-border)}
    .sidebar-actions{background:var(--surface)}
    .sidebar-actions::before{background:linear-gradient(to bottom,transparent,var(--surface))}
    .nav-selection-lens,.sidebar-cta>.nav-selection-lens{
      background:var(--color-accent-soft);border-color:var(--color-separator-strong);
      -webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:none;
    }
  }
}
/* Forced colours outrank the material entirely. Optical filters and every decorative highlight or
   shadow are switched off, and the surfaces fall back to system colours so the chrome keeps real
   boundaries. The selection lens survives as an outline rather than a fill: `aria-current` still
   owns the semantics, the label keeps the system foreground it is given instead of sitting on a
   forced Highlight block, and the focus ring stays the topmost indicator. This sits outside the
   @supports block so it also corrects the solid baseline's decorative edges. */
@media(forced-colors:active){
  .masthead,.app-sidebar,.sidebar-actions,.nav-selection-lens,.sidebar-cta>.nav-selection-lens{
    -webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:none;
  }
  .masthead,.app-sidebar,.sidebar-actions{background:Canvas}
  .masthead{border-bottom:1px solid CanvasText}
  .app-sidebar{border-right:1px solid CanvasText}
  .sidebar-actions::before{background:none}
  .nav-selection-lens,.sidebar-cta>.nav-selection-lens{background:transparent;border:2px solid Highlight}
  .app-sidebar.nav-lens-ready .nav-item[aria-current=page],
  .app-sidebar.nav-lens-ready #approvals[aria-current=page]{background:transparent;color:CanvasText}
}
/* Reduced motion is a first-class mode rather than a fallback, so this is the explicit
   no-motion path for the whole product. Every moving pattern lives inside the
   `no-preference` query below; this block is the safety net that also covers anything a
   later milestone forgets to gate, plus the motion classes themselves for the case where
   the preference changes while an entry is in flight. It removes movement, never state: a
   control still tints when pressed, the disclosure caret still rotates, focus still
   appears — all instantly. `transform` is deliberately not stripped globally, because the
   caret's rotation is its state, not decoration. */
@media(prefers-reduced-motion:reduce){
  *{transition:none!important;animation:none!important}
  .motion-enter,.motion-exit{transform:none!important}
  /* §11 spells the dialog out separately, and it has to be: the catch-all above matches
     elements, never pseudo-elements, so `::backdrop` would keep animating in reduced-motion
     mode. `transform` is stripped here because the dialog's own entry state is decoration —
     unlike the disclosure caret, nothing about the dialog's meaning is carried by its scale. */
  dialog,dialog::backdrop{transition:none!important;animation:none!important;transform:none!important}
  /* §11 names the notice too. It never keeps `.motion-exit` in this mode — hideNotice() applies
     `hidden` immediately — but the class list is spelled out so a notice that is hiding when the
     preference flips still leaves without movement. `opacity` is not stripped: one is the resting
     value, and forcing it would only fight the `hidden` attribute. */
  .notice{transition:none!important;animation:none!important;transform:none!important}
}
@media(prefers-reduced-motion:no-preference){
  /* Theme change is the only place a colour transition runs across surfaces, and the toggle arms
     it for a single token. The selector is wrapped in :where() so it contributes no specificity
     at all: every component rule that owns a transition — the drawer's entry, the dialog's exit,
     a control's press — keeps winning, and this rule only supplies one where there is none. That
     is also why sections and cards are included: the change reads as one movement instead of the
     chrome fading while the content snaps. */
  :where(html.is-theming) :where(body,.masthead,.app-shell,.app-sidebar,.workspace-main,section,article,dl,dialog,.notice,.state){
    transition:background-color var(--motion-base) var(--ease-standard),color var(--motion-base) var(--ease-standard),border-color var(--motion-base) var(--ease-standard);
  }
  button,.nav-item,.nav-summary{transition:background-color var(--motion-fast) var(--ease-standard),border-color var(--motion-fast) var(--ease-standard),color var(--motion-fast) var(--ease-standard),scale var(--motion-instant) var(--ease-standard)}
  /* Press feedback. The individual `scale` property is used instead of a transform so a
     button's own `transform` — the nav caret's rotation, and anything added later — can
     never be overwritten by a press. Disabled and busy controls keep accepting no input,
     so they must read as inert: `:disabled` is excluded and the busy states in the write
     flow set the disabled property, which matches this selector. */
  button:active:not(:disabled){scale:.985}
  /* Workspace entry. Only the target moves, only after it is logically active, and only
     6px — no full-screen horizontal slide. The previous section is already hidden by the
     time these classes land, so nothing slides out. The classes are transient; the script
     removes them once the entry has played so a settled page never carries motion state.
     The transition is declared on the state the element moves TO, and the entry state
     switches it off: a transition declared together with the value it changes only starts
     on the next style change, and this entry applies `is-ready` on that same frame, so the
     value would return to its resting one before anything had started. Switching it off
     makes the entry state an instant snap and the release a real transition from it. */
  .workspace-main>section.motion-enter{
    opacity:0;transform:translateY(6px);transition:none;
  }
  .workspace-main>section.motion-enter.is-ready{
    opacity:1;transform:translateY(0);
    transition:opacity var(--motion-enter) var(--ease-enter),transform var(--motion-enter) var(--ease-enter);
  }
  /* Selected navigation settles a little slower than hover, so choosing a destination reads
     as a state change rather than a hover. Press feedback keeps its own faster token. The
     rail indicator is left alone: it appears with the attribute and fades with nothing to
     fade from, and the specification only allows it to fade or scale subtly. */
  .nav-item[aria-current=page]{
    transition:background-color var(--motion-base) var(--ease-standard),border-color var(--motion-base) var(--ease-standard),color var(--motion-base) var(--ease-standard),scale var(--motion-instant) var(--ease-standard);
  }
  .nav-item .icon,.nav-summary .icon{transition:color var(--motion-base) var(--ease-standard)}
  /* The caret is an `.icon` too, so the summary's colour rule was taking the shorthand for itself
     at equal specificity and the rotation snapped instead of turning. This rule comes after it and
     declares both: the glyph settles its colour and the caret turns, on the same token. */
  .nav-summary .nav-caret{
    transition:transform var(--motion-base) var(--ease-standard),color var(--motion-base) var(--ease-standard);
  }
  /* Dialog. Entry is CSS-only: the surface and its backdrop animate from their first frame
     whenever this element starts matching [open], which also covers a reopen after a close and
     never replays for a content swap, because [open] stays matched. Backdrop timing comes from
     --motion-enter rather than --motion-dialog: the layer table puts the backdrop at 180-220ms
     and 220ms is inside it, while 260ms is not.
     The exit cannot be expressed this way — a native dialog disappears the instant close()
     runs — so the script marks it `is-closing`, waits one exit budget, then calls close().
     That budget is capped at 160ms by the close-helper contract, which is why the exit uses
     --motion-fast: --motion-base (180ms) would exceed the cap. --ease-exit keeps it from
     lingering. `animation:none` drops a still-running entry first; an animation outranks a
     transition, so a close that lands during the entry would otherwise swallow the exit. */
  @keyframes dialog-enter{from{opacity:0;transform:scale(.975) translateY(8px)}}
  @keyframes dialog-backdrop-enter{from{opacity:0}}
  dialog[open]{animation:dialog-enter var(--motion-dialog) var(--ease-enter)}
  dialog[open]::backdrop{animation:dialog-backdrop-enter var(--motion-enter) var(--ease-enter)}
  /* Stopping a running entry is a separate step from applying the closing values, and the script
     flushes the resting state in between. Both in one recalculation and a browser generates no
     transition at all: the dialog just disappears when the safety timer fires, and a backdrop
     still playing its entry keeps fading in while the dialog leaves. The dialog owns an
     animation and so does its backdrop, so both are stopped here. */
  dialog.motion-exit,dialog.motion-exit::backdrop{animation:none}
  /* A dialog on its way out is presentation, not an interaction surface: eight controls inside
     the content call close() directly, and without this they stay clickable while invisible. */
  dialog.is-closing{
    animation:none;pointer-events:none;opacity:0;transform:scale(.985) translateY(4px);
    transition:opacity var(--motion-fast) var(--ease-exit),transform var(--motion-fast) var(--ease-exit);
  }
  dialog.is-closing::backdrop{animation:none;opacity:0;transition:opacity var(--motion-fast) var(--ease-exit)}
  /* Notice. Presentation only: notify() owns the six-second timer and neither the entry nor the
     exit re-arms it. Same frame-later pattern as a page, so the transition really runs — and the
     same split of entry state and transition, for the same reason. */
  .notice.motion-enter{
    opacity:0;transform:translateY(8px);transition:none;
  }
  .notice.motion-enter.is-ready{
    opacity:1;transform:translateY(0);
    transition:opacity var(--motion-enter) var(--ease-enter),transform var(--motion-enter) var(--ease-enter);
  }
  .notice.motion-exit{
    opacity:0;transform:translateY(4px);
    transition:opacity var(--motion-base) var(--ease-exit),transform var(--motion-base) var(--ease-exit);
  }
  /* List and table replacement fades the container as one unit, opacity only: no row stagger and
     no movement, so a filter, a search or a page change reads as a single change of content. This
     is the pattern the transition split above exists for: the container is already on screen when
     it is replaced, so declaring the transition together with the entry value would leave the
     replacement motionless — the release has to be the thing that animates. */
  .list-host.motion-enter,
  .state.motion-enter{
    opacity:0;transition:none;
  }
  .list-host.motion-enter.is-ready,
  .state.motion-enter.is-ready{
    opacity:1;transition:opacity var(--motion-base) var(--ease-standard);
  }
  /* Fading into the dimmed state, not the state itself: reduced motion still shows the dim. */
  .is-refreshing{transition:opacity var(--motion-base) var(--ease-standard)}
  /* Status chips carry state with tint only — no movement, no scale — so a chip that changes can
     never pull the eye away from the table it belongs to. */
  .status-label,.badge{
    transition:background-color var(--motion-base) var(--ease-standard),color var(--motion-base) var(--ease-standard),border-color var(--motion-base) var(--ease-standard);
  }
  /* Scanner feedback: the result surface gives its tint back on its own. The colour itself is
     declared outside this query — it is state, so it survives reduced motion — and the tint
     appears with no transition, so only its release moves. Focus is never touched: a USB scanner
     types like a keyboard, so moving focus would break the next scan. */
  .scan-result{transition:background-color var(--motion-slow) var(--ease-standard)}
  .scan-result.is-scan-ok{transition:none}
}
/* The mobile drawer enters as a small surface after `display` turns on, using the same
   frame-later pattern as a workspace page rather than a screen-wide slide. */
@media(max-width:980px) and (prefers-reduced-motion:no-preference){
  .nav-open .app-sidebar.motion-enter{
    opacity:0;transform:translateY(-6px);transition:none;
  }
  .nav-open .app-sidebar.motion-enter.is-ready{
    opacity:1;transform:translateY(0);
    transition:opacity var(--motion-enter) var(--ease-enter),transform var(--motion-enter) var(--ease-enter);
  }
}


/* Dense metric strips must survive 200% text zoom in narrow dialogs: long single-word
   labels are allowed to break rather than force horizontal overflow. */
.workforce-summary dt,.workforce-summary dd,.summary dt,.command-values dt{overflow-wrap:anywhere}
/* Command center strips carry unbreakable rupiah tokens and nowrap hints inside
   space-between flex rows. Without min-width:0 the rows size to their min-content,
   which fits exactly at 320px/200% on some platforms and overflows on others whose
   default font metrics run wider; the tokens must break instead of forcing a
   document-level scrollbar. */
#command-center-view .page-heading>*,#command-center-view .section-heading>*,
#command-center-view .band-heading>*,#command-center-view .hero-bar-top>*,
#command-center-view .ops-stat dd,#command-center-view .kpi-card dd,
#command-center-view .rank-figures,#command-center-view .hero-bar-value{min-width:0}
#command-center-view .section-heading .hint,#command-center-view .ops-stat dd,
#command-center-view .kpi-value .chip,#command-center-view .hero-bar-value,
#command-center-view .rank-figures,#command-center-view .channel-name,
#command-center-view .channel-net{overflow-wrap:anywhere}
/* `overflow-wrap` only breaks text where a soft wrap opportunity exists, and a chip is `nowrap` by
   design, so the declaration above was inert on this one: the chip has to be allowed to wrap before
   its text can break. Measured as a 2px document overflow at 320px/200% on the KPI strip. */
#command-center-view .kpi-value .chip{white-space:normal}
#command-center-view .hero-bar-value,#command-center-view .rank-figures{white-space:normal}
.workspace-main h1{overflow-wrap:anywhere}
@media(max-width:650px){
  .workforce-summary div,.workforce-summary div:first-child{padding:12px}
}


/* ==========================================================================
   A5 Command Center. A static ambient field separates opaque business surfaces
   from the A4 chrome. All composition changes are local to this destination.
   ========================================================================== */
.workspace-main:has(>#command-center-view:not([hidden])){position:relative}
.workspace-main:has(>#command-center-view:not([hidden]))::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(ellipse 90% 40rem at 10% 0,var(--color-accent-soft),transparent 85%),
    radial-gradient(ellipse 70% 45rem at 100% 30%,var(--material-content-subtle),transparent 90%);
}
.command-center-view{position:relative;min-width:0;max-width:1440px;margin-inline:auto}
.command-center-view .eyebrow{color:var(--color-label-secondary);font-size:var(--type-caption);text-transform:none;letter-spacing:0;font-weight:500;margin-bottom:8px}
.command-center-view .dashboard-heading{margin-bottom:24px;gap:24px}
.dashboard-heading>div:first-child{min-width:0;max-width:42rem}
.command-center-view .dashboard-heading .actions{gap:12px}
.command-center-view .card,.command-center-view .hero-panel{padding:24px;border:0;border-radius:var(--radius-content);background:var(--material-content);box-shadow:var(--shadow-content)}
#command-center-summary{gap:16px;margin-bottom:24px;grid-template-columns:repeat(4,minmax(0,1fr))}
#command-center-summary>div{padding:20px;min-height:168px;border:0;background:var(--material-content);border-radius:var(--radius-content)}
#command-center-summary dt{font-size:var(--type-control);font-weight:500;color:var(--color-label-secondary);align-items:flex-start}
#command-center-summary dd{margin:16px 0 0;font-size:2.125rem;font-weight:700}
#command-center-summary .kpi-value{gap:8px}
#command-center-summary .kpi-foot{margin-top:auto;padding-top:12px;font-size:var(--type-caption)}
#command-center-summary .chip{font-weight:500}
.command-center-content{display:flex;flex-direction:column;gap:32px;align-items:stretch}
.command-hero-grid,.business-grid,.operations-grid{display:grid;grid-template-columns:minmax(0,1.8fr) minmax(280px,1fr);gap:24px;align-items:start}
.command-hero-grid{align-items:stretch}
.command-overview{min-width:0}
#command-center-period{margin:0 0 12px}
.command-hero-grid .hero-body{display:flex;flex-direction:column;align-items:stretch;gap:16px}
.command-hero-grid .hero-figure{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.5fr);column-gap:24px;align-items:start}
.command-hero-grid .hero-figure .hero-value{font-size:2.5rem;font-weight:700}
.command-hero-grid .hero-meta{gap:8px;margin:0}
.command-hero-grid .hero-caption{grid-column:1/-1;font-size:var(--type-caption)}
.command-hero-grid .hero-split{border:0;border-radius:0;margin-top:20px}
.command-hero-grid .hero-split-grid{background:none;gap:16px}
.command-hero-grid .hero-split-cell{padding:0}
.command-hero-grid .hero-split-value{margin:0;font-weight:650}
.command-hero-grid .hero-split-bars{padding:12px 0 0;gap:3px}
.command-hero-grid .hero-split-bar{min-width:0}
.pulse-panel{display:flex;flex-direction:column;min-width:0}
.pulse-total,.approval-total{display:flex;flex-direction:column;margin:16px 0 8px}
.pulse-total dt,.approval-total dt{color:var(--color-label-secondary);font-size:var(--type-control)}
.pulse-total dd,.approval-total dd{order:-1;margin:0;font-size:3rem;line-height:1.1;font-weight:650;letter-spacing:-.04em;font-variant-numeric:tabular-nums}
.pulse-critical{font-size:var(--type-control);color:var(--color-label-secondary);margin:0 0 20px}
.pulse-critical.is-critical{color:var(--color-danger)}
.pulse-next{padding-top:20px;border-top:1px solid var(--color-separator)}
.pulse-next h3{margin-bottom:8px}
.pulse-next p{color:var(--color-label-secondary);font-size:var(--type-control)}
.pulse-next button{width:100%}
.pulse-panel .command-source{margin-top:auto;padding-top:20px;border:0}
.command-context{display:flex;flex-direction:column;gap:24px;min-width:0}
.command-context h2{margin-bottom:16px}
.command-context .approval-total dd{font-size:var(--type-data)}
.approval-amount{font-size:1.125rem;font-weight:650;font-variant-numeric:tabular-nums;margin:16px 0 4px;overflow-wrap:anywhere}
.approval-breakdown{margin:20px 0}
.approval-breakdown summary{cursor:pointer;min-height:var(--touch-target-min);padding:10px 0;font-size:var(--type-control);color:var(--color-label-secondary)}
.approval-breakdown .command-values{margin-top:8px}
.command-context button{width:100%;margin-top:16px}
.command-context .command-values{gap:8px}
.command-center-view .section-heading{align-items:baseline;flex-wrap:wrap;gap:8px;margin-bottom:8px}
.command-center-view .decision-row{grid-template-columns:24px minmax(0,1fr);gap:8px 12px;align-items:start;padding:20px 0;border-bottom:1px solid var(--color-separator)}
.command-center-view .decision-mark{width:24px;height:28px;background:none;border-radius:0}
.command-center-view .decision-title{gap:8px}
.command-center-view .decision-title .status-label{font-weight:500}
.command-center-view .decision-detail{font-size:var(--type-control);color:var(--color-label-secondary);line-height:1.5;margin-top:8px}
.command-center-view .decision-action{grid-column:2;justify-self:start}
.command-center-view .decision-action button{min-height:var(--touch-target-min);font-size:var(--type-control);width:auto}
.command-center-view .command-source{border:0;font-size:var(--type-caption)}
.snapshot-grid{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:20px;align-items:stretch}
.snapshot-grid>article{grid-column:span 2;display:flex;flex-direction:column}
.snapshot-grid>[data-command-snapshot=production],.snapshot-grid>[data-command-snapshot=capacity]{grid-column:span 3}
.snapshot-grid>[data-command-snapshot=production]{order:-2}
.snapshot-grid>[data-command-snapshot=capacity]{order:-1}
.snapshot-grid .command-values{margin-bottom:12px}
.snapshot-grid .command-values dt,.snapshot-grid .command-values dd{font-size:var(--type-control)}
.snapshot-grid .gauge{align-items:flex-start}
.snapshot-grid .gauge svg{max-width:140px}
.snapshot-grid .gauge-value{align-self:flex-start;margin:8px 0 0;font-size:1.5rem}
.snapshot-grid .gauge-note{text-align:left}
.snapshot-grid>article>button:last-child{margin-top:auto}
.snapshot-grid>article>p:last-of-type{margin-bottom:16px}
.command-center-view .ai-copy{font-size:var(--type-control)}
.business-rail{display:flex;flex-direction:column;gap:var(--gap);min-width:0}
.band-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;margin:0 0 14px}
.band-heading h2{margin:0;font-size:1.1875rem;font-weight:700;letter-spacing:-.02em}
.band-heading>div{min-width:0}
.band-heading .hint{display:block;margin:3px 0 0}

/* operational stat strip: keeps the operational numbers, visually secondary to the KPI row */
.ops-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:24px;margin:24px 0 0;padding:20px 24px;border-radius:var(--radius-content);background:var(--material-content-subtle)}
.ops-stat{min-width:0}
.ops-stat dt{color:var(--color-label-secondary);font-size:var(--type-caption);font-weight:500}
.ops-stat dd{margin:6px 0 0;color:var(--ink);font-size:var(--type-control);font-weight:650;font-variant-numeric:tabular-nums}
.ops-stat small{display:block;margin-top:4px;color:var(--muted);font-size:var(--type-caption);font-weight:400}

/* sales trend: real daily points from the latest Jubelio order batch */
.hero-trendwrap{min-width:0}
.trend{min-width:0}
.trend svg{display:block;width:100%;height:auto;overflow:visible}
.trend-grid{stroke:var(--line);stroke-width:1;opacity:.55}
.trend-area{fill:url(#trend-fill);stroke:none}
.trend-line{fill:none;stroke:var(--primary);stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.trend-dot{fill:var(--surface);stroke:var(--primary);stroke-width:2.2}
.trend-ticks{display:flex;justify-content:space-between;gap:.5rem;margin:8px 0 0;color:var(--muted);font-size:var(--type-caption);flex-wrap:wrap}
.trend-peak{margin:0 0 6px;color:var(--muted);font-size:var(--type-caption);font-weight:500;text-align:right}
.trend-note{margin:6px 0 0;color:var(--muted);font-size:.75rem;text-align:center}

/* order funnel occupies the reference's nested breakdown card */
.hero-split-grid{grid-template-columns:repeat(4,minmax(0,1fr))}
.hero-split-bar.is-pending{background:var(--muted)}
.hero-split-bar.is-processing{background:var(--warning-edge)}
.hero-split-bar.is-completed{background:var(--primary)}
.hero-split-bar.is-cancelled{background:var(--danger-edge)}

/* marketplace comparison occupies the reference's table slot */
.channel-panel{min-width:0}
.table-scroll{margin:0;padding:0 20px;overflow-x:auto}
.channel-table{font-size:.8125rem}
.channel-table th{padding:0 10px 9px 0;border-bottom:1px solid var(--line);background:none;white-space:nowrap}
.channel-table th:first-child{padding-left:0}
.channel-table td{padding:20px 12px 20px 0;border-bottom:1px solid var(--line);vertical-align:middle}
.channel-table td:first-child{padding-left:0}
.channel-table tbody tr:last-child td{border-bottom:0;padding-bottom:2px}
.channel-table .is-num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.channel-table th:not(:first-child){text-align:right}
.channel-name{display:block;color:var(--ink);font-weight:650}
.channel-flow{display:block;margin-top:2px;color:var(--muted);font-size:.6875rem}
.channel-net{display:block;margin-top:1px;color:var(--muted);font-size:.6875rem;font-weight:500}
.share{display:inline-flex;align-items:center;gap:7px;justify-content:flex-end}
.share-bar{width:46px;height:6px;flex:0 0 auto;border-radius:var(--r-pill);background:var(--surface-sunken);overflow:hidden}
.share-bar>span{display:block;height:100%;min-width:2px;border-radius:var(--r-pill);background:var(--primary)}
.channel-panel>button:last-child{width:100%;margin-top:14px}

/* top products list occupies the reference's ranked product rows */
.rank-list{display:flex;flex-direction:column;margin:0;padding:0;list-style:none}
.rank-row{display:grid;grid-template-columns:28px minmax(0,1fr) auto;gap:11px;align-items:center;padding:10px 0;border-bottom:1px dashed var(--line)}
.rank-row:last-child{border-bottom:0;padding-bottom:2px}
.rank-mark{display:grid;place-items:center;width:28px;height:28px;border-radius:11px;background:var(--surface-sunken);color:var(--muted);font-size:.75rem;font-weight:800;font-variant-numeric:tabular-nums}
.rank-row:first-child .rank-mark{background:var(--primary-soft);color:var(--primary)}
.rank-body{min-width:0}
.rank-name{display:block;color:var(--ink);font-size:.875rem;font-weight:650;overflow-wrap:anywhere}
.rank-meta{display:block;margin-top:4px;color:var(--muted);font-size:var(--type-caption);overflow-wrap:anywhere}
.rank-figures{text-align:right;white-space:nowrap}
.rank-figures strong{display:block;color:var(--ink);font-size:.8125rem;font-weight:700;font-variant-numeric:tabular-nums}
.rank-figures span{display:block;color:var(--muted);font-size:.6875rem;font-variant-numeric:tabular-nums}

@media(max-width:1200px){
  .command-hero-grid,.business-grid,.operations-grid{grid-template-columns:minmax(0,1.5fr) minmax(250px,1fr);gap:20px}
  #command-center-summary{grid-template-columns:repeat(2,minmax(0,1fr))}
  .snapshot-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .snapshot-grid>article,.snapshot-grid>[data-command-snapshot]{grid-column:auto}
  .ops-stats{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media(max-width:980px){
  .command-center-content{gap:24px}
  .command-center-view .back{min-height:var(--touch-target-min)}
  .command-hero-grid,.business-grid,.operations-grid{grid-template-columns:minmax(0,1fr)}
  .business-rail,.command-context{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}
  .pulse-panel{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.5fr);gap:0 24px}
  .pulse-next{grid-column:2;grid-row:1/5;padding:0 0 0 24px;border-top:0;border-left:1px solid var(--line)}
}
@media(max-width:760px){
  .business-rail,.command-context,.snapshot-grid{grid-template-columns:minmax(0,1fr)}
}
@media(max-width:650px){
  .command-center-content{gap:20px}
  .command-center-view .card,.command-center-view .hero-panel{padding:20px}
  #command-center-summary{grid-template-columns:minmax(0,1fr);gap:12px}
  #command-center-summary>div{min-height:0;padding:20px}
  #command-center-summary dd{font-size:var(--type-data);margin-top:12px}
  .pulse-panel{display:flex}
  .command-hero-grid .hero-figure{display:block}
  .command-hero-grid .hero-meta{margin-top:12px}
  .pulse-next{padding:16px 0 0;border-left:0;border-top:1px solid var(--line)}
  .ops-stats{gap:20px;padding:20px}
  .command-center-view .decision-action{justify-self:stretch}
  .command-center-view .decision-action button{width:100%}
  .command-hero-grid .hero-split-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
  .band-heading{gap:.75rem}
  .band-heading h2{font-size:var(--type-section-title)}
  .table-scroll{margin:0;padding:0}
  .rank-row{grid-template-columns:24px minmax(0,1fr);gap:8px}
  .rank-figures{grid-column:2;text-align:left}
  .rank-figures strong,.rank-figures span{display:inline;margin-right:8px}
}
@media(max-width:360px){
  .ops-stats{grid-template-columns:minmax(0,1fr)}
  .command-hero-grid .hero-figure .hero-value{font-size:var(--type-data)}
}


/* ==========================================================================
   Shared surfaces for the operational sections.

   These existing vocabulary classes consume the A1 foundation through aliases.
   Workspace structure is unchanged. Nested surfaces keep their smaller 14px
   radius inside 20px content cards; floating dialog shells use 24px.
   ========================================================================== */

/* ---------- dialog chrome: same heading rhythm as a page heading ---------- */
.dialog-heading{
  align-items:flex-start;gap:1rem;margin:-2px 0 18px;padding-bottom:14px;
  border-bottom:1px solid var(--line);
}
.dialog-heading h2{font-size:var(--type-section-title);font-weight:700;letter-spacing:-.02em}
.dialog-heading .quiet{flex:0 0 auto}
#dialog-content{min-width:0}
#dialog-content>h3,.dialog-section-title{
  margin:22px 0 12px;color:var(--ink);font-size:1rem;font-weight:650;letter-spacing:-.01em;
}
#dialog-content>h3:first-child{margin-top:0}
#dialog-content>p.hint{margin:0 0 var(--gap)}
#dialog-content>.eyebrow{margin-bottom:.35rem}

/* ---------- info / notice panels ---------- */
.form-info{
  margin:0 0 var(--gap);padding:13px 16px;
  border:1px solid var(--line);border-radius:14px;
  background:var(--surface-subtle);color:var(--ink-2);
  font-size:.8125rem;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;
}
.form-info strong{color:var(--ink);font-weight:650}
.inline-alert{
  margin:0 0 var(--gap);padding:11px 14px;
  border:1px solid var(--line);border-left:3px solid var(--edge);border-radius:12px;
  background:var(--surface-subtle);color:var(--ink-2);font-size:.8125rem;line-height:1.5;
  overflow-wrap:anywhere;
}
.inline-alert.is-danger{border-color:var(--danger-edge);border-left-color:var(--danger-edge);background:var(--danger-bg);color:var(--danger)}
.inline-alert.is-warning{border-color:var(--warning-edge);border-left-color:var(--warning-edge);background:var(--warning-bg);color:var(--warning)}
.inline-alert.is-success{border-color:var(--success-edge);border-left-color:var(--success-edge);background:var(--success-bg);color:var(--success)}
.inline-alert.is-info{border-color:var(--primary-edge);border-left-color:var(--primary-edge);background:var(--primary-soft);color:var(--primary)}

/* ---------- list rows: one card language for every record list ---------- */
.material-event,.audit-event,.issue-item,.product-item,.workforce-row{
  min-width:0;padding:16px 18px;margin:0 0 10px;
  border:1px solid var(--line);border-radius:var(--r-card);
  background:var(--surface);box-shadow:var(--shadow-sm);
  overflow-wrap:anywhere;
}
.material-event:last-child,.audit-event:last-child,.issue-item:last-child,
.product-item:last-child,.workforce-row:last-child{margin-bottom:0}
.material-event>h3,.material-event>h4,.audit-event h3,.issue-item h3,.workforce-row h3{
  margin:0 0 6px;color:var(--ink);font-size:.9375rem;font-weight:650;letter-spacing:-.01em;
}
.material-event>strong,.audit-event>strong,.product-item strong{
  display:block;margin-bottom:4px;color:var(--ink);font-size:.9375rem;font-weight:650;
}
/* Several surfaces emit their status chip as a <p class="status-label">, so the body-copy
   rule excludes chips and badges — otherwise it would out-specify their own type scale
   and neutral tone. */
.material-event p:not(.status-label):not(.badge),.audit-event p:not(.status-label):not(.badge),
.issue-item p:not(.status-label):not(.badge),.workforce-row p:not(.status-label):not(.badge){
  margin:4px 0;color:var(--ink-2);font-size:.8125rem;line-height:1.5;
}
.material-event>p.status-label,.audit-event>p.status-label,
.issue-item>p.status-label,.workforce-row>p.status-label{margin:0 0 8px}
.material-event .hint,.audit-event .hint,.issue-item .hint,.workforce-row .hint,
.product-item .hint{color:var(--muted);font-size:.75rem}
.material-event .reason,.issue-item .reason,.workforce-row .reason{
  color:var(--muted);font-size:.8125rem;white-space:pre-wrap;
}
.material-event>button,.material-event>.actions,.audit-event>button,
.issue-item>button,.workforce-row-actions{margin-top:12px}
.material-event>button,.audit-event>button,.issue-item>button{min-height:var(--control-height-compact);padding:.35rem .85rem;font-size:.8125rem}

/* A surface nested inside a surface steps the radius down and sinks its ground,
   the same relationship the dashboard's breakdown panel has to its hero card. */
.material-event .material-event,.material-event .product-item{
  border-radius:14px;background:var(--surface-subtle);box-shadow:none;padding:13px 15px;
}
.material-event .material-event .material-event{
  border-radius:11px;background:var(--surface-sunken);
}

/* ---------- label / value metric lists ---------- */
.requirement-values{
  display:grid;gap:0;margin:10px 0 0;padding:10px 0 0;border-top:1px dashed var(--line);
}
.requirement-values>div{
  display:flex;align-items:baseline;justify-content:space-between;gap:1rem;
  padding:4px 0;min-width:0;flex-wrap:wrap;
}
.requirement-values dt{color:var(--muted);font-size:.8125rem;min-width:0;overflow-wrap:anywhere}
.requirement-values dd{
  margin:0;color:var(--ink);font-size:.8125rem;font-weight:650;
  font-variant-numeric:tabular-nums;text-align:right;overflow-wrap:anywhere;
}
.requirement-values dd small{display:block;color:var(--muted);font-size:.6875rem;font-weight:500}
.material-event>.requirement-values:first-child{margin-top:0;padding-top:0;border-top:0}

/* ---------- list containers ---------- */
.product-list{max-height:min(420px,52dvh);overflow:auto;margin:0 0 var(--gap);padding:1px}
/* Halaman workspace tidak memakai area scroll internal dialog; daftar ikut
   menggulung bersama halaman supaya seluruh SKU dapat dijangkau keyboard maupun mobile. */
#products-view .product-list{max-height:none;overflow:visible}
.product-item{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;flex-wrap:wrap}
.product-item>div{min-width:0}
.workforce-master{max-height:min(460px,58dvh)}

/* ---------- history / timeline ---------- */
.history-section{margin-top:26px}
.history-heading{margin-bottom:12px}
.history-heading h2{margin:0}
.history-item{
  gap:1rem 1.2rem;padding:14px 16px;margin-bottom:8px;
  border:1px solid var(--line);border-bottom:1px solid var(--line);border-radius:14px;
  background:var(--surface);align-items:start;
}
.history-item:last-child{margin-bottom:0}
.history-item time{
  color:var(--muted);font-size:.75rem;font-variant-numeric:tabular-nums;line-height:1.5;
}
.history-item strong{color:var(--ink);font-size:.875rem;font-weight:650}
.history-item p{margin:3px 0;font-size:.8125rem}
.history-item .hint{color:var(--muted);font-size:.75rem}
.history-item .reason{color:var(--muted);font-size:.8125rem}
.history-item>button{min-height:34px;padding:.3rem .8rem;font-size:.78125rem}
.activity-item .order-title{font-size:.875rem;font-weight:650}

/* ---------- order detail: issue list ---------- */
.issue-heading{gap:8px 12px;margin-bottom:6px}
.issue-heading h3{margin:0}

/* ---------- compact stat strips (People roster, request summaries) ---------- */
.workforce-summary{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));gap:10px;
  margin:0 0 var(--gap);border:0;
}
.workforce-summary>div{
  padding:13px 15px;border:1px solid var(--line);border-right:1px solid var(--line);
  border-radius:14px;background:var(--surface);min-width:0;
}
.workforce-summary>div:last-child{border-right:1px solid var(--line)}
.workforce-summary dt{color:var(--muted);font-size:.75rem;overflow-wrap:anywhere}
.workforce-summary dd{
  margin:3px 0 0;color:var(--ink);font-size:1.25rem;font-weight:750;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;overflow-wrap:anywhere;
}

/* ---------- People screens ---------- */
.workforce-heading{align-items:flex-start;gap:1rem;margin-bottom:var(--gap)}
.workforce-heading>div{min-width:0}
.workforce-heading p{margin-bottom:0}
.workforce-heading .hint{margin-top:2px}
.workforce-row{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(180px,auto);gap:1rem 1.25rem;
  align-items:center;
}
.workforce-row h3{margin-bottom:4px}
.workforce-row .reference{margin-bottom:2px}
.workforce-row-actions{margin-top:0;gap:8px}
.workforce-row-actions button{min-height:var(--control-height-compact);padding:.35rem .85rem;font-size:.8125rem}

/* ---------- filter toolbars inside dialogs ---------- */
.filter-form{
  margin:0 0 var(--gap);padding:16px;
  border:1px solid var(--line);border-radius:var(--r-card);
  background:var(--surface);box-shadow:var(--shadow-sm);
}
.filter-form>.hint:first-child{margin:0 0 12px}
.filter-form .form-grid{gap:12px}
.filter-form .form-actions{margin-top:14px;padding-top:0;border-top:0}
.filter-form>.form-info{margin-bottom:12px}

/* ---------- forms ---------- */
.form-grid{gap:14px}
label>input,label>select,label>textarea{margin-top:6px}
.form-actions{
  gap:10px;margin-top:20px;padding-top:16px;border-top:1px solid var(--line);
}
.check-field{
  gap:.7rem;min-height:var(--touch-target-min);padding:.5rem .8rem;
  border:1px solid var(--control-line);border-radius:var(--control-radius);background:var(--surface);
}
.line-input,.bom-line{
  gap:10px;margin:0 0 12px;padding:13px 15px;
  border:1px solid var(--line);border-radius:14px;background:var(--surface-subtle);
}
.line-input>button,.bom-line button{min-height:var(--control-height-compact);padding:.35rem .85rem;font-size:.8125rem}

/* ---------- AI surfaces ---------- */
.ai-prompts{gap:8px;margin:0 0 var(--gap)}
.ai-prompts button{min-height:34px;padding:.3rem .8rem;font-size:.78125rem}
.ai-assumptions{
  margin:0 0 var(--gap);padding:13px 16px;
  border:1px solid var(--line);border-radius:14px;background:var(--surface-subtle);
}
/* The native disclosure marker is kept. The sidebar hides it because its markup supplies an
   explicit .nav-caret from the sprite; this summary has no such element, so hiding the
   marker would leave the collapsed section looking like ordinary text. */
.ai-assumptions summary{
  color:var(--ink);font-size:.8125rem;font-weight:650;cursor:pointer;
}
.ai-assumptions .form-grid{margin-top:14px}
.ai-answer{
  margin:0 0 var(--gap);padding:18px 20px;
  border:1px solid var(--line);border-top:3px solid var(--primary);border-radius:var(--r-card);
  background:var(--surface);box-shadow:var(--shadow-sm);overflow-wrap:anywhere;
}
.ai-answer .eyebrow{margin-bottom:.25rem}
.ai-answer h3{margin-bottom:.35rem}
.ai-answer p{color:var(--ink-2);font-size:.8125rem;line-height:1.55}
.ai-answer p:last-child{margin-bottom:0}

/* ---------- raw payload viewer ---------- */
.audit-json{
  margin:0 0 var(--gap);padding:14px 16px;
  border:1px solid var(--line);border-radius:14px;background:var(--surface-sunken);
  color:var(--ink-2);font:.75rem/1.6 ui-monospace,SFMono-Regular,Consolas,monospace;
  max-height:320px;overflow:auto;white-space:pre-wrap;overflow-wrap:anywhere;
}

/* ---------- login ---------- */
.login-form{padding:26px 28px}
.login-form h2{font-size:1.25rem;font-weight:700}
.login-form>p{color:var(--muted);font-size:.8125rem}

/* ---------- responsive ---------- */
@media(max-width:980px){
}
@media(max-width:650px){
  .dialog-heading{margin-bottom:14px;padding-bottom:12px}
  .dialog-heading h2{font-size:var(--type-section-title)}
  .material-event,.audit-event,.issue-item,.product-item,.workforce-row{padding:14px 15px}
  .material-event .material-event,.material-event .product-item{padding:12px 13px}
  .workforce-summary{grid-template-columns:repeat(auto-fit,minmax(104px,1fr));gap:8px}
  .workforce-row{grid-template-columns:minmax(0,1fr)}
  .workforce-row-actions{justify-content:flex-start}
  .workforce-row-actions button{flex:1 1 auto}
  .history-item{grid-template-columns:minmax(0,1fr);gap:4px;padding:13px 15px}
  .history-item>button{justify-self:start;margin-top:8px}
  .filter-form{padding:13px}
  .login-form{padding:18px 20px}
}


/* ---------- action hierarchy inside dense toolbars ----------
   A .quiet control is borderless by design, which works for the isolated back link
   and the dialog close. Inside a toolbar it read as stray text between
   buttons, so a low-emphasis control keeps an edge while staying clearly lighter
   than a default button and lighter still than .primary. */
.actions>.quiet{
  border-color:var(--line);background:var(--surface-subtle);color:var(--ink-2);
}
.actions>.quiet:hover{
  border-color:var(--line-strong);background:var(--surface-sunken);color:var(--ink);
}
/* Corrections reverse a posted record, so they are marked as such rather than sitting
   in the same visual class as the navigation buttons beside them. */
.history-action,.history-action.quiet{
  border-color:var(--danger-edge);background:var(--danger-bg);color:var(--danger);
}
.history-action:hover,.history-action.quiet:hover{
  border-color:var(--danger);background:var(--danger-bg);color:var(--danger);
}
/* ==========================================================================
   A6.1 · Produksi containment.

   Produksi is the first workspace migrated onto the A6.0 primitives, so the
   board's and the order detail's presentation now live in
   `workspace-primitives.css`. The premium-UI block that used to sit here - a
   `#board-view`-scoped redesign of `.summary`, `#search-form`, `.order-grid`,
   `.table-head`, `.progress-note` and their three container queries - is
   DELETED rather than overridden. Two reasons, both structural:

     - every selector in it was scoped to `#board-view`, so removing it cannot
       change any other page; and
     - an id out-specifies a class, so `#board-view #search-form` and
       `#board-view #summary` would have kept the stacked filter grid and the
       old KPI card alive underneath the command bar and the metric strip. That
       is the override war A6.1 is required not to start.

   What remains is only what a shared primitive must not own:

     - the bounded column the workspace was approved with;
     - the grouping element's own stacking rhythm;
     - narrow neutralisations that stop a legacy rule still shipped for OTHER
       pages (`.state`, `button`, `.order-title`) from styling migrated markup.

   Nothing here is a component. The legacy vocabulary itself - `.summary`,
   `.filters`, `.order-grid`, `.order-row`, `.table-head`, `.status-label`,
   `.stages`, `.stage`, `.sku-block`, `.sku-balances`, `.history-item`,
   `.issue-item`, `.detail-meta`, `.list-host`, `.state` - is deliberately left
   intact above, because Command Center, Bahan baku, People, Aktivitas, Audit,
   the approval queues and every production child dialog still render with it.
   A6.1 contains its migration instead of widening it.
   ========================================================================== */
#board-view,#detail-view{max-width:1360px;margin-inline:auto}
/* The board's grouping element. It deliberately carries no material of its own: under
   A6/(S)53 the near-opaque surface is the data surface, and a second card wrapped around it
   would put two borders and two shadows between the wallpaper and the table. */
.production-work{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
/* Both state hosts keep their id, their `role="status"` and their `.state` class, because
   the request lifecycle, `fail()` and the M4 motion contract all address them by exactly
   those - the motion module asserts that `#board-message.className` returns to `state`
   after the empty-state fade. Their BOX, though, now comes from the A6 state primitive
   rendered inside them, so the legacy `.state` card is withdrawn here; without this every
   loading, empty and error state would draw a second bordered panel around the first. */
#board-message,#detail-message{padding:0;border:0;border-radius:0;background:none;color:inherit;text-align:left;font-size:inherit}
/* The open-issue summary is an A6 attention note that happens to be a <button>. It needs
   the element resets the primitive has no reason to carry, and its hover must not fall back
   to the generic button hover, which would drop the semantic tint the state is carried by. */
#issues-summary{width:100%;min-height:0;box-shadow:none;font-weight:500}
#issues-summary:hover{background:var(--attention-tint);border-color:var(--attention-ink)}
/* The CLEARED state is a different weight of message from the raised one, and it was being given
   the same one: a full-width, two-line, green-bordered panel announcing that nothing is wrong. An
   all-clear should be confirmable at a glance and then ignored, so it collapses to a single
   shrink-wrapped row - icon, count, reason - with the green kept in the tint and the check rather
   than in a callout edge. The raised state below is untouched and stays prominent.
   Only the layout changes: the same element, the same DOM, the same click behaviour, and the same
   `has-issues` / `attention-note-success` state classes the lifecycle already toggles. */
#issues-summary.attention-note-success{
  width:auto;max-width:100%;align-self:flex-start;
  align-items:center;gap:.45rem;padding:5px 11px;
  border-color:var(--workspace-edge);
}
#issues-summary.attention-note-success>.attention-note-copy{
  flex:0 1 auto;flex-direction:row;align-items:baseline;flex-wrap:wrap;gap:0 .35rem;
}
#issues-summary.attention-note-success .attention-note-title{font-size:var(--type-metadata);font-weight:650}
#issues-summary.attention-note-success .attention-note-reason{color:var(--muted);font-size:var(--type-metadata)}
/* The separator is decorative punctuation between two runs of text, so it is drawn rather than
   written: it must not enter the accessible name or the element's text content. */
#issues-summary.attention-note-success .attention-note-reason::before{content:"·";margin-right:.35rem;color:var(--color-separator-strong)}
#issues-summary.attention-note-success>.icon{margin-top:0}
/* Secondary information, not a panel. The stage-filter explanation keeps every word and keeps its
   `aria-describedby` relationship, but it no longer needs a filled surface and its own padding to
   say "read me after the command bar" - size and position already say it. Withdrawing the panel is
   what buys the order list its place higher in the first viewport. */
#production-filter-hint{padding:1px 0 0;border-radius:0;background:none;font-size:var(--type-metadata);line-height:1.4}
/* M4's refresh dim stays authoritative on this list. `markRefreshing()` sets BOTH `.is-refreshing`
   and `aria-busy`, and A6 dims a busy data surface to .6 while M4 documents .78 - and A6 wins on
   specificity and load order. The migrated board keeps the documented M4 value, because the
   refresh treatment here is an existing, reviewed behaviour that A6.1 is not re-opening. The
   transition itself is still the one declared in the reduced-motion gate of `style.css`. */
#order-list.is-refreshing{opacity:.78}
/* Back to the board. `.workspace-page` is a flex column, so a full-width stretch plus the
   foundation's `button{justify-content:center}` was parking "Semua order" near the middle of the
   page, reading as a floating control that belonged to nothing. Shrink-wrapped and left-aligned,
   it sits directly above the order identity and reads as part of it - which is also why the
   section gap here is tighter than the page's own rhythm. Behaviour and focus restoration are
   untouched; this is alignment only. */
#detail-view{gap:10px}
#detail-view>#back{align-self:flex-start;justify-content:flex-start;margin:0}
/* ---------- A6.1 order-detail utility rows ----------
   An action that OPENS something is not an action that DOES something, and the order detail is
   mostly the former: of its 27 actions, 24 open a record, a history or a list. Rendering all of
   them as equally loud capsules produced a wall that dominated the page and flattened the three
   things that are real commands.

   So navigational actions become rows in a grouped utility list - label, then a chevron that says
   "this opens a page" - and only genuine commands stay button-shaped. The grammar is A6.0's own
   `.record-row`; what is declared here is purely the <button> element resets that primitive has no
   reason to carry, which is the same containment already used for `#issues-summary`. No action was
   removed, no `data-action` changed, and no popover or menu architecture was introduced.

   Deliberately denser than a data row: a utility list is navigation, not content, so it uses its
   own compact step instead of `--workspace-row-height`. The `max-width:980px` touch floor above
   still lifts every one of these to 44px on a narrow viewport. */
#detail-content .utility-rows{display:flex;flex-direction:column;min-width:0}
#detail-content .utility-rows>.record-row{
  min-height:36px;padding:5px 6px;
  border:0;border-bottom:1px solid var(--workspace-divider);border-radius:0;
  background:none;box-shadow:none;color:var(--ink);
  justify-content:flex-start;text-align:left;gap:.6rem;
  font-size:var(--type-control);font-weight:500;letter-spacing:0;
}
#detail-content .utility-rows>.record-row:last-child{border-bottom:0}
/* The generic `button:hover` would repaint the row with the control surface and its edge, which
   is exactly the capsule look these rows exist to leave behind. */
#detail-content .utility-rows>.record-row:hover{background:var(--workspace-hover);border-color:var(--workspace-divider);color:var(--ink)}
#detail-content .utility-rows>.record-row>.icon{color:var(--muted)}
#detail-content .utility-rows>.record-row:hover>.icon{color:var(--ink-2)}
/* A position holding nothing is still stated, with its real number, and simply reads quieter than
   a position holding stock. This is emphasis, not filtering: no quantity is hidden or altered. */
#detail-content .balance-idle>dd{color:var(--muted);font-weight:500}
/* The product's own narrow-width touch floor, restored for the migrated controls that
   legitimately out-specify it. `button,input,select,textarea{min-height:44px}` is declared
   globally at this breakpoint, but a class beats a bare element selector, so A6's compact
   control height (and `.order-title`'s shared `min-height:auto` reset, which cannot be changed
   at the source because Aktivitas renders it too) would silently drop these below 44px on a
   narrow viewport. A6.0's `pointer:coarse` step is ADDITIVE to that policy, not a replacement
   for it: this page may not lose a target size it already guaranteed. */
@media(max-width:980px){
  #board-view button,#detail-view button,
  #board-view .command-filter,#board-view .command-filter>select,
  #board-view .command-search input{min-height:var(--touch-target-min)}
}
/* A phone cannot fit the cleared note's count and its reason on one line, so it stops pretending
   to: the two runs stack and the separator is withdrawn rather than left to orphan at the start of
   the wrapped line. Still the quiet treatment - small type, soft tint, no callout edge - just over
   two lines instead of one. */
@media(max-width:650px){
  #issues-summary.attention-note-success{width:100%;align-items:flex-start}
  #issues-summary.attention-note-success>.attention-note-copy{flex-direction:column;gap:.1rem}
  #issues-summary.attention-note-success .attention-note-reason::before{content:none;margin:0}
}


/* ==========================================================================
   A6.2 - Bahan baku + Master SKU inner-workspace containment
   --------------------------------------------------------------------------
   The same narrow job the A6.1 block above does for Produksi, done once more for the
   two master-data workspaces: the bounded column they were approved with, the
   grouping element's own stacking rhythm, and the handful of neutralisations that stop
   a legacy rule still shipped for OTHER pages (`.state`, `.product-list`, `button`,
   `.order-title`) from styling migrated markup.

   Nothing here is a component. No A6.0 primitive is redefined and no A6.1 selector is
   touched. Every rule is scoped to an id or to one of the two grouping classes, so it
   cannot reach another workspace by accident - which is what the containment test in
   `tests/test_apple27_materials_master_sku_contract.py` asserts selector by selector.

   The legacy vocabulary these pages used to render with - `.filters`, `.sku-block`,
   `.sku-heading`, `.material-balance`, `.product-item` - is deliberately left intact
   above. `.filters` is still rendered by People and Aktivitas, `.product-item` by four
   other dialogs, and `.bundle-label` by Bundle and Barang jadi plus the print
   stylesheet. The two selectors that A6.2 stops emitting entirely (`.material-balance`,
   `#batch-list .sku-block`) are left in place rather than cleaned up, because A6.1's
   documented rule is that inert legacy CSS is removed by the phase that can prove the
   removal safe on its own, not opportunistically by the next migration.
   ========================================================================== */
#materials-view,#products-view{max-width:1360px;margin-inline:auto}
/* Both grouping elements carry no material of their own: the near-opaque surface is the
   data surface / record list, and a second card around it would put two borders and two
   shadows between the wallpaper and the rows. */
.materials-work,.products-work{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
/* Both state hosts keep their id, their `role="status"` and their `.state` class, because
   the request lifecycle, `fail()` and the M4 motion contract address them by exactly
   those. Their BOX now comes from the A6 state primitive rendered inside them, so the
   legacy `.state` card is withdrawn - without this every loading, empty and error state
   would draw a second bordered panel around the first. */
#materials-message,#products-message{padding:0;border:0;border-radius:0;background:none;color:inherit;text-align:left;font-size:inherit}
/* Secondary information, not a panel. The inventory explanation and the mapping note keep
   every word and keep their `aria-describedby` relationship, but they do not need a filled
   surface to say "read me after the command bar" - size and position already say it.
   Withdrawing the panel is what buys the batch list and the SKU list their place in the
   first viewport.
   The measure is bounded for the same reason the page subtitle is: a three-sentence definition of
   what saldo and stok bebas mean is read once and has to be readable, and 1360px of it is not a
   line, it is a ribbon. */
#materials-inventory-note,#products-mapping-note{max-width:96ch;padding:1px 0 0;border-radius:0;background:none;font-size:var(--type-metadata);line-height:1.4}
/* M4's refresh dim stays authoritative on both lists. `markRefreshing()` sets BOTH
   `.is-refreshing` and `aria-busy`, and A6 dims a busy data surface to .6 while M4
   documents .78. The migrated pages keep the documented M4 value, exactly as the board
   does, because the refresh treatment is an existing reviewed behaviour A6.2 is not
   re-opening. */
#batch-list.is-refreshing,#product-list.is-refreshing{opacity:.78}
/* The legacy `.product-list` scroll box belongs to the three dialogs that still render it.
   On the page itself the catalog is the page, so it neither clips nor carries a bottom
   margin the flex column already provides. */
#product-list{max-height:none;overflow:visible;margin:0;padding:0}
/* Master bahan is a long identity list inside a dialog whose own primary action sits
   below it, so the list keeps its own scroll rather than pushing the action out of reach.
   `.record-list` declares `overflow:hidden`, and an id is what re-opens it. */
#material-master-list{max-height:min(420px,52dvh);overflow:auto}
/* Quantity emphasis, not authority. Every quantity is labelled by its own column header;
   what changes here is only which one the eye lands on first. Free stock is what an
   operator can actually commit, the balance is the physical figure and reads at normal
   weight, and the reservation is context. `forced-colors` collapses these differences,
   which is why the column header - not the weight - is what carries the meaning. */
#batch-list [data-batch-balance]{color:var(--ink)}
#batch-list [data-batch-available]{color:var(--ink);font-weight:650}
#batch-list [data-batch-reserved]{color:var(--muted)}
/* Seven real columns of inventory need more room than the shared 44rem floor before the
   identifiers start breaking a character per line, so this surface raises its own floor.
   Still rem, not px: at 200% text the columns grow with the text instead of re-crushing
   at twice the size. Overflow is handed to the surface by A6.0's own 980px rule. */
@media(max-width:980px){
  #batch-list>table{min-width:56rem}
  /* The product's own narrow-width touch floor, restored for the migrated controls that
     legitimately out-specify it. `button,input,select,textarea{min-height:44px}` is
     declared globally at this breakpoint, but a class beats a bare element selector, so
     A6's compact control height (and `.order-title`'s shared `min-height:auto` reset,
     which cannot be changed at the source because Aktivitas renders it too) would
     silently drop these below 44px on a narrow viewport. A6.0's `pointer:coarse` step is
     ADDITIVE to that policy, not a replacement for it. */
  #materials-view button,#products-view button,
  #materials-view .command-filter,#materials-view .command-filter>select,
  #products-view .command-search input{min-height:var(--touch-target-min)}
}



/* ==========================================================================
   A6.3 - People + Scan bundle + Scan barang jadi inner-workspace containment
   --------------------------------------------------------------------------
   The same narrow job the A6.1 and A6.2 blocks above do, done once more for the
   three workflow workspaces A6.3 owns: the bounded column they were approved with, the
   grouping element's own stacking rhythm, and the handful of neutralisations that stop a
   legacy rule still shipped for OTHER pages (`.state`, `.error`, `button`, `h3`) from
   styling migrated markup.

   Nothing here is a component. No A6.0 primitive is redefined and no A6.1 or A6.2
   selector is touched. Every rule is scoped to an id, to one of the two grouping
   classes, or to `#dialog-content` + an A6 primitive that only a migrated renderer is
   allowed to emit - which is what makes the last group safe: an unmigrated dialog cannot
   be reached by `#dialog-content .utility-rows`, because emitting `.utility-rows` at all
   is exactly what the A6.0 containment test forbids outside the migrated renderers.

   The legacy vocabulary these surfaces used to render with - `.filters`, `.page-heading`,
   `.eyebrow`, `.hint`, `.search-field`, `.workforce-summary`, `.workforce-row`,
   `.workforce-row-actions`, `.workforce-heading`, `.product-list`, `.product-item`,
   `.history-item`, `.form-info`, `.form-grid`, `.form-actions`, `.filter-form`,
   `.material-event`, `.scan-result`, `.bundle-label` - is deliberately left intact above.
   `.filters` is still rendered by Aktivitas, `.page-heading` by eleven other pages,
   `.form-info`/`.form-grid`/`.form-actions` by every `formDialog()`, `.material-event` and
   `.history-item` by the production and warehouse child dialogs, and `.scan-result` and
   `.bundle-label` are still rendered by these very pages (the tint host and the printable
   label). The four selectors A6.3 stops emitting entirely (`.workforce-summary`,
   `.workforce-row`, `.workforce-row-actions`, `.workforce-heading`) are left in place
   rather than cleaned up, because A6.1's documented rule is that inert legacy CSS is
   removed by the phase that can prove the removal safe on its own, not opportunistically
   by the next migration.
   ========================================================================== */
#people-view,#bundle-scan-view,#finished-goods-scan-view{max-width:1360px;margin-inline:auto}
/* The roster's grouping element. It deliberately carries no material of its own: the
   near-opaque surface is the record list, and a second card wrapped around it would put two
   borders and two shadows between the wallpaper and the rows. */
.people-work{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
/* Every state host keeps its id, its `role="status"` and its `.state` class, because the
   request lifecycle, `message()`, `fail()` and the M4 motion contract all address them by
   exactly those - the motion module asserts that `#workforce-list.className` returns to
   `list-host` after the replacement fade. Their BOX now comes from the A6 state primitive
   rendered inside them, so the legacy `.state` card is withdrawn here; without this every
   loading, empty and error state would draw a second bordered panel around the first.
   The two scanner hosts are written with `textContent`, not a primitive, so they keep the
   `.scan-state` row they were given in the markup and only lose the card. */
#workforce-message,#workforce-request-message,#bundle-scan-message,#finished-goods-scan-message{
  padding:0;border:0;border-radius:0;background:none;color:inherit;text-align:left;font-size:inherit;
}
/* A rejected scan is the one thing on a scanner page that must not be missed, so it keeps a
   real surface - but an A6 one, built from the danger tokens the rest of the product already
   uses, instead of the legacy `.error` block's own geometry. `fail()` writes `textContent`
   here, so the treatment has to live in CSS. */
#bundle-scan-error,#finished-goods-scan-error{
  display:flex;align-items:flex-start;gap:.6rem;min-width:0;margin:0;
  padding:10px 12px;border:1px solid var(--color-danger-edge);border-radius:var(--workspace-panel-radius);
  background:var(--color-danger-soft);color:var(--color-danger);
  font-size:var(--type-caption);line-height:1.45;
}
/* Secondary information, not a panel. The revision explanation keeps every word and keeps its
   `aria-describedby` relationship, but it does not need a filled surface to say "read me after
   the command bar" - size and position already say it. Withdrawing the panel is what buys the
   roster its place in the first viewport.
   The measure is bounded for the same reason the page subtitle is: a two-sentence definition of
   what stays visible and what a correction does is read once and has to be readable, and 1360px
   of it is not a line, it is a ribbon. */
#workforce-revision-note{max-width:96ch;padding:1px 0 0;border-radius:0;background:none;font-size:var(--type-metadata);line-height:1.4}
/* The employee name and the request employee are headings, because a roster row is a record
   about a person and the browser contract has always exposed them as such. `h3` carries the
   product's own type scale and margins, neither of which belongs on a row's primary line. */
#workforce-list h3,#workforce-request-list h3{margin:0;font-size:var(--type-control);font-weight:620;letter-spacing:-.005em}
/* ---------- A6.3 the daily state is a strip, not six dashboard cards ----------
   Six figures describing ONE daily roster state have to read as one row. A6.0's shared
   `auto-fit,minmax(190px,1fr)` is right for a strip whose length it cannot know, but here the
   length is exactly six and always six: at 1360px it lands on five tracks and orphans the sixth,
   and a lone card with four empty columns beside it reads as a seventh figure that failed to load.
   So the count is stated - six across when six fit, otherwise 3+3, then 2+2+2 - and the cells stay
   compact, because the roster is the page and the state above it is a header, not the content. */
@media(min-width:1180px){#workforce-summary{grid-template-columns:repeat(6,minmax(0,1fr))}}
@media(max-width:1179px){#workforce-summary{grid-template-columns:repeat(3,minmax(0,1fr))}}
#workforce-summary .metric-card{min-height:0;gap:.15rem;padding:11px 13px}
#workforce-summary .metric-icon{margin-bottom:.05rem}
#workforce-summary .metric-value{font-size:1.25rem}
/* Master karyawan is a long identity list inside a dialog whose own primary action sits above
   it, so the list keeps its own scroll rather than growing the dialog past the viewport.
   `.record-list` declares `overflow:hidden`, and an id is what re-opens it. */
#workforce-master-list{max-height:min(460px,58dvh);overflow:auto}
/* ---------- A6.3 scanner surface ----------
   The input is the page. A6.0's `.scan-target` already gives the 44px control and the 1.05rem
   tabular face; what is added here is only the emphasis that makes the eye land on it first -
   a wider measure than a form field would get, and a label that reads as an instruction rather
   than a caption. Nothing about the keyboard path changes: no focus rule, no transition. */
#bundle-scan-form,#finished-goods-scan-form{max-width:44rem}
/* `.scan-target` is a centred row and `.field` turns it into a column, but `align-items:center`
   survives that switch and centres the label over the input. A field label is the start of a line
   of text, not a caption under a picture: it aligns with the control it names. */
#bundle-scan-form .scan-target,#finished-goods-scan-form .scan-target{align-items:stretch;gap:.45rem}
#bundle-scan-form .field-label,#finished-goods-scan-form .field-label{font-size:var(--type-caption);font-weight:650}
/* A6.0 gave `.scan-target input` a 44px floor and a 1.05rem tabular face, and composing the field
   grammar onto the same element silently takes both away: `.scan-target input` and `.field>input`
   have IDENTICAL specificity (0,1,1), and `.field>input` is declared later in the sheet, so the
   scan field was rendering at the compact 34px control height. This restores A6.0's stated intent
   for the two pages that own it, rather than reordering a shared primitive sheet underneath every
   other surface that consumes `.field`. The field is the page; it may not be the smallest control
   on it. */
#bundle-scan-form .scan-target>input,#finished-goods-scan-form .scan-target>input{
  min-height:48px;padding:.45rem .7rem;font-size:1.05rem;font-variant-numeric:tabular-nums;
}
/* The success tint lives on the `.scan-result` host, so nothing rendered inside it may carry an
   opaque background - that colour is the only feedback a scanner gives, and the motion contract
   measures the host's own resting colour. The result row therefore drops the record list's
   surface, its divider and its hover repaint, and keeps only the layout. */
#bundle-scan-result .record-row,#finished-goods-scan-result .record-row{
  padding:10px 0;border-bottom:0;background:none;
}
#bundle-scan-result .record-row:hover,#finished-goods-scan-result .record-row:hover{background:none}
#bundle-scan-result>h2,#finished-goods-scan-result>h2{margin:0 0 .35rem}
#bundle-scan-result h3,#finished-goods-scan-result h3{margin:0;font-size:var(--type-card-title);font-weight:650}
/* A scan returns two facts, and they belong beside each other where the eye already is. The shared
   `auto-fit` track would stretch them across the full width of the page and read as two unrelated
   columns, so the result shrink-wraps its own grid instead. */
#bundle-scan-result .detail-grid,#finished-goods-scan-result .detail-grid{
  grid-template-columns:repeat(2,minmax(0,max-content));gap:.2rem 1.75rem;margin-top:.35rem;
}
/* ---------- A6.3 scan-result dialog utility rows ----------
   The same reasoning A6.1 recorded for the order detail, and the same rules: of the bundle
   detail's twelve actions seven only OPEN something, and of the finished-goods detail's
   nineteen, twelve do. Rendering them all as equally loud capsules produced the wall these
   two dialogs exist to leave behind, so navigational actions become rows in a grouped utility
   list and only genuine commands stay button-shaped. What is declared here is purely the
   <button> element resets the `.record-row` primitive has no reason to carry. No action was
   removed, no `data-action`, `data-kind` or id changed, and no popover or menu was introduced. */
#dialog-content .utility-rows{display:flex;flex-direction:column;min-width:0}
#dialog-content .utility-rows>.record-row{
  min-height:36px;padding:5px 6px;
  border:0;border-bottom:1px solid var(--workspace-divider);border-radius:0;
  background:none;box-shadow:none;color:var(--ink);
  justify-content:flex-start;text-align:left;gap:.6rem;
  font-size:var(--type-control);font-weight:500;letter-spacing:0;
}
#dialog-content .utility-rows>.record-row:last-child{border-bottom:0}
/* The generic `button:hover` would repaint the row with the control surface and its edge, which
   is exactly the capsule look these rows exist to leave behind. */
#dialog-content .utility-rows>.record-row:hover{background:var(--workspace-hover);border-color:var(--workspace-divider);color:var(--ink)}
#dialog-content .utility-rows>.record-row>.icon{color:var(--muted)}
#dialog-content .utility-rows>.record-row:hover>.icon{color:var(--ink-2)}
/* The two result dialogs lead with an identity line and a primary fact rather than a section
   title, so the shared `#dialog-content>h3` step - sized and spaced for a section break - is
   withdrawn for that one line only. */
#dialog-content>h3.data-primary{margin:0;font-size:var(--type-card-title)}
#dialog-content>.workspace-meta{display:block}
/* Inventori sekarang keeps three separate numbers per sellable row, and they have to read as a ROW
   of figures beside the location they belong to. The shared `auto-fit` track collapses to a single
   column inside a row aside, which stacked quantity/available/reserved vertically and - because a
   record row centres its children - dragged the location and its chip down to the middle of a tall
   row. Each figure gets its own track instead, and the row tops-aligns so identity and figures
   start on the same line. */
#dialog-content .record-list .record-row{align-items:flex-start}
#dialog-content .record-row-aside>.detail-grid{
  grid-auto-flow:column;grid-template-columns:none;gap:.2rem 1.5rem;min-width:0;
}
@media(max-width:980px){
  /* The product's own narrow-width touch floor, restored for the migrated controls that
     legitimately out-specify it. `button,input,select,textarea{min-height:44px}` is declared
     globally at this breakpoint, but a class beats a bare element selector, so A6's compact
     control height would silently drop these below 44px on a narrow viewport. A6.0's
     `pointer:coarse` step is ADDITIVE to that policy, not a replacement for it: these pages
     may not lose a target size they already guaranteed. */
  #people-view button,#bundle-scan-view button,#finished-goods-scan-view button,
  #people-view .command-filter,#people-view .command-filter>select,
  #people-view .command-filter>input,#people-view .command-search input{min-height:var(--touch-target-min)}
  /* A utility row is navigation, so it follows the same floor inside the dialog. */
  #dialog-content .utility-rows>.record-row{min-height:var(--touch-target-min)}
}
@media(max-width:650px){
  /* A phone shows the scanner at full width: a bounded measure that was emphasis on a desktop
     becomes an arbitrary indent once the viewport is the measure. */
  #bundle-scan-form,#finished-goods-scan-form{max-width:none}
  /* Three figures in a column track need room a phone does not have, so they take the row's own
     width and share it equally rather than overflowing the dialog. */
  #dialog-content .record-row-aside>.detail-grid{
    grid-auto-flow:row;grid-template-columns:repeat(3,minmax(0,1fr));width:100%;
  }
  /* Six roster figures cannot hold a 140px track at 200% text on a 320px viewport without the
     labels breaking a word per line, so the strip stops pretending to be a strip and becomes
     two deliberate columns. The numbers and their units are untouched. */
  #workforce-summary{grid-template-columns:repeat(2,minmax(0,1fr))}
}


/* ==========================================================================
   A6.4 - Analitik inner-workspace containment
   --------------------------------------------------------------------------
   The same narrow job the A6.1, A6.2 and A6.3 blocks above do, done once more for the
   ONE host that twelve reports share. Analitik is different from every phase before it in
   exactly one way: there is no per-report `<section>` in index.html to scope rules to,
   because all twelve destinations render into `#analytics-body`. So the grouping vocabulary
   here is four classes that only a migrated analytics renderer is allowed to emit
   (`.analytics-report`, `.analytics-filters`, `.analytics-record`, `.analytics-subgroup`
   and their parts) plus the report-owned ids - which is what makes it safe: an unmigrated
   surface cannot be reached by `.analytics-record`, because emitting it at all is what the
   A6.0 containment test forbids outside the allowlisted renderers.

   Nothing here is a component. No A6.0 primitive is redefined and no A6.1, A6.2 or A6.3
   selector is touched. The quantity-bar family the reports draw their distributions with is
   NOT here - it is a real primitive and lives in workspace-primitives.css with the rest.

   The legacy vocabulary these twelve reports used to render with - `.filter-form`,
   `.form-grid`, `.form-actions`, `.form-info`, `.material-event`, `.requirement-values`,
   `.status-label`, `.hint`, `.page-heading`, `.eyebrow`, `.history-item`, `.product-list` -
   is deliberately left intact above. `.filter-form` is still rendered by Aktivitas' audit
   filter, `.form-grid`/`.form-actions`/`.form-info` by every `formDialog()`,
   `.material-event`/`.requirement-values`/`.status-label`/`.hint` by the production,
   warehouse, purchasing and Command Center surfaces A6.5-A6.7 still own, and
   `.product-list` by the Master SKU catalog host. The two selectors A6.4 makes fully inert
   (`.issue-heading`, `.product-item`) are left in place rather than cleaned up, because
   A6.1's documented rule is that inert legacy CSS is removed by the phase that can prove
   the removal safe on its own, and proving it needs the A6.5-A6.7 sweep this phase is not
   allowed to start.
   ========================================================================== */
#analytics-view{max-width:1360px;margin-inline:auto}
/* The report question. It is the one line that tells the operator what this page can answer,
   so it gets a bounded measure for the same reason every other page subtitle does: a sentence
   read once has to be readable, and 1360px of it is a ribbon rather than a line. */
#analytics-subtitle{max-width:84ch}
/* The shared report host. `#analytics-body` is replaced wholesale by whichever renderer owns
   it, so the rhythm between a filter surface, the assumptions, a summary, a comparison and the
   records cannot live in the markup - the renderer would have to restate it twelve times. The
   gap is the PANEL step, not the section step: Analitik is meant to read dense, and twenty
   pixels between two explanatory panels reads as two unrelated pages. */
#analytics-body{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
#analytics-body>*{min-width:0}
/* One stacking rhythm for a report or a part of one. Used both for the grouping `<section>`s
   Kapasitas needs (master, then plan) and for the summary hosts every report writes into. They
   deliberately carry no material of their own: the near-opaque surfaces are the record list and
   the metric cards, and a card wrapped around them would put two borders between the wallpaper
   and the data. */
.analytics-report{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
.analytics-report>*{min-width:0}
/* The filter surface is one <form> in two tiers: the command bar the operator touches every
   day, and the assumptions panel that states the thresholds the numbers depend on. The tiers
   are stacked rather than merged because forcing ten controls onto one horizontal line is the
   legacy experience this phase exists to leave behind - and they stay in the SAME form so
   FormData, the saved filter values and every parameter name are untouched. */
.analytics-filters{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
/* Every state host keeps its id, its `role="status"` and its `.state` class, because the
   request lifecycle, `message()` and `pageState()` all address them by exactly those. Their BOX
   now comes from the A6 state primitive rendered inside them, so the legacy `.state` card is
   withdrawn here; without this every loading, ready, empty and error state in Analitik would
   draw a second bordered panel around the first. */
#wip-ageing-message,#capacity-plan-message,#capacity-plan-boot,#production-quality-message,
#supplier-performance-message,#material-price-message,#purchase-commitment-message,
#forecast-message,#replenishment-message,#size-demand-message,#return-insights-message,
#dead-stock-message,#stock-adjustment-insights-message{
  padding:0;border:0;border-radius:0;background:none;color:inherit;text-align:left;font-size:inherit;
}
/* ---------- A6.4 the composite analytics record ----------
   Six of the twelve reports describe a subject that owns nested evidence - a work centre owns
   orders, a supplier owns QC results per unit, a material owns its PO history. A `.record-row`
   is a ROW and cannot hold that; the legacy answer was `.material-event` inside
   `.material-event` inside `.material-event`, which is the card-inside-a-card feeling A6.4 is
   asked to remove. So the record stays ONE list item inside the ONE `.record-list` surface -
   it gets the list's divider and nothing else. No border of its own, no radius, no shadow, no
   second background. The nesting becomes typographic, not material. */
.analytics-record{
  display:flex;flex-direction:column;gap:.5rem;min-width:0;
  padding:14px var(--workspace-cell-x);border-bottom:1px solid var(--workspace-divider);
  overflow-wrap:anywhere;
}
.analytics-record:last-child{border-bottom:0}
.analytics-record>p{margin:0}
/* Identity on the left, status on the right, one line when they fit. `baseline` rather than
   `center` so a two-line reference does not drag its chip to the middle of the record. */
.analytics-record-head{display:flex;align-items:baseline;justify-content:space-between;gap:.35rem .9rem;flex-wrap:wrap;min-width:0}
/* A record is a record ABOUT something, so its primary line has always been a heading and the
   browser contract exposes it as one. `h3` carries the product's own card-title scale and
   margins, neither of which belongs on a dense analytical row. */
.analytics-record-head>h3{min-width:0;margin:0;font-size:var(--type-control);font-weight:620;letter-spacing:-.005em}
.analytics-record-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-top:.15rem}
/* ---------- A6.4 quiet subsection ----------
   The replacement for a nested card. A rule, a small label, and divider rows: enough to say
   "these belong to the record above" without another surface. */
.analytics-subgroup{display:flex;flex-direction:column;gap:.2rem;min-width:0;padding-top:.4rem;border-top:1px solid var(--workspace-divider)}
.analytics-subgroup-title{margin:0;color:var(--muted);font-size:var(--type-metadata);font-weight:650;letter-spacing:0}
.analytics-sublist{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}
.analytics-sublist>li{display:flex;flex-direction:column;gap:.1rem;min-width:0;padding:.45rem 0;border-bottom:1px solid var(--workspace-divider)}
.analytics-sublist>li:last-child{border-bottom:0}
.analytics-sublist>li>h5{margin:0}
.analytics-sublist-title{min-width:0;color:var(--ink);font-size:var(--type-caption);font-weight:620;overflow-wrap:anywhere}
.analytics-sublist-action{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding-top:.25rem}
/* A result list with no rows yet - before the first run, after a reset, or when the report
   answered with nothing - must not draw an empty bordered box under the empty state that
   already explains why. */
#analytics-body .record-list:empty{display:none}
/* The pagination row already has its own top margin for the pages that place it after a table.
   Inside a flex stack that margin is added to the gap and reads as a dropped footer. */
#analytics-body .pagination{margin-top:0}
/* ---------- A6.4 Kapasitas master ----------
   The work-centre master is a long identity list that sits ABOVE the report it configures, so
   it keeps its own scroll rather than pushing the plan off the first viewport. `.record-list`
   declares `overflow:hidden`, and an id is what re-opens it. */
#capacity-center-master{max-height:min(420px,52dvh);overflow:auto}
#capacity-center-master h3{margin:0;font-size:var(--type-control);font-weight:620;letter-spacing:-.005em}
@media(max-width:980px){
  /* The product's own narrow-width touch floor, restored for the migrated controls that
     legitimately out-specify it. `button,input,select,textarea{min-height:44px}` is declared
     globally at this breakpoint, but a class beats a bare element selector, so A6's compact
     control height would silently drop these below 44px on a narrow viewport. A6.0's
     `pointer:coarse` step is ADDITIVE to that policy, not a replacement for it: a report may
     not lose a target size the product already guaranteed. */
  #analytics-view button,#analytics-view .command-filter,#analytics-view .command-filter>select,
  #analytics-view .command-filter>input,#analytics-view .command-search input,
  #analytics-view .field>input,#analytics-view .field>select{min-height:var(--touch-target-min)}
}
@media(max-width:650px){
  /* A phone reads a record as a column. The head stops trying to put identity and status on one
     line, because at this width "PO-2026-0031 · SUPPLIER-QA" and its chip cannot share a row
     without one of them wrapping mid-token. */
  .analytics-record{padding:12px var(--workspace-cell-x)}
  .analytics-record-head{flex-direction:column;align-items:flex-start;gap:.3rem}
  /* An analytics filter becomes a labelled field on a phone. A6.0's `.command-filter` is a
     fixed-height inline row - label beside control - which is right for a bar of short filters. A
     report's filters are named after the business question they ask ("Data demand sampai tanggal",
     "Ambang tanpa demand (hari)", "Batas segera jatuh tempo (hari)"), and at 200% text that label
     alone measures wider than a 320px viewport. Beside a control it cannot fit, and because the
     label is a `<span>` in an `inline-flex` row it cannot wrap either: measured at 320px/200% the
     host ran 106px past its own width.
     So inside Analitik, and only at this breakpoint, the filter turns the corner: label above
     control, label allowed to wrap, control at full width and at the product's touch floor. The
     control keeps its name, its type and every attribute - this is layout, not a different field. */
  #analytics-view .command-filter{
    flex:1 1 100%;flex-direction:column;align-items:stretch;
    height:auto;padding:.3rem .6rem;gap:.1rem;
  }
  #analytics-view .command-filter>span{overflow-wrap:anywhere;white-space:normal}
  #analytics-view .command-filter>select,#analytics-view .command-filter>input{
    width:100%;min-width:0;min-height:var(--touch-target-min);
  }
  /* A6.0 gave `.detail-field>dd` `overflow-wrap:anywhere` but not its `<dt>`, which is invisible
     until a label is long AND the text is large: "Ambang porsi penerimaan" in a 96px compact track
     at 200% text is wider than the track and the grid cannot shrink it. The wrap is added here
     rather than in the primitive sheet, because A6.4 is not allowed to change how A6.1, A6.2 and
     A6.3 render - the cross-product reconciliation of this is A6.8's job. */
  #analytics-view .detail-field>dt{overflow-wrap:anywhere}
  /* Two of the twelve reports have a metric whose value is legitimately a WORD rather than a
     quantity - the WIP signal is a stage name, the open commitment is a formatted rupiah figure -
     and a 1.5rem face at 200% text makes a single word wider than a 320px card. The figure is
     narrowed rather than clipped, and mid-word breaking stays available as the last resort so
     nothing is ever cut off or pushed past the host. */
  #analytics-view .metric-value{font-size:1.2rem;overflow-wrap:anywhere}
  #analytics-view .metric-label{overflow-wrap:anywhere}
}


/* ==========================================================================
   A6.5 - Tanya Beeloft + Integrasi inner-workspace containment
   --------------------------------------------------------------------------
   The same narrow job the A6.1-A6.4 blocks above do, done once more for the two
   workspaces A6.5 owns and the dialogs they launch. They share ONE small evidence
   grammar (`.evidence-*`) because both read evidence - one investigates the ledger,
   the other inspects vendor snapshots - but they are deliberately NOT one page
   composition: Tanya Beeloft is a composer above an answer, Integrasi is a set of
   system surfaces with scope rows.

   Everything here is reachable only from markup a migrated A6.5 renderer emits: the
   two section ids, the A6.5-owned host ids inside them and inside the dialogs, and
   the `.investigation*` / `.integration*` / `.evidence*` / `.ai-*` names that only
   those renderers write. Nothing here is a component and no A6.0 primitive is
   redefined; the A6.1-A6.4 blocks are not touched.

   The legacy vocabulary these surfaces used to render with - `.page-heading`,
   `.eyebrow`, `.filter-form`, `.form-grid`, `.form-actions`, `.form-info`,
   `.material-event`, `.status-label`, `.requirement-values`, `.history-section`,
   `.history-heading`, `.ai-answer`, `.ai-prompts` - is left intact above: every one
   of them is still rendered by a surface A6.6-A6.7 own (Activity, Audit, Purchasing,
   Approval Inbox) or by `formDialog()` itself. `.ai-answer` and `.ai-prompts` become
   inert here and are left for A6.8's proven sweep, as A6.1's rule requires.
   `.ai-assumptions` stays in the markup because showAi() addresses
   `details.ai-assumptions`; its legacy box is withdrawn below by id, not removed.
   ========================================================================== */
#ai-view,#integrations-view{max-width:1360px;margin-inline:auto}
#ai-view .workspace-subtitle,#integrations-view .workspace-subtitle{max-width:72ch}

/* ---------- A6.5 question composer ----------
   The composer is the page's one primary interactive surface, so it is the one near-opaque
   surface above the fold. It is a form, not a chat box: an explicit label, restrained starter
   chips, a closed assumptions disclosure, and one confident primary action on the right. */
#ai-form{
  display:flex;flex-direction:column;gap:12px;min-width:0;padding:16px 18px;
  border:1px solid var(--workspace-edge);border-radius:var(--workspace-surface-radius);
  background:var(--workspace-surface);box-shadow:var(--workspace-shadow);
}
#ai-form>*{min-width:0}
#ai-form>.workspace-subhead{align-items:center}
.ai-trust{display:flex;align-items:center;gap:.35rem;margin:0;min-width:0}
.ai-trust>.icon{flex:0 0 auto;color:var(--muted)}
/* Starters are prompts, not destinations: pill-sized, one quiet tone, wrapping in reading order. */
.ai-starters{display:flex;align-items:center;flex-wrap:wrap;gap:6px;min-width:0}
.ai-starters>.field-label{margin-right:.2rem}
.ai-starters>.filter-chip{border-color:var(--workspace-edge);background:var(--workspace-surface-secondary);color:var(--ink-2)}
.ai-starters>.filter-chip:hover{border-color:var(--color-separator-strong);background:var(--workspace-hover);color:var(--ink)}
/* Three lines is enough to see a whole business question; the control still grows on demand. */
#ai-question{min-height:4.6rem;max-height:40dvh}
/* The assumptions stay a native disclosure (showAi() closes `details.ai-assumptions` on every
   activation). The legacy card around it is withdrawn and replaced by the A6 secondary panel
   material, so six planning parameters read as secondary to the question above them. */
#ai-form .ai-assumptions{margin:0;padding:9px 14px;border:1px solid var(--workspace-edge);border-radius:var(--workspace-panel-radius);background:var(--workspace-surface-secondary)}
#ai-form .ai-assumptions>summary{min-height:26px;color:var(--ink);font-size:var(--type-control);font-weight:620;cursor:pointer}
#ai-form .ai-assumptions[open]>summary{margin-bottom:.35rem}
#ai-form .ai-assumptions>.field-help{margin:0 0 .7rem}
#ai-form .field-actions{margin-top:0}
/* Disabled after an uncertain save: the form stays readable but visibly locked. */
#ai-form :disabled:not(button){opacity:.72;cursor:not-allowed}

/* ---------- A6.5 request status ----------
   `#ai-message` keeps its id, its `role="status"` and its `.state` class, because
   message(), reauthenticate() and the logout-safety suite address it by exactly those. Its
   legacy centred card becomes a left-aligned status panel; `.error` - which message() toggles -
   raises it to the danger tint so an uncertain save is never a quiet toast. */
#ai-message{margin:0;padding:10px 14px;border:1px solid var(--workspace-edge);border-radius:var(--workspace-panel-radius);background:var(--workspace-surface-secondary);color:var(--ink-2);font-size:var(--type-caption);line-height:1.5;text-align:left}
#ai-message.error{border-color:var(--color-danger-edge);background:var(--color-danger-soft);color:var(--ink)}
#ai-message>button{margin-top:.5rem}
/* Page-level state hosts whose box now comes from the A6 state rendered inside them. */
#ai-history-message,#integrations-message{padding:0;border:0;border-radius:0;background:none;color:var(--muted);text-align:left;font-size:var(--type-caption)}
/* The dialog-owned hosts that message() still writes plain text into. */
#integration-run-message,#payroll-payment-message,#payroll-accounting-message{margin:0 0 var(--workspace-panel-gap);padding:8px 12px;border:0;border-radius:var(--workspace-panel-radius);background:var(--workspace-surface-secondary);color:var(--ink-2);text-align:left;font-size:var(--type-caption)}
#integration-run-message.error,#payroll-payment-message.error,#payroll-accounting-message.error{background:var(--color-danger-soft);color:var(--color-danger);font-weight:600}
#integration-run-message:empty,#payroll-payment-message:empty,#payroll-accounting-message:empty{display:none}

/* ---------- A6.5 investigation result ----------
   One answer surface, then evidence. The answer is the only raised surface in the result; the
   facts, findings and recommendations below it are sections on the page canvas with their own
   record lists, so a recommendation can never outrank the evidence it came from. */
#ai-results:empty{display:none}
.investigation{display:flex;flex-direction:column;gap:var(--workspace-section-gap);min-width:0;margin:0}
.investigation>*{min-width:0}
.investigation-context{display:flex;align-items:center;justify-content:space-between;gap:.5rem 1rem;flex-wrap:wrap;min-width:0}
.investigation-context>.workspace-meta{margin:0}
.investigation-answer{
  display:flex;flex-direction:column;gap:.55rem;min-width:0;margin:0;padding:18px 20px;
  border:1px solid var(--workspace-edge);border-radius:var(--workspace-surface-radius);
  background:var(--workspace-surface);box-shadow:var(--workspace-shadow);overflow-wrap:anywhere;
}
.investigation-answer>*{margin:0}
.investigation-answer-title{color:var(--ink);font-size:var(--type-section-title);font-weight:680;letter-spacing:-.015em}
.investigation-question{display:block;padding-left:.75rem;border-left:2px solid var(--workspace-divider);color:var(--ink-2);font-size:var(--type-caption);line-height:1.5}
.investigation-question>.field-label{display:block;margin-bottom:.1rem}
.investigation-answer-text{color:var(--ink);font-size:var(--type-body);line-height:1.6;white-space:pre-line}
.investigation-trust{display:flex;align-items:flex-start;gap:.35rem;color:var(--muted);font-size:var(--type-metadata)}
.investigation-trust>.icon{flex:0 0 auto;margin-top:.1rem}
/* The facts are evidence, so they sit on a near-opaque surface like every other evidence list
   rather than floating on the wallpaper; secondary material keeps them quieter than the answer. */
.investigation .evidence-section>.detail-grid{padding:12px var(--workspace-cell-x);border:1px solid var(--workspace-edge);border-radius:var(--workspace-panel-radius);background:var(--workspace-surface)}
.investigation-feedback{margin:0;color:var(--ink);font-size:var(--type-control);font-weight:620;font-variant-numeric:tabular-nums}
.investigation-limits{min-width:0;padding:9px 14px;border:1px solid var(--workspace-edge);border-radius:var(--workspace-panel-radius);background:var(--workspace-surface-secondary)}
.investigation-limits>summary{color:var(--ink-2);font-size:var(--type-caption);font-weight:620;cursor:pointer}
.investigation-limits>ul{margin:.5rem 0 0;padding-left:1.1rem;color:var(--ink-2);font-size:var(--type-caption);line-height:1.5}
.investigation-proposal-title{margin:0;color:var(--ink);font-size:var(--type-card-title);font-weight:650;overflow-wrap:anywhere}
#dialog-content>h3.investigation-proposal-title{margin:0 0 var(--workspace-panel-gap)}

/* ---------- A6.5 investigation history ---------- */
#ai-history{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0;scroll-margin-top:1rem}
#ai-history-list:empty,#integration-run-list:empty,#payroll-payment-results:empty,#payroll-accounting-results:empty{display:none}
#ai-history-list .record-row{align-items:flex-start}
#ai-history-list h3{margin:.2rem 0 0}
.investigation-history-answer{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
.ai-history-more,.evidence-more{display:flex;justify-content:center;min-width:0}
.ai-history-more:has(>[hidden]),.evidence-more:has(>[hidden]){display:none}

/* ---------- A6.5 Integrasi page ----------
   Context first, then the systems side by side where there is room for two readable columns,
   so the first viewport at 1440 shows the beginning of both. A system is ONE surface: identity
   and health in its head, scope rows inside it, utilities in its foot. The health chip carries
   the status; the surface itself is never tinted red or green. */
#integrations-body{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
#integrations-body>*{min-width:0}
.integration-context{display:flex;align-items:center;justify-content:space-between;gap:.5rem 1rem;flex-wrap:wrap;min-width:0}
.integration-context>.info-panel{flex:1 1 32rem}
.integration-context>.workspace-meta{margin:0}
.integration-systems{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,28rem),1fr));gap:var(--workspace-panel-gap);align-items:start;min-width:0}
.integration-system{
  display:flex;flex-direction:column;min-width:0;overflow:hidden;
  border:1px solid var(--workspace-edge);border-radius:var(--workspace-surface-radius);
  background:var(--workspace-surface);box-shadow:var(--workspace-shadow);
}
.integration-system-head{display:flex;align-items:flex-start;justify-content:space-between;gap:.5rem 1rem;flex-wrap:wrap;padding:14px var(--workspace-cell-x) 12px;border-bottom:1px solid var(--workspace-divider)}
.integration-system-copy{min-width:0;flex:1 1 12rem}
.integration-system-copy>*{margin:0}
.integration-mapping{display:flex;align-items:center;justify-content:space-between;gap:.5rem 1rem;flex-wrap:wrap;padding:10px var(--workspace-cell-x);border-bottom:1px solid var(--workspace-divider);background:var(--workspace-surface-secondary)}
.integration-mapping-copy{display:flex;flex-direction:column;gap:.1rem;min-width:0;flex:1 1 16rem}
.integration-mapping-count{margin:0;color:var(--ink);font-size:var(--type-control);font-weight:600;overflow-wrap:anywhere}
.integration-scopes{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}
.integration-scopes .record-row{align-items:flex-start}
.integration-scopes h3{margin:0}
.integration-scopes .field-error,#integration-run-list .field-error{margin:.3rem 0 0}
.integration-utilities{display:flex;flex-direction:column;gap:.75rem;min-width:0;padding:12px var(--workspace-cell-x) 14px;border-top:1px solid var(--workspace-divider)}
.integration-utility{display:flex;flex-direction:column;gap:.4rem;min-width:0}

/* ---------- A6.5 evidence grammar ----------
   One shape for every snapshot, reconciliation and investigation record. A dialog body is plain
   block flow, so the evidence blocks a migrated renderer writes at the top level get the panel
   rhythm here; nested ones take the rhythm of their own section instead. */
#dialog-content>.evidence-block{margin:0 0 var(--workspace-panel-gap)}
#dialog-content>.evidence-block:last-child{margin-bottom:0}
#payroll-payment-summary,#payroll-accounting-summary{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0;margin-bottom:var(--workspace-panel-gap)}
#payroll-payment-summary:empty,#payroll-accounting-summary:empty{display:none}
.evidence-identity{display:flex;align-items:center;justify-content:space-between;gap:.35rem .9rem;flex-wrap:wrap;min-width:0}
.evidence-identity>.workspace-meta{margin:0;overflow-wrap:anywhere}
.evidence-section{display:flex;flex-direction:column;gap:.6rem;min-width:0}
.evidence-section>.workspace-subhead>h3{margin:0;font-size:var(--type-card-title)}
.evidence-nav{padding-top:.1rem}
.evidence-quiet{margin:0;padding:10px var(--workspace-cell-x);border:1px dashed var(--workspace-edge);border-radius:var(--workspace-panel-radius);color:var(--muted);font-size:var(--type-caption)}
.evidence-list>.evidence-quiet{border:0;border-radius:0}
.evidence-reason{margin:0;color:var(--ink-2);white-space:pre-wrap;overflow-wrap:anywhere}
.evidence-record{display:flex;flex-direction:column;gap:.35rem;min-width:0;padding:12px var(--workspace-cell-x);border-bottom:1px solid var(--workspace-divider);overflow-wrap:anywhere}
.evidence-record:last-child{border-bottom:0}
.evidence-record>*{margin:0}
.evidence-record-head{display:flex;align-items:baseline;justify-content:space-between;gap:.35rem .9rem;flex-wrap:wrap;min-width:0}
.evidence-record-title{min-width:0;margin:0;color:var(--ink);font-size:var(--type-control);font-weight:620;letter-spacing:-.005em;overflow-wrap:anywhere}
.evidence-record-line{color:var(--ink-2);font-size:var(--type-caption);line-height:1.5;font-variant-numeric:tabular-nums}
.evidence-record-meta{color:var(--muted);font-size:var(--type-metadata);font-variant-numeric:tabular-nums}
.evidence-record>.detail-grid{padding-top:.2rem}
.evidence-record>.attention-note{margin-top:.15rem;padding:8px 12px}
.evidence-lines{display:flex;flex-wrap:wrap;gap:.25rem .4rem;min-width:0;margin:0;padding:0;list-style:none}
.evidence-lines>li{min-width:0;padding:.05rem .45rem;border-radius:var(--radius-compact);background:var(--workspace-surface-secondary);color:var(--ink-2);font-size:var(--type-metadata);font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.evidence-metrics .metric-card{min-height:0}
/* The note's copy is a flex item beside its glyph; without a zero floor its longest word sets the
   minimum width and a 320px dialog at 200% text overflows by that word. */
.info-panel.evidence-block>span,.integration-context>.info-panel>span{min-width:0;overflow-wrap:anywhere}

@media(max-width:980px){
  /* The product's narrow-width touch floor, restored for migrated controls that out-specify it,
     exactly as A6.1-A6.4 did for theirs. */
  #ai-view button,#integrations-view button,#ai-view .command-filter,#ai-view .command-filter>select,
  #ai-view .command-search input,#ai-view .field>input,#ai-view .field>select,
  #dialog-content .evidence-block button{min-height:var(--touch-target-min)}
}
@media(max-width:650px){
  #ai-form{padding:14px}
  #ai-form>.workspace-subhead{align-items:flex-start}
  .ai-starters>.filter-chip{white-space:normal;text-align:left}
  #ai-form .field-actions-end{justify-content:stretch}
  #ai-form .field-actions-end>button{flex:1 1 auto}
  .investigation-answer{padding:14px}
  .evidence-record-head{flex-direction:column;align-items:flex-start;gap:.3rem}
  /* A spelled-out action ("Buka hasil tindakan", "Rincian snapshot listing") is wider than a
     320px dialog at 200% text; it wraps inside its own button instead of widening the dialog. */
  #dialog-content .evidence-block button,#ai-view .investigation button,#integrations-view .integration-system button{max-width:100%;white-space:normal;text-align:center;overflow-wrap:anywhere}
  #dialog-content .attention-note.evidence-block>button{flex:1 1 100%;margin-left:0}
  .evidence-identity{flex-direction:column;align-items:flex-start}
  /* A filter in these views is a labelled field on a phone, for the same measured reason A6.4
     gave its reports: an inline label beside a control cannot wrap at 320px/200%. */
  #ai-view .command-filter,#dialog-content .evidence-block .command-filter{flex:1 1 100%;flex-direction:column;align-items:stretch;height:auto;padding:.3rem .6rem;gap:.1rem}
  #ai-view .command-filter>span,#dialog-content .evidence-block .command-filter>span{overflow-wrap:anywhere;white-space:normal}
  #ai-view .command-filter>select,#dialog-content .evidence-block .command-filter>select{width:100%;min-width:0;min-height:var(--touch-target-min)}
  /* Short counts stay comparable in columns; a rupiah figure is allowed to wrap rather than be
     cut off, and a long label or cursor wraps deliberately instead of widening the dialog. */
  .evidence-metrics .metric-value{font-size:1.15rem;overflow-wrap:anywhere}
  .evidence-metrics .metric-label,#dialog-content .evidence-block .detail-field>dt,#ai-view .detail-field>dt{overflow-wrap:anywhere}
  .evidence-record .chip-row .status-chip,.investigation .chip-row .status-chip,.integration-system .status-chip,.evidence-identity .status-chip{white-space:normal}
}
/* No A6.5 forced-colors block, on purpose. Every surface this block draws carries a real 1px
   border, which forced-colors mode repaints as CanvasText over a Canvas background; shadows and
   tints are dropped by the mode itself. Meaning lives in chip text, headings and the A6.0
   `.status-dot`, which workspace-primitives.css already maps. The product keeps exactly four
   forced-colors contracts (A4, A5.2, A5.3, A6.0), and A6.5 does not add a fifth. */


/* ==========================================================================
   A6.6 - Aktivitas + Audit trail + Cadangan data inner-workspace containment
   --------------------------------------------------------------------------
   The same narrow job the A6.1-A6.5 blocks above do, done once more for the three
   operational/system workspaces A6.6 owns. They share one visual language but have
   three deliberately different compositions:

     Aktivitas     - a chronological report. The timeline is the page's centre; the four
                     range-wide figures above it are a compact strip, not a dashboard.
     Audit trail   - an immutable investigation ledger. Read-only context, one command
                     bar, a result count, a record list, and a focused evidence dialog.
     Cadangan data - one focused, security-sensitive utility surface. No table, no
                     status chip, no invented history.

   Everything here is reachable only from markup a migrated A6.6 renderer or section
   emits: the three section ids, the A6.6 host ids inside them, and the `.activity-*`,
   `.audit-*` and `.backup-*` names that only those renderers write. Nothing here is a
   component and no A6.0 primitive is redefined; the A6.1-A6.5 blocks are not touched.

   Three legacy names are still EMITTED on purpose, as stable hooks the existing suites
   address (`#activity-list .activity-item`, `#audit-list .audit-event`, `.audit-json`).
   Their legacy boxes - the `.history-item`-era grid gutters, the `.audit-event` card and
   the payload viewer's sunken slab - are withdrawn below by id/class scope, never by
   editing the legacy rules, which other surfaces still render. The legacy vocabulary
   these pages used to render with - `.page-heading`, `.eyebrow`, `.filters`, `.summary`,
   `.history-item`, `.hint`, `.filter-form`, `.form-grid`, `.form-actions`, `.form-info`,
   `.status-label`, `.requirement-values`, `.pagination` - is left intact above: each is
   still rendered by A6.7's Purchasing / Budget marketing / Approval Inbox or by a child
   dialog. What A6.6 makes inert is left for A6.8's proven sweep, as A6.1's rule requires.
   ========================================================================== */
#activity-view,#audit-view,#backup-view{max-width:1360px;margin-inline:auto}
#activity-view .workspace-subtitle,#audit-view .workspace-subtitle,#backup-view .workspace-subtitle{max-width:72ch}

/* ---------- A6.6 Aktivitas: command bar ----------
   Dates keep their words. A date control that only showed a calendar glyph would hide which end
   of the range it is, so each filter keeps its visible label and a width that fits a full date.
   The floor is desktop-only on purpose: `[type=date]` out-specifies the phone rule below, and at
   320px / 200% text an 8.75rem floor is 280px - wider than the command bar, clipped by the window
   rather than overflowing the document. On a phone the field is full-width instead. */
@media(min-width:651px){
  #activity-form .command-filter>input[type=date]{min-width:8.75rem}
  #activity-form .command-filter>select{min-width:10rem}
}

/* ---------- A6.6 Aktivitas: the range-wide summary ----------
   Four real figures, read as one compact row. The tile sits beside its label rather than above it,
   which is what keeps the strip to a single short band so the timeline starts in the first
   viewport at 1440. */
#activity-summary{grid-template-columns:repeat(auto-fit,minmax(min(100%,11rem),1fr))}
#activity-summary:empty{display:none}
#activity-summary .metric-card{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:.1rem .65rem;min-height:0;padding:10px 12px}
#activity-summary .metric-icon{grid-row:1/3;margin:0}
#activity-summary .metric-value{font-size:1.25rem}
/* Secondary information, not a panel, exactly as A6.1-A6.2 treat their scope notes. Every word of
   the summary / warehouse-net / CSV truth is kept; it is read once and then out of the way. */
#activity-summary-note{max-width:100ch;padding:1px 0 0;border-radius:0;background:none;font-size:var(--type-metadata);line-height:1.45}
#activity-summary-note>span{min-width:0;overflow-wrap:anywhere}
#activity-export-note{color:var(--ink-2);font-weight:600}

/* ---------- A6.6 Aktivitas: the timeline ---------- */
.activity-work{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
/* The state host keeps its id, its `role="status"` and its `.state` class; its box now comes from
   the A6 state rendered inside it. */
#activity-message{padding:0;border:0;border-radius:0;background:none;color:inherit;text-align:left;font-size:inherit}
/* The timeline lives on one near-opaque surface so the events stay readable over the wallpaper.
   The rail and markers belong to the A6 timeline primitive; nothing here draws per-row material. */
.activity-surface{min-width:0;padding:18px 20px 16px 22px;border:1px solid var(--workspace-edge);border-radius:var(--workspace-surface-radius);background:var(--workspace-surface);box-shadow:var(--workspace-shadow)}
.activity-surface:has(>#activity-list:empty){display:none}
/* A fixed time column, so fifty events read as one aligned chronology instead of fifty
   differently indented rows. rem, not px: at 200% text the column grows with the timestamp. */
#activity-list .activity-item{grid-template-columns:8.75rem minmax(0,1fr);padding-bottom:1.1rem;overflow-wrap:anywhere}
#activity-list .activity-item:last-child{padding-bottom:0}
#activity-list .activity-item p{margin:0}
#activity-list .activity-item .timeline-time{grid-row:1/span 2;padding-top:.05rem}
#activity-list .activity-item .timeline-event{font-size:var(--type-metadata);font-weight:650;color:var(--ink-2);letter-spacing:.01em}
#activity-list .activity-item .timeline-detail{margin-top:.1rem}
#activity-list .activity-item .timeline-actor{margin-top:.25rem;color:var(--muted)}
#activity-list .activity-order>.order-title{display:inline;font-size:var(--type-control);font-weight:650;color:var(--ink);overflow-wrap:anywhere}
#activity-list .activity-sku{color:var(--muted);font-size:var(--type-metadata);font-variant-numeric:tabular-nums}
#activity-list .activity-reason{padding-left:.6rem;border-left:2px solid var(--workspace-divider);color:var(--ink-2);white-space:pre-wrap}
.activity-more,.audit-more{display:flex;justify-content:center;min-width:0}
.activity-more:has(>[hidden]),.audit-more:has(>[hidden]){display:none}

/* ---------- A6.6 Audit trail ---------- */
.audit-body{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
.audit-body>*{min-width:0}
#audit-readonly-note{max-width:100ch}
#audit-readonly-note>span{min-width:0;overflow-wrap:anywhere}
/* Search is the widest control: it takes its own row, and the classification filters and the
   two dates sit beneath it with Reset and the explicit submit on the right. */
#audit-filter .command-search{flex:1 1 100%}
#audit-filter .command-filter>select{max-width:16rem}
#audit-summary{margin:0}
#audit-error{margin:0;padding:10px 14px;border:1px solid var(--color-danger-edge);border-radius:var(--workspace-panel-radius);background:var(--color-danger-soft);color:var(--ink);font-weight:600}
/* The legacy `.audit-event` card is withdrawn inside the ledger: a record is a row of one list,
   not a card of its own. The class stays because the existing suites address it. */
#audit-list .audit-event{align-items:flex-start;margin:0;padding:12px var(--workspace-cell-x);border:0;border-bottom:1px solid var(--workspace-divider);border-radius:0;background:none;box-shadow:none;overflow-wrap:anywhere}
#audit-list .audit-event:last-child{border-bottom:0}
#audit-list .audit-event p{margin:0}
#audit-list .audit-event h3{margin:.35rem 0 0}
#audit-list .audit-event .data-secondary{margin-top:.1rem}
#audit-list .audit-event .data-meta{margin-top:.2rem}
#audit-list .empty-state{border:1px solid var(--workspace-edge);border-radius:var(--workspace-surface-radius);background:var(--workspace-surface)}

/* ---------- A6.6 audit evidence dialog ---------- */
.audit-evidence{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
.audit-evidence>*{min-width:0;margin:0}
.audit-facts .audit-fact-wide{grid-column:1/-1}
.audit-evidence code{font:.8125rem/1.45 ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere;word-break:break-word}
.audit-evidence>.info-panel>span{min-width:0;overflow-wrap:anywhere}
.audit-evidence-section{display:flex;flex-direction:column;gap:.45rem;min-width:0}
.audit-evidence-section>h3{margin:0;font-size:var(--type-card-title)}
/* The stored JSON keeps its legacy hook and its wrapping, readable, scrollable box; only its
   material moves onto the A6 tokens so it reads as evidence inside the dialog in both themes. */
.audit-evidence .audit-json{margin:0;max-height:min(360px,48dvh);padding:12px 14px;border:1px solid var(--workspace-edge);border-radius:var(--workspace-panel-radius);background:var(--workspace-surface-secondary);color:var(--ink)}
.audit-evidence .field-actions{margin-top:0}

/* ---------- A6.6 Cadangan data ----------
   One focused utility surface: what the file is, why it is sensitive, what it does and does not
   contain about access keys, when the snapshot is taken, what to verify afterwards, then the one
   action and its status. The measure is bounded so the whole utility reads as a single column. */
.backup-utility{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0;max-width:52rem;padding:18px 20px;border:1px solid var(--workspace-edge);border-radius:var(--workspace-surface-radius);background:var(--workspace-surface);box-shadow:var(--workspace-shadow)}
.backup-utility>*{min-width:0;margin:0}
.backup-file{display:flex;align-items:flex-start;gap:.75rem;min-width:0}
.backup-file>.metric-icon{margin:0;background:var(--color-accent-soft);color:var(--color-accent)}
.backup-file-copy{flex:1 1 0;min-width:0}
.backup-contents{margin:.3rem 0 0;color:var(--ink-2);font-size:var(--type-body);line-height:1.5;overflow-wrap:anywhere}
.backup-notes{display:flex;flex-direction:column;gap:8px;min-width:0;padding:0;list-style:none}
.backup-notes>.info-panel>span{min-width:0;overflow-wrap:anywhere}
.backup-notes strong{color:var(--ink);font-weight:650}
.backup-actions{margin-top:.1rem}
/* message() writes plain text here and toggles `.error`; the status reads as a quiet panel and the
   failure as the danger tint, inline, never only as a toast. */
#backup-message{padding:10px 14px;border:1px solid var(--workspace-edge);border-radius:var(--workspace-panel-radius);background:var(--workspace-surface-secondary);color:var(--ink-2);font-size:var(--type-caption);line-height:1.5}
#backup-message.error{border-color:var(--color-danger-edge);background:var(--color-danger-soft);color:var(--ink);font-weight:600}

@media(max-width:980px){
  /* The product's narrow-width touch floor, restored for migrated controls that out-specify it,
     exactly as A6.1-A6.5 did for theirs. */
  #activity-view button,#audit-view button,#backup-view button,.audit-evidence button,
  #activity-view .command-filter,#activity-view .command-filter>select,#activity-view .command-filter>input,
  #audit-view .command-filter,#audit-view .command-filter>select,#audit-view .command-filter>input,
  #audit-view .command-search input{min-height:var(--touch-target-min)}
  #activity-list .activity-order>.order-title{min-height:var(--touch-target-min);display:inline-flex;align-items:center}
}
@media(max-width:650px){
  /* A filter is a labelled field on a phone, for the same measured reason A6.4 and A6.5 gave
     theirs: an inline label beside a date cannot wrap at 320px/200%. */
  #activity-view .command-filter,#audit-view .command-filter{flex:1 1 100%;flex-direction:column;align-items:stretch;height:auto;padding:.3rem .6rem;gap:.1rem}
  #activity-view .command-filter>span,#audit-view .command-filter>span{overflow-wrap:anywhere;white-space:normal}
  #activity-view .command-filter>select,#activity-view .command-filter>input,
  #audit-view .command-filter>select,#audit-view .command-filter>input{width:100%;max-width:none;min-width:0}
  #activity-view .command-actions,#audit-view .command-actions{width:100%}
  #activity-view .command-actions>button,#audit-view .command-actions>button{flex:1 1 auto}
  #activity-view button,#audit-view button,#backup-view button,.audit-evidence button{max-width:100%;white-space:normal;overflow-wrap:anywhere}
  /* One column: the time sits above its event instead of beside it. */
  .activity-surface{padding:14px 14px 12px 18px}
  #activity-list .activity-item{grid-template-columns:minmax(0,1fr)}
  #activity-list .activity-item>*{grid-column:1}
  #activity-list .activity-item .timeline-time{grid-row:auto;white-space:normal}
  #activity-summary .metric-value{font-size:1.1rem;overflow-wrap:anywhere}
  #audit-list .audit-event{flex-direction:column;align-items:stretch;gap:.6rem}
  #audit-list .audit-event .record-row-aside{justify-content:flex-start}
  #audit-list .chip-row .status-chip{white-space:normal}
  .backup-utility{padding:14px}
  .backup-actions>button{flex:1 1 100%}
}
/* No A6.6 forced-colors block, on purpose, for the reason A6.5 recorded: every surface here has a
   real 1px border that forced-colors repaints, and meaning lives in text - the spelled-out event
   label, the category chip text with its A6.0 `.status-dot`, the error copy and the button label -
   never in a tint or a timeline marker alone. */



/* ==========================================================================
   A6.7 - Permintaan pembelian + Budget marketing + Inbox approval containment
   --------------------------------------------------------------------------
   The same narrow job the A6.1-A6.6 blocks above do, done once more for the three
   business queues A6.7 owns and for the request sheets they open. The three pages
   share ONE idiom - context, one command bar, one record list, "load more" - and
   differ only in their records:

     Permintaan pembelian - material requests; its two utilities (Master pemasok,
                            Daftar PO) are dialogs in the same grammar.
     Budget marketing     - campaign ceilings; approval never records spending.
     Inbox approval       - nine approval kinds read from the ledgers that own them;
                            opening one lands in that domain's own detail.

   Everything here is reachable only from markup a migrated A6.7 section or renderer
   emits: the three section ids, the A6.7 host ids inside them (`#approval-*`,
   `#pr-page-*`, `#marketing-budget-*`) and inside the two utility dialogs (`#po-*`,
   `#pr-lines`), and the `.queue-*` and `.request-*` names only those renderers write.
   `.approval-*` and `.decision-*` are deliberately NOT used: the frozen Command Center
   owns both. Nothing here is a component and no A6.0 primitive is redefined; the
   A6.1-A6.6 blocks are not touched.

   Two legacy hooks are still EMITTED on purpose. `.state` wraps each queue's loading
   and empty state inside its `list-host`, because appendRows(), clearPageLoading() and
   the shared-UI suite recognise a state by it and the M6 motion contract requires the
   host's own class to stay exactly `list-host`; its legacy card is withdrawn here by
   class scope. `.bom-line` stays on the PR line editor for the same reason A6.2 kept it
   on the BOM editor: it is the collector's hook. The legacy vocabulary these pages used
   to render with - `.page-heading`, `.eyebrow`, `.hint`, `.actions`, `.filter-form`,
   `.form-grid`, `.form-actions`, `.form-info`, `.material-event`, `.status-label` - is
   left intact above: the PO fulfilment dialogs, the production change and payroll
   approval dialogs, the order-scoped "PR untuk order ini" dialog and `formDialog()`
   itself still render it. What A6.7 makes inert is left for A6.8's proven sweep, as
   A6.1's rule requires.
   ========================================================================== */
#purchase-requests-view,#marketing-budgets-view,#approvals-view{max-width:1360px;margin-inline:auto}
#purchase-requests-view .workspace-subtitle,#marketing-budgets-view .workspace-subtitle,#approvals-view .workspace-subtitle{max-width:72ch}
/* Scope notes are read once and are one or two sentences each: a bounded measure, not a ribbon. */
#purchase-requests-note,#marketing-budgets-note,#approvals-source-note{max-width:100ch}
#purchase-requests-note>span,#marketing-budgets-note>span,#approvals-source-note>span{min-width:0;overflow-wrap:anywhere}
/* The Inbox's context: the source note, then the four domain lists it can hand the operator to.
   They are navigation, so they are quiet and labelled as one group, not four equal CTAs. */
.queue-context{display:flex;flex-direction:column;gap:8px;min-width:0}
.queue-sources{display:flex;align-items:center;gap:4px 6px;flex-wrap:wrap;min-width:0}
.queue-sources>.workspace-meta{margin-right:4px;font-weight:600}

/* ---------- A6.7 the queue ---------- */
.queue-body{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
.queue-body>*{min-width:0}
#approval-filter .command-filter>select,#marketing-budget-filter .command-filter>select,#pr-page-filter .command-filter>select{max-width:18rem}
/* The load-more control is an error's retry as well ("Coba lagi"), so the errors sit directly
   above the list they describe, in the same danger panel A6.6 gave the Audit ledger. */
#approval-error,#pr-page-error,#marketing-budget-error,#po-error{margin:0;padding:10px 14px;border:1px solid var(--color-danger-edge);border-radius:var(--workspace-panel-radius);background:var(--color-danger-soft);color:var(--ink);font-size:var(--type-caption);font-weight:600}
/* The state host keeps the `.state` hook, so its legacy card is withdrawn and the A6 state inside
   it provides the box. An empty queue still reads as the queue's own surface. */
.queue-state{padding:0;border:0;border-radius:0;background:none;color:inherit;text-align:left;font-size:inherit}
.queue-state>.empty-state{border:1px solid var(--workspace-edge);border-radius:var(--workspace-surface-radius);background:var(--workspace-surface)}
/* A record with several lines reads from its identity down, so the amount and the action align
   with the reference, not with the middle of the record. */
.queue-item{align-items:flex-start}
.queue-item :is(h3,p){margin:0}
.queue-item>.metric-icon{background:var(--workspace-surface-secondary);color:var(--ink-2)}
.queue-item .chip-row{margin-top:.4rem}
.queue-item .data-meta{margin-top:.25rem;overflow-wrap:anywhere}
.queue-item .chip-row+.data-meta{margin-top:.35rem}
/* Who asked and when rides on the chip line, as A6.3's request rows do: one line less per record. */
.queue-item .chip-row>.queue-stamp{margin:0 0 0 .2rem}
/* The requester's own words, quoted quietly and kept to two lines in the queue; the whole text
   is in the record's detail, and a screen reader still reads all of it here.
   Both rules below are scoped to the record because A6.0's `.data-meta{display:block}` loads
   after this sheet and would otherwise win at equal specificity. */
.queue-item .queue-reason{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;color:var(--ink-2);white-space:pre-wrap}
/* A stale production change says so in words; the glyph carries the tone, never the tint alone. */
.queue-item .queue-warning{display:flex;align-items:flex-start;gap:.3rem;color:var(--ink);font-weight:600}
.queue-item .queue-warning>.icon{flex:0 0 auto;margin-top:.05rem;color:var(--color-warning)}
.queue-amount{display:flex;flex-direction:column;align-items:flex-end;min-width:0;margin:0;text-align:right}
.queue-amount-value{color:var(--ink);font-size:var(--type-control);font-weight:650;font-variant-numeric:tabular-nums;white-space:nowrap}
.queue-amount-label{color:var(--muted);font-size:var(--type-metadata)}
.queue-more{display:flex;justify-content:center;min-width:0}
.queue-more:has(>[hidden]){display:none}

/* ---------- A6.7 request sheets and the two Purchasing utilities ----------
   A focused dialog, read top to bottom: identity, facts, the requester's words, the rule that
   governs the decision, the decisions this viewer may take, related records, navigation, then
   the append-only history. The dialog's own chrome (`formDialog()`) is A6.8's, not this block's. */
.request-sheet{display:flex;flex-direction:column;gap:var(--workspace-panel-gap);min-width:0}
.request-sheet>*{min-width:0;margin:0}
.request-sheet :is(h3,p){margin:0}
.request-sheet .info-panel>span{min-width:0;overflow-wrap:anywhere}
.request-identity{display:flex;flex-direction:column;gap:.3rem;min-width:0}
.request-identity>.data-primary{font-size:var(--type-card-title);font-weight:650}
.request-section{display:flex;flex-direction:column;gap:.45rem;min-width:0}
.request-section>h3{font-size:var(--type-card-title)}
.request-text{color:var(--ink);font-size:var(--type-body);line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}
.request-lines .record-row,.request-orders .record-row{min-height:0;padding:10px var(--workspace-cell-x)}
.request-quantity{color:var(--ink);font-size:var(--type-control);font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.request-history .timeline-detail{white-space:pre-wrap}
.request-navigation{padding-top:10px;border-top:1px solid var(--workspace-divider)}
.request-sheet>.workspace-subhead{align-items:center}
/* The PR line editor keeps A6.2's `.bom-line` rows; only the section around it is A6.7's. */
.request-lines-editor{display:flex;flex-direction:column;gap:.45rem;min-width:0}
.request-lines-editor>h3{margin:0;font-size:var(--type-card-title)}
.request-lines-editor>.field-help{margin:0}
.request-lines-editor>.field-actions{margin-top:0}

@media(max-width:980px){
  /* The product's narrow-width touch floor, restored for migrated controls that out-specify it,
     exactly as A6.1-A6.6 did for theirs. */
  #purchase-requests-view button,#marketing-budgets-view button,#approvals-view button,.request-sheet button,
  #purchase-requests-view .command-filter,#purchase-requests-view .command-filter>select,
  #marketing-budgets-view .command-filter,#marketing-budgets-view .command-filter>select,
  #approvals-view .command-filter,#approvals-view .command-filter>select,
  .request-sheet .command-filter,.request-sheet .command-filter>select{min-height:var(--touch-target-min)}
}
@media(max-width:650px){
  /* A filter is a labelled field on a phone, for the same measured reason A6.4-A6.6 gave theirs:
     an inline label beside a select cannot wrap at 320px / 200%. */
  #purchase-requests-view .command-filter,#marketing-budgets-view .command-filter,#approvals-view .command-filter,
  .request-sheet .command-filter{flex:1 1 100%;flex-direction:column;align-items:stretch;height:auto;padding:.3rem .6rem;gap:.1rem}
  #purchase-requests-view .command-filter>span,#marketing-budgets-view .command-filter>span,#approvals-view .command-filter>span,
  .request-sheet .command-filter>span{overflow-wrap:anywhere;white-space:normal}
  #purchase-requests-view .command-filter>select,#marketing-budgets-view .command-filter>select,#approvals-view .command-filter>select,
  .request-sheet .command-filter>select{width:100%;max-width:none;min-width:0}
  #purchase-requests-view .command-actions,#marketing-budgets-view .command-actions,#approvals-view .command-actions,
  .request-sheet .command-actions{width:100%}
  #purchase-requests-view .command-actions>button,#marketing-budgets-view .command-actions>button,#approvals-view .command-actions>button,
  .request-sheet .command-actions>button{flex:1 1 auto}
  #purchase-requests-view button,#marketing-budgets-view button,#approvals-view button,.request-sheet button{max-width:100%;white-space:normal;overflow-wrap:anywhere}
  /* The record wraps below its tile (A6.0's own rule); its amount then reads from the left like
     the rest of the record, and a long figure may break rather than cross the window. */
  /* The group label takes its own line, so the four lists wrap as one aligned run beneath it. */
  .queue-sources>.workspace-meta{flex:1 1 100%;margin:0}
  .queue-amount{align-items:flex-start;text-align:left}
  .queue-amount-value{white-space:normal;overflow-wrap:anywhere}
  .request-quantity{white-space:normal;overflow-wrap:anywhere}
  /* A6.0 caps a chip at its column and keeps it on one line; at 320px / 200% a "Menunggu
     keputusan" or a kind label is wider than the column, so on these surfaces it may wrap inside
     its own pill, exactly as A6.6 lets the Audit category chip wrap. The same holds for a history
     stamp, which A6.0 keeps on one line beside a column that no longer exists here. */
  .queue-item .status-chip,.request-sheet .status-chip{white-space:normal;overflow-wrap:anywhere}
  .request-history .timeline-time{white-space:normal}
}
/* No A6.7 forced-colors block, for the reason A6.5 and A6.6 recorded: every A6.7 surface has a
   real 1px border that forced-colors repaints, and meaning lives in text - the status and kind
   chip text with A6.0's `.status-dot`, the spelled-out stale sentence, the error copy and every
   button label - never in a tint, a tile or a timeline marker alone. */




/* ==========================================================================
   A6.8 - Final consistency: the shared dialog chrome, formDialog(), and the
   fulfilment / approval sheets A6.7 left on legacy markup
   --------------------------------------------------------------------------
   Every primary workspace was migrated by A6.1-A6.7. What still read as a second
   product was the ONE dialog every workflow opens: a fixed 650px box whose margins
   and padding were in rem (so 200% text spent ~130px of a 320px phone on chrome),
   whose close control floated below the title's baseline, and whose shared form
   still wore the pre-A6 label, note and footer styling.

   This block owns exactly:
     #dialog and its heading          - viewport-safe width / height, three semantic
                                        widths (data-size: compact | standard | wide,
                                        set by openDialog(); standard is the historical
                                        650px), px insets, a sticky heading, internal
                                        vertical scrolling.
     #action-form                     - formDialog()'s chrome. The hooks it styles
                                        (.form-info, .form-grid, .form-actions,
                                        #form-fields, #form-error, #reauth, #save-form)
                                        are load-bearing and keep their names; only
                                        their presentation, scoped to the one form,
                                        moves onto the A6 tokens.
     .request-record(s), .request-quantities, .request-amounts, .request-attention,
     .request-identifier, .request-amount, .request-empty, .request-closure,
     .request-payment-summary, #pr-error
                                      - the few names the A6.8 sheets add to A6.7's
                                        request grammar.
   It adds no blur, no transition, no keyframe and no animation; the dialog's M3 exit
   and every reduced-motion rule above are untouched. No A6.0 primitive and no
   earlier phase block is edited.
   ========================================================================== */
#dialog{--dialog-inset:12px;--dialog-pad:24px;
  width:min(650px,calc(100vw - var(--dialog-inset)*2));max-width:calc(100vw - var(--dialog-inset)*2);
  max-height:min(90dvh,calc(100dvh - var(--dialog-inset)*2));
  padding:0 var(--dialog-pad) var(--dialog-pad);overflow-x:auto;overflow-y:auto;overscroll-behavior:contain}
#dialog[data-size=compact]{width:min(520px,calc(100vw - var(--dialog-inset)*2))}
#dialog[data-size=wide]{width:min(880px,calc(100vw - var(--dialog-inset)*2))}
/* A6.3's People screens keep the 960px their roster layouts were approved at (the legacy
   `dialog:has(.workforce-screen)` rule above is out-specified by `#dialog`, so it is restated). */
#dialog:has(.workforce-screen){width:min(960px,calc(100vw - var(--dialog-inset)*2))}
/* The heading stays with the reader while a long sheet scrolls under it, so the title and
   "Tutup dialog" are never scrolled away. It is painted with the dialog's own opaque material,
   and the close control is centred on the title instead of hanging below its baseline. */
#dialog>.dialog-heading{position:sticky;top:0;z-index:2;align-items:center;
  margin:0 calc(var(--dialog-pad)*-1) 18px;padding:20px var(--dialog-pad) 14px;
  border-bottom:1px solid var(--workspace-divider);background:var(--material-floating-solid)}
#dialog>.dialog-heading>h2{min-width:0;line-height:1.3}

/* ---------- formDialog() ---------- */
#form-fields{min-width:0;margin:0;padding:0;border:0}
/* The context note: the subject, then what saving means - A6.0's info-panel measure and tone, with
   a real border so it survives forced colors. */
#action-form>.form-info{margin:0 0 var(--workspace-section-gap);padding:10px 12px;
  border:1px solid var(--workspace-edge);border-radius:var(--workspace-panel-radius);
  background:var(--workspace-surface-secondary);color:var(--ink-2);font-size:var(--type-caption);line-height:1.5}
#action-form .form-grid{gap:14px 16px}
/* Legacy fields are `<label>Text<control></label>` (or a label beside its control). They read as
   the A6 `.field` does - a small label above a quiet control - without a single name changing,
   because every collector reads these forms by `name` and every suite finds them by label. A6
   fields inside the same grid already carry `.field` and are left to A6.0. */
#action-form .form-grid label:not([class*=field]):not(.check-field){display:flex;flex-direction:column;gap:.3rem;
  min-width:0;color:var(--ink-2);font-size:var(--type-metadata);font-weight:600}
#action-form .form-grid :is(input:not([type=checkbox]):not([type=radio]),select,textarea):not(.field>*){
  width:100%;min-width:0;min-height:var(--workspace-control-height);margin:0;padding:.35rem .6rem;
  border:1px solid var(--workspace-edge);border-radius:var(--workspace-control-radius);
  background:var(--workspace-surface-secondary);color:var(--ink);font-size:var(--type-control);font-weight:400}
#action-form .form-grid textarea:not(.field>*){min-height:5.5rem;padding:.5rem .6rem;line-height:1.5;resize:vertical}
#action-form .form-grid :is(input,select,textarea):not(.field>*):focus{border-color:var(--focus);background:var(--workspace-surface)}
#action-form .form-grid :is(input,select,textarea):disabled{opacity:.72;cursor:not-allowed}
#action-form .form-grid>.hint{margin:0;font-size:var(--type-metadata)}
/* A nested card inside a form (a PO price per material) is a quiet group, not a second card. */
#action-form .form-grid .material-event{margin:0 0 10px;padding:12px 14px;border:1px solid var(--workspace-edge);
  border-radius:var(--workspace-panel-radius);background:var(--workspace-surface-secondary);box-shadow:none}
#action-form .form-grid .material-event>h3{font-size:var(--type-control)}
/* The error is inline and actionable, in the same danger panel the A6.6 / A6.7 queues use. */
#form-error{margin:14px 0 0;padding:10px 14px;border:1px solid var(--color-danger-edge);border-radius:var(--workspace-panel-radius);
  background:var(--color-danger-soft);color:var(--ink);font-size:var(--type-caption);font-weight:600;overflow-wrap:anywhere}
/* Footer: cancel, re-authenticate when needed, then the one primary (save, or "Coba ulang
   penyimpanan" while unresolved), right-aligned and ruled off from the fields. */
#action-form>.form-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;
  margin-top:20px;padding-top:16px;border-top:1px solid var(--workspace-divider)}
#action-form>.form-actions>button{min-height:var(--workspace-control-height);max-width:100%;white-space:normal;overflow-wrap:anywhere}
#action-form>.form-actions>:is([data-action=cancel-form],#reauth){border:1px solid var(--workspace-edge);
  background:var(--workspace-surface);color:var(--ink)}

/* ---------- the A6.8 sheets ---------- */
#pr-error{margin:0;padding:10px 14px;border:1px solid var(--color-danger-edge);border-radius:var(--workspace-panel-radius);
  background:var(--color-danger-soft);color:var(--ink);font-size:var(--type-caption);font-weight:600}
#pr-status{max-width:18rem}
.request-sheet .request-section>.field-help{margin:0}
.request-records .request-record{min-height:0;padding:10px var(--workspace-cell-x);align-items:flex-start}
.request-record :is(h4,p){margin:0}
.request-record>.record-row-copy>h4{color:var(--ink);font-size:var(--type-control);font-weight:650;overflow-wrap:anywhere}
.request-record .chip-row{margin-top:.35rem}
.request-record .data-meta{margin-top:.2rem;overflow-wrap:anywhere}
.request-record .reason{white-space:pre-wrap}
.request-quantities{margin-top:.55rem;gap:8px 12px}
.request-quantities .detail-field>dd{font-size:var(--type-caption)}
.request-amounts{display:flex;flex-direction:column;min-width:0;margin:0;border:1px solid var(--workspace-edge);
  border-radius:var(--workspace-panel-radius);background:var(--workspace-surface)}
.request-amounts>div{display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap;gap:.2rem 1rem;min-width:0;
  padding:8px 12px;border-bottom:1px solid var(--workspace-divider)}
.request-amounts>div:last-child{border-bottom:0}
.request-amounts dt{min-width:0;color:var(--ink-2);font-size:var(--type-caption);overflow-wrap:anywhere}
.request-amounts dd{margin:0 0 0 auto;color:var(--ink);font-size:var(--type-control);font-weight:600;
  font-variant-numeric:tabular-nums;text-align:right;overflow-wrap:anywhere}
/* The last total is the one the decision is about. */
.request-amounts>div:last-child>:is(dt,dd){color:var(--ink);font-weight:700}
/* Old beside proposed: two columns, so each semula value sits next to its diajukan value. */
.request-change>.detail-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
.request-identifier{overflow-wrap:anywhere}
.request-amount,.request-payment-summary{font-variant-numeric:tabular-nums}
.request-amount{font-weight:650}
.request-empty{margin:0}

@media(max-width:980px){
  #action-form .form-grid :is(input:not([type=checkbox]):not([type=radio]),select):not(.field>*),
  #action-form>.form-actions>button{min-height:var(--touch-target-min)}
}
@media(max-width:650px){
  /* px, not rem: at 320px with 200% text a rem inset and padding cost the sheet a third of its
     width. The dialog keeps an 8px window edge and a 16px inner edge at every text size. */
  #dialog{--dialog-inset:8px;--dialog-pad:16px}
  #dialog>.dialog-heading{margin-bottom:14px;padding:14px var(--dialog-pad) 12px}
  #action-form>.form-actions>button{flex:1 1 auto}
  #pr-status{max-width:none}
  .request-record>.record-row-aside{flex:1 1 100%;justify-content:flex-start}
  .request-record .queue-amount{align-items:flex-start;text-align:left}
  .request-record .queue-amount-value{white-space:normal;overflow-wrap:anywhere}
}
/* ---------- two regressions the A6.8 product sweep found ----------
   Command Center's first-pass-yield ring is a fixed 120px on a phone, but its figure is text: at
   320px with 200% text "66.67%" is ~160px wide and collides with the ring's stroke. On a phone the
   ring now scales with the text size - `7.5rem` IS 120px at the default size, so nothing moves
   until the text grows - and stays inside its card. (The marketplace table's negative bleed is the
   other one; it is fixed at `.table-scroll` itself above.) */
@media(max-width:650px){
  #command-center-hero .hero-ring{width:clamp(120px,7.5rem,100%);height:auto;aspect-ratio:1}
  #command-center-hero .hero-ring small{max-width:6rem}
}
/* The marketplace card head holds a title AND a date hint. The legacy `.card-head>*{min-width:0}`
   let the hint shrink below its own words, so at 320px / 200% text (with the wider DejaVu metrics
   of a stock Linux runner) "26 Sep" spilled 2px out of its box. The head now wraps only when the
   two do not fit side by side: the hint drops under the title and keeps its words whole. Nothing
   moves at any width where both already fit. */
#command-center-channels .card-head{flex-wrap:wrap;row-gap:.15rem}
#command-center-channels .card-head>.hint{min-width:auto}
/* No A6.8 forced-colors block: every surface here has a real 1px border that forced colors
   repaints, the sticky heading paints the system Canvas, and meaning lives in text - chip words
   with A6.0's `.status-dot`, the spelled-out stale / hold sentences, the error copy and each
   button label - never in a tint alone. */
