/* ZAG PIM Studio surface — Nexus 商品登録
   Tokens and patterns copied inline (no cross-repo links) from zag-design-system:
     brand/colors.css                   — brand layer, verified 2026-08-30
     surfaces/pim-studio/tokens.css     — type, spacing, radii, shadows, motion
     surfaces/pim-studio/ui_kit/kit.css — page header, card, status badge, stat tile,
                                          summary strip, progress bar, fill grid,
                                          run log, mono inline, segmented filter
   Token names are kept identical to the design system so the two stay diffable.
   #0014DF is ZAG and carries every primary action; the #7A5CFF→#3D7BFF gradient
   means exactly one thing — a machine did this (run progress, AI-generated
   columns, agent cards). It is never decoration. */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Manrope:wght@500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&family=Noto+Sans+JP:wght@400;500;700&display=swap');

:root{
 /* How tall the result's two-column row is: what the viewport has left under the app bar, the page
    header, the result card's own header, the category line, the strip and the legend. One value,
    because the grid and the source panel beside it must come out the same height. */
 --result-h:calc(100vh - 230px);
 /* --- the ZAG blue: brand + primary action (DECISIONS.md S-2) --- */
 --zag-blue:#0014DF;--zag-blue-ink:#0010AE;--zag-blue-tint:#E5E7FC;--zag-blue-wash:rgba(0,20,223,.055);
 /* --- the AI signal: a machine did this --- */
 --ai-purple:#7A5CFF;--ai-purple-600:#5B3FD1;--ai-blue:#3D7BFF;--ai-tint:#F1ECFF;--ai-tint-soft:#FBF9FF;--ai-border:#CFC2FF;
 --ai-gradient:linear-gradient(135deg,#7A5CFF 0%,#3D7BFF 100%);
 --ai-gradient-h:linear-gradient(90deg,#7A5CFF,#3D7BFF);
 --ai-gradient-hdr:linear-gradient(180deg,#FBF9FF,#ffffff);
 /* --- neutrals: zero saturation, --gray-900 is the body near-black --- */
 --gray-50:#FAFAFA;--gray-100:#F4F4F5;--gray-200:#E7E7EA;--gray-300:#D1D5DB;--gray-400:#9CA3AF;
 --gray-500:#6B7280;--gray-600:#4B5563;--gray-700:#374151;--gray-800:#1F2937;--gray-900:#0D0D0D;--white:#FFFFFF;
 --border:#E2E4EC;--border-strong:#D1D5DB;
 /* --- semantic: functional, never decorative --- */
 --success:#107C41;--success-tint:#E6F4EC;--warning:#F59E0B;--warning-tint:#FFF6E5;
 --danger:#D93025;--danger-tint:#FCEAE8;--info:#2B5BDF;--info-tint:#EBF0FE;
 /* --- file-type colors --- */
 --brand-excel:#107C41;--brand-pdf:#D93025;--brand-docx:#2B5BDF;--brand-teal:#14B28A;--brand-zip:#6B5B2A;
 /* --- surfaces --- */
 --bg-canvas:#FFFFFF;--surface-sunken:#F8F9FA;--surface-hover:#F4F6FA;
 /* --- fill source-kind cell tints --- */
 --src-exact:#FFFFFF;--src-alias:#F0F5FF;--src-ai:#FFF8EE;--src-transform:#FFFBE8;--src-constant:#F7F7F2;--src-missing:#FEF1EE;
 /* --- type --- */
 --font-sans:"Inter","Manrope","Noto Sans JP","Hiragino Sans","Yu Gothic",system-ui,sans-serif;
 --font-display:"Manrope","Inter","Noto Sans JP","Hiragino Sans",system-ui,sans-serif;
 --font-mono:"JetBrains Mono","SFMono-Regular",Menlo,Consolas,monospace;
 --fs-display-l:26px;--fs-display-m:22px;--fs-display-s:20px;--fs-h2:14px;--fs-body:13.5px;
 --fs-body-s:12.5px;--fs-label:10.5px;--fs-meta:11.5px;--fs-mono:12px;--fs-mono-micro:10px;
 --tracking-display:-.02em;--tracking-tight:-.01em;--tracking-label:.06em;
 /* --- spacing / radii / shadows --- */
 --space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-8:32px;
 --radius-xs:4px;--radius-sm:6px;--radius-md:8px;--radius-lg:10px;--radius-xl:12px;--radius-2xl:16px;--radius-pill:999px;
 --shadow-xs:0 1px 0 rgba(15,17,23,.04);
 --shadow-sm:0 1px 2px rgba(15,17,23,.06),0 1px 0 rgba(15,17,23,.03);
 --shadow-md:0 4px 12px rgba(15,17,23,.08);
 --shadow-lg:0 12px 32px rgba(15,17,23,.12);
 --shadow-focus-blue:0 0 0 4px rgba(0,20,223,.12);
 --shadow-focus-purple:0 0 0 4px rgba(122,92,255,.10);
 /* --- motion: sharp, finishes quickly. no bounce, no spring --- */
 --ease-standard:cubic-bezier(.2,0,0,1);--dur-fast:120ms;--dur-base:200ms;--dur-slow:320ms;
}

/* =========================================================== base */
*{box-sizing:border-box}
[hidden]{display:none!important}
html{color-scheme:light}
/* No min-width here (Defect S6): the page must fit a 400px viewport. Only the grid/table
   (.fillgrid) and the source panel (.panel) are allowed to run wider than their box, and each
   already scrolls inside its own overflow-x:auto/overflow:auto container -- see .layout below. */
body{margin:0;background:var(--surface-sunken);color:var(--gray-900);
 font-family:var(--font-sans);font-size:var(--fs-body);line-height:1.55;
 -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
[lang="ja"],.lang-ja{font-family:"Noto Sans JP","Hiragino Sans","Yu Gothic","Meiryo","Inter","Manrope",system-ui,sans-serif;font-feature-settings:"palt"}
[lang="ja"] .mono,[lang="ja"] code{font-family:"JetBrains Mono","Noto Sans JP","Hiragino Sans",monospace}
h1{font-family:var(--font-display);font-size:var(--fs-display-m);font-weight:700;letter-spacing:var(--tracking-display);line-height:1.2;margin:0}
h2{font-family:var(--font-display);font-size:var(--fs-h2);font-weight:700;letter-spacing:var(--tracking-tight);margin:0}
h3{font-family:var(--font-display);font-size:13px;font-weight:700;letter-spacing:var(--tracking-tight);color:var(--gray-800);margin:0}
.mono{font-family:var(--font-mono);font-size:var(--fs-mono);font-weight:500;color:var(--gray-800)}
.mono-micro{font-family:var(--font-mono);font-size:var(--fs-mono-micro);font-weight:400;color:var(--gray-500)}
.eyebrow{font-size:var(--fs-label);font-weight:700;letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--gray-500)}
.note{font-size:var(--fs-meta);color:var(--gray-500)}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
svg{flex-shrink:0}
::selection{background:var(--zag-blue-tint)}

/* =========================================================== app bar + wordmark lockup */
.appbar{display:flex;align-items:center;gap:var(--space-3);padding:10px 28px;background:var(--bg-canvas);border-bottom:1px solid var(--border);position:sticky;top:0;z-index:40}
.lockup{display:flex;align-items:center;gap:10px}
.lockup img{height:22px;width:auto;display:block}
.lockup .rule{width:1px;height:18px;background:var(--border-strong)}
.lockup .suffix{font-family:var(--font-display);font-weight:700;font-size:12.5px;letter-spacing:var(--tracking-tight);color:var(--gray-500)}
/* the lockup of a deployment with its own brand (NEXUS_BRAND): plain text, no logo */
.lockup .wordmark{font-family:var(--font-display);font-weight:700;font-size:15px;letter-spacing:var(--tracking-tight);color:var(--gray-900)}

/* --- JA | EN. Only the interface switches; the workbook's own words never do. --- */
.langsw{display:inline-flex;align-items:center;gap:2px;flex:none}
.langsw .bar{color:var(--gray-300);font-size:var(--fs-mono-micro)}
.lang{padding:3px 9px;border:none;border-radius:var(--radius-sm,6px);background:none;cursor:pointer;
 font:inherit;font-size:var(--fs-mono-micro);font-weight:700;letter-spacing:var(--tracking-label);
 color:var(--gray-500);transition:all var(--dur-base) var(--ease-standard)}
.lang:hover{color:var(--gray-900)}
.lang.on{background:var(--zag-blue);color:#fff}
.lang:focus-visible{outline:2px solid var(--zag-blue);outline-offset:2px}
.login-card .langsw{margin-top:14px;justify-content:center;width:100%}

/* --- per-row delete: tertiary, quiet until the row is under the cursor --- */
.delbtn{padding:3px 10px;border:1px solid transparent;border-radius:var(--radius-sm,6px);background:none;
 cursor:pointer;font:inherit;font-size:var(--fs-body-s);color:var(--gray-500);opacity:0;
 transition:all var(--dur-base) var(--ease-standard)}
tr:hover .delbtn,.delbtn:focus-visible{opacity:1}
.delbtn:hover{border-color:var(--danger);background:var(--danger-tint);color:var(--danger)}
.delbtn:focus-visible{outline:2px solid var(--zag-blue);outline-offset:1px}
.delbtn[disabled]{cursor:not-allowed;color:var(--gray-300);border-color:transparent;background:none}
tr:hover .delbtn[disabled]{opacity:1}
.listmsg{display:block;padding:0 16px 14px;color:var(--danger)}

/* --- operator notices: written by the on-call engineer, read by whoever is waiting ---
   A modal, not a bar. This is the one thing on the page written by a person TO the person
   waiting, and a strip above the header reads as chrome -- the user asked for it in the middle of
   the screen with a button that says it has been read. Shown once per notice per viewer. */
.ndlg{width:min(460px,calc(100vw - 32px));padding:0;border:0;border-radius:var(--radius-xl);
 background:#fff;color:var(--gray-900);box-shadow:0 18px 48px rgba(13,13,13,.22)}
.ndlg::backdrop{background:rgba(13,13,13,.42)}
.ndlg .nd-box{padding:22px 24px 18px;display:flex;gap:14px;align-items:flex-start}
.ndlg .nd-icon{flex:none;width:34px;height:34px;border-radius:var(--radius-pill);display:grid;place-items:center}
.ndlg .nd-body{min-width:0}
.ndlg h3{margin:0 0 6px;font-size:15px;font-weight:650;letter-spacing:.01em}
.ndlg .nd-text{margin:0;font-size:var(--fs-body-s);line-height:1.65;color:var(--gray-700);
 white-space:pre-wrap;word-break:break-word}
.ndlg .nd-btns{display:flex;justify-content:flex-end;padding:0 24px 20px}
/* Two tones: somebody is working on it, and somebody has finished. */
.ndlg.warn .nd-icon{background:var(--warning-tint);color:#B45309}
.ndlg.warn h3{color:#B45309}
.ndlg.ok .nd-icon{background:var(--success-tint,#E7F6EE);color:var(--success,#127A45)}
.ndlg.ok h3{color:var(--success,#127A45)}

/* --- the one confirmation the app asks for: destructive, and it names the action --- */
.cdlg{border:1px solid var(--border);border-radius:var(--radius-xl);padding:0;max-width:420px;
 background:#fff;color:var(--gray-900);box-shadow:0 12px 40px rgba(13,13,13,.18)}
.cd-box{padding:20px 22px}
.cdlg::backdrop{background:rgba(13,13,13,.32)}
.cd-msg{font-size:var(--fs-body);line-height:1.6;margin-bottom:18px}
.cd-btns{display:flex;justify-content:flex-end;gap:8px}

/* --- EN only: says why the log lines and column headers above stayed Japanese --- */
.i18nnote{margin:8px 16px 0;font-size:var(--fs-mono-micro);color:var(--gray-400)}
.appbar .back{display:inline-flex;align-items:center;gap:5px;font-size:var(--fs-meta);color:var(--gray-600);text-decoration:none;padding:5px 9px 5px 6px;border-radius:var(--radius-md);transition:background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard)}
.appbar .back:hover{background:var(--surface-hover);color:var(--gray-900)}
.appbar .spacer{flex:1}

/* =========================================================== page shell */
main{padding:0 28px 32px;max-width:1520px;margin:0 auto}
.page-hdr{display:flex;align-items:flex-end;gap:var(--space-4);padding:22px 0 14px;border-bottom:1px solid var(--border);margin-bottom:18px}
.page-hdr .ttl{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.page-hdr .sub{font-size:var(--fs-meta);color:var(--gray-500);margin-top:5px}
.page-hdr .actions{margin-left:auto;display:flex;gap:var(--space-2);flex-shrink:0}

/* =========================================================== card */
.card{background:var(--bg-canvas);border:1px solid var(--border);border-radius:var(--radius-xl);margin-bottom:16px}
.card-hdr{padding:12px 16px;display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--border)}
.card-hdr h2{white-space:nowrap}
.card-hdr .right{margin-left:auto;display:flex;align-items:center;gap:var(--space-2)}
.card-body{padding:14px 16px}
.card-body.tight{padding:0}

/* =========================================================== buttons */
.btn{font-family:var(--font-sans);font-size:13px;font-weight:600;border-radius:var(--radius-md);padding:8px 14px;
 cursor:pointer;border:1px solid transparent;line-height:1;white-space:nowrap;text-decoration:none;
 display:inline-flex;align-items:center;gap:6px;
 transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard)}
.btn-primary{background:var(--zag-blue);color:#fff}
.btn-primary:hover{background:var(--zag-blue-ink)}
.btn-secondary{background:#fff;color:var(--gray-800);border-color:var(--border)}
.btn-secondary:hover{background:var(--zag-blue-wash);border-color:var(--zag-blue);color:var(--zag-blue)}
.btn-ghost{background:transparent;color:var(--gray-700)}
.btn-ghost:hover{background:var(--surface-hover);color:var(--gray-900)}
.btn-danger{background:#fff;color:var(--danger);border-color:var(--border)}
.btn-danger:hover{background:var(--danger-tint);border-color:var(--danger)}
.btn-sm{padding:5px 10px;font-size:12px;border-radius:var(--radius-sm)}
.btn:active{opacity:.85}
.btn:disabled{cursor:not-allowed;opacity:.6}
.btn-primary:disabled{background:var(--gray-200);color:var(--gray-500);opacity:1}
.btn:focus-visible{outline:none;box-shadow:var(--shadow-focus-blue)}
.iconbtn{width:26px;height:26px;padding:0;justify-content:center;background:transparent;border:1px solid transparent;border-radius:var(--radius-sm);color:var(--gray-500);cursor:pointer;display:inline-flex;align-items:center;transition:all var(--dur-fast) var(--ease-standard)}
.iconbtn:hover{background:var(--danger-tint);color:var(--danger)}
.iconbtn:focus-visible{outline:none;box-shadow:var(--shadow-focus-blue)}

/* =========================================================== inputs */
input[type=password],input[type=text]{font-family:var(--font-sans);font-size:var(--fs-body);color:var(--gray-900);
 padding:9px 12px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:#fff;
 transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard)}
input[type=password]:focus,input[type=text]:focus{outline:none;border-color:var(--zag-blue);box-shadow:var(--shadow-focus-blue)}
input::placeholder{color:var(--gray-400)}

/* =========================================================== status badge */
.badge{display:inline-flex;align-items:center;gap:5px;padding:3px 9px 3px 7px;border-radius:var(--radius-pill);
 font-size:var(--fs-label);font-weight:700;line-height:1.3;white-space:nowrap}
.badge .dot{width:6px;height:6px;border-radius:var(--radius-pill);flex-shrink:0}
.badge.success{background:var(--success-tint);color:var(--success)}.badge.success .dot{background:var(--success)}
.badge.warning{background:var(--warning-tint);color:#B45309}.badge.warning .dot{background:var(--warning)}
.badge.danger{background:var(--danger-tint);color:#A1170F}.badge.danger .dot{background:var(--danger)}
.badge.info{background:var(--info-tint);color:var(--info)}.badge.info .dot{background:var(--info)}
.badge.accent{background:var(--ai-tint);color:var(--ai-purple-600)}.badge.accent .dot{background:var(--ai-purple)}
.badge.neutral{background:var(--gray-100);color:var(--gray-600)}.badge.neutral .dot{background:var(--gray-400)}
.badge.brand{background:var(--zag-blue-tint);color:var(--zag-blue)}.badge.brand .dot{background:var(--zag-blue)}
.badge .dot.pulse{animation:dotpulse 2s var(--ease-standard) infinite}
@keyframes dotpulse{0%{box-shadow:0 0 0 0 rgba(122,92,255,.45)}70%{box-shadow:0 0 0 6px rgba(122,92,255,0)}100%{box-shadow:0 0 0 0 rgba(122,92,255,0)}}

/* type chip (file kind) */
.tchip{display:inline-flex;align-items:center;gap:5px;padding:3px 9px 3px 6px;border-radius:var(--radius-pill);
 border:1px solid var(--border);background:#fff;font-family:var(--font-mono);font-size:11px;font-weight:600;color:var(--gray-700)}
.tchip .sq{width:8px;height:8px;border-radius:2px;background:var(--gray-400)}
.tchip.xlsx .sq{background:var(--brand-excel)}
.tchip.pdf .sq{background:var(--brand-pdf)}
.tchip.image .sq{background:var(--brand-docx)}
.tchip.zip .sq{background:var(--brand-zip)}

/* AI-generated marker — the gradient means "a machine did this" */
.aipill{display:inline-flex;align-items:center;gap:3px;padding:2px 6px;border-radius:var(--radius-xs);
 background:var(--ai-gradient);color:#fff;font-family:var(--font-sans);font-size:9.5px;font-weight:700;letter-spacing:.03em;line-height:1.3}
.mpill{display:inline-flex;align-items:center;padding:2px 6px;border-radius:var(--radius-xs);font-size:9.5px;font-weight:700;letter-spacing:.03em;line-height:1.3}
.mpill.copied{background:var(--info-tint);color:var(--info)}
.mpill.fixed{background:var(--gray-100);color:var(--gray-600)}
.mpill.rule{background:var(--warning-tint);color:#B45309}
.mpill.blank{background:var(--gray-100);color:var(--gray-400)}

/* =========================================================== tables */
table{border-collapse:collapse;width:100%;font-size:var(--fs-body-s)}
table.list th{text-align:left;font-weight:700;font-size:var(--fs-label);letter-spacing:var(--tracking-label);
 text-transform:uppercase;color:var(--gray-500);padding:8px 14px;background:var(--surface-sunken);border-bottom:1px solid var(--border-strong)}
table.list td{padding:11px 14px;border-bottom:1px solid var(--border);color:var(--gray-800);vertical-align:middle}
table.list tr:last-child td{border-bottom:none}
table.list tbody tr.click{cursor:pointer;transition:background var(--dur-fast) var(--ease-standard)}
table.list tbody tr.click:hover td{background:var(--surface-hover)}
table.list th:not(:first-child),table.list td:not(:first-child){width:1%;white-space:nowrap}
table.list td.right{text-align:right}
table.list td.empty{color:var(--gray-500);font-size:var(--fs-meta);text-align:center;padding:26px 14px}

/* The job list at 727px was rendered with no scrolling ancestor, so it forced the whole LIST page
   to scroll sideways at any narrower width (Defect F3) -- the same escape hatch as .fillgrid:
   `width:auto` so the table keeps its own content width instead of being squeezed to the wrapper's,
   `min-width:100%` so it is never narrower, and the wrapper (not body, not main) owns the scroll. */
.tablewrap{overflow-x:auto}
.tablewrap table.list{width:auto;min-width:100%}
/* L7. At 400px the wrapper already DOES scroll -- measured: 342px of viewport against a 466px
   table, with the page itself not scrolling sideways. What was missing is any sign that it does.
   This platform paints overlay scrollbars (a probe element with overflow-x:scroll reserves 0px),
   so the scrollbar appears only mid-gesture and 作成 and 削除 sit off the right edge with nothing
   on screen saying they are reachable -- which is what the dev acceptance read as clipped.
   A scroll shadow is painted whether or not a scrollbar ever is. The `local` pair are the
   background-coloured masks that ride with the content; the `scroll` pair are the shadows they
   uncover, so the right-hand shadow shows exactly while there is more table to the right and is
   gone at the end of the scroll. */
.tablewrap{
 background:
  linear-gradient(to right,var(--bg-canvas) 30%,rgba(255,255,255,0)) left center/28px 100% no-repeat local,
  linear-gradient(to left,var(--bg-canvas) 30%,rgba(255,255,255,0)) right center/28px 100% no-repeat local,
  radial-gradient(farthest-side at 0 50%,rgba(16,24,40,.13),rgba(16,24,40,0)) left center/12px 100% no-repeat scroll,
  radial-gradient(farthest-side at 100% 50%,rgba(16,24,40,.13),rgba(16,24,40,0)) right center/12px 100% no-repeat scroll}
/* And where a platform DOES paint a real scrollbar, give it the page's own colours rather than the
   default chrome. Deliberately no `scrollbar-width`: where the standard property is honoured these
   rules are ignored, and `thin` is still an overlay on macOS -- i.e. it would buy nothing and cost
   the styling below. */
.tablewrap::-webkit-scrollbar{-webkit-appearance:none;height:8px}
.tablewrap::-webkit-scrollbar-track{background:transparent}
.tablewrap::-webkit-scrollbar-thumb{background:var(--gray-400);border-radius:var(--radius-pill)}
.tablewrap::-webkit-scrollbar-thumb:hover{background:var(--gray-500)}

/* =========================================================== login */
body.page-list main{max-width:1120px}
body.page-login{background:var(--surface-sunken);display:flex;align-items:center;justify-content:center;min-height:100vh;min-width:0}
.login-card{width:340px;background:#fff;border:1px solid var(--border);border-radius:var(--radius-2xl);padding:28px 26px 22px;box-shadow:var(--shadow-sm);margin:0 16px}
.login-card .lockup{margin-bottom:18px}
.login-card h1{font-size:18px;margin-bottom:4px}
.login-card .sub{font-size:var(--fs-meta);color:var(--gray-500);margin-bottom:18px}
.login-card .field{display:flex;flex-direction:column;gap:6px;margin-bottom:14px}
.login-card .field label{font-size:var(--fs-label);font-weight:700;letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--gray-500)}
.login-card input{width:100%}
.login-card .btn{width:100%;justify-content:center;padding:10px 14px}
.errline{display:none;align-items:center;gap:6px;margin-top:12px;font-size:var(--fs-meta);color:var(--danger)}
.errline.on{display:flex}

/* =========================================================== inline new-job form */
.inlineform{display:none;gap:var(--space-2);align-items:center;padding:12px 16px;border-bottom:1px solid var(--border);background:var(--surface-sunken)}
.inlineform.on{display:flex}
.inlineform input{flex:1;max-width:340px}
.inlineform .btn{padding:9px 14px}

/* =========================================================== upload */
.drop{border:1.5px dashed var(--border-strong);border-radius:var(--radius-2xl);padding:22px;text-align:center;
 background:var(--gray-50);transition:border-color var(--dur-base) var(--ease-standard),background var(--dur-base) var(--ease-standard)}
.drop.over{border-color:var(--zag-blue);background:var(--zag-blue-wash)}
.drop .dt{font-size:var(--fs-body);color:var(--gray-700);font-weight:500}
.drop .dh{font-size:var(--fs-meta);color:var(--gray-500);margin-top:4px}
.drop .dbtns{display:flex;gap:var(--space-2);justify-content:center;margin-top:14px}
.drop input[type=file]{display:none}
/* One upload at a time: while batches are in flight there is nothing to drop onto. */
.drop.busy{opacity:.55;pointer-events:none}
/* A 447-file upload used to run the whole page on for screens of table rows below a drop zone that
   scrolled away with it. The box now caps itself (~40% of the viewport) and scrolls internally --
   the drop zone above stays put, and the card's own header (資料 N 件) stays visible above the box
   rather than being carried off with the rows. */
.filewrap{margin-top:14px;border:1px solid var(--border);border-radius:var(--radius-lg);overflow:auto;max-height:40vh}
.filewrap table.list th:first-child,.filewrap table.list td:first-child{padding-left:14px}
.filewrap table.list thead th{position:sticky;top:0;z-index:1}
.uploadbar{display:flex;align-items:center;gap:10px;margin-top:14px}
.uploadbar .note{margin-left:auto}
/* The upload in flight. Same card language as the file list below it: one hairline box, the run
   bar's own progress track, the list table's rows. The bar is a PLAIN blue fill, never the
   #7A5CFF->#3D7BFF gradient -- that gradient means "a machine did this" and nothing else
   (app.css header), and sending a file is the person's own doing. */
.upprog{margin-top:14px;border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden;background:var(--white)}
.upprog .uph{display:flex;align-items:center;gap:10px;padding:10px 14px}
.upprog .upsay{font-size:var(--fs-meta);color:var(--gray-700);font-weight:500}
.upprog .uph .mono{margin-left:auto;color:var(--gray-500);font-size:var(--fs-mono)}
.upprog .uph .btn{padding:6px 11px;font-size:var(--fs-meta)}
.upprog .prog{height:6px;background:var(--gray-100);overflow:hidden}
.upprog .prog>i{display:block;height:100%;width:0;background:var(--zag-blue);transition:width var(--dur-base) var(--ease-standard)}
.upprog.bad .prog>i{background:var(--danger)}
/* 446 rows is a real upload, so the list scrolls inside the card rather than pushing the page
   down; the rows themselves are updated in place, never re-rendered (nexus/ui/static/app.js). */
.upprog .uplist{max-height:240px;overflow:auto;border-top:1px solid var(--border)}
.upprog .uplist table.list thead th{position:sticky;top:0;z-index:1}
.upprog .uplist table.list td{padding:6px 14px;font-size:var(--fs-meta)}
.upprog .uplist table.list td:first-child{max-width:340px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.upprog .ust{font-size:var(--fs-label);font-weight:700;letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--gray-500)}
.upprog .ust.sending{color:var(--zag-blue)}
.upprog .ust.done{color:var(--success)}
.upprog .ust.failed{color:var(--danger)}
.upprog .ust.cancelled{color:var(--warning)}
.upprog .rebtn{padding:3px 9px;font-size:var(--fs-label);font-weight:700;letter-spacing:var(--tracking-label);
 text-transform:uppercase;border:1px solid var(--border-strong);border-radius:var(--radius-sm);
 background:var(--white);color:var(--gray-700);cursor:pointer}
.upprog .rebtn:hover{border-color:var(--zag-blue);color:var(--zag-blue)}
.upprog .rebtn:focus-visible{outline:none;box-shadow:var(--shadow-focus-blue)}

/* =========================================================== stepper */
.runbar{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border)}
.runbar .prog{flex:1;height:6px;background:var(--gray-100);border-radius:var(--radius-pill);overflow:hidden}
.runbar .prog>i{display:block;height:100%;width:0;background:var(--ai-gradient-h);border-radius:var(--radius-pill);transition:width var(--dur-slow) var(--ease-standard)}
.runbar .pct{font-family:var(--font-mono);font-size:var(--fs-mono);font-weight:600;color:var(--gray-600);min-width:44px;text-align:right}
/* THE RAIL KEYS ON ITS CONTENT, not on a breakpoint. `minmax(max-content,1fr)` resolves to plain
   1fr for every track whose 1fr share already clears its own max-content -- which it does at the
   1520px desktop, where this renders byte for byte as it did with `1fr` -- and holds each track at
   its content width where it does not, whereupon the rail overflows its box and the scroller takes
   over. 480px was the sheet's breakpoint, not the rail's: with ten stages the rail's natural
   one-line width is about a thousand pixels, so between 480px and that the columns were still 1fr
   and still squashed 資料の発見 to one character a line (review of fd96538, Medium 1).
   The local/scroll gradient pair paints a shadow only while there IS something to scroll, so a
   desktop that never overflows shows none of it. */
.stepper{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(max-content,1fr);gap:0;
 padding:16px 16px 40px;overflow-x:auto;
 background:
  linear-gradient(to right,var(--bg-canvas) 30%,rgba(255,255,255,0)) left center/28px 100% no-repeat local,
  linear-gradient(to left,var(--bg-canvas) 30%,rgba(255,255,255,0)) right center/28px 100% no-repeat local,
  radial-gradient(farthest-side at 0 50%,rgba(16,24,40,.13),rgba(16,24,40,0)) left center/12px 100% no-repeat scroll,
  radial-gradient(farthest-side at 100% 50%,rgba(16,24,40,.13),rgba(16,24,40,0)) right center/12px 100% no-repeat scroll}
.stepper::-webkit-scrollbar{-webkit-appearance:none;height:8px}
.stepper::-webkit-scrollbar-track{background:transparent}
.stepper::-webkit-scrollbar-thumb{background:var(--gray-400);border-radius:var(--radius-pill)}
.stepper::-webkit-scrollbar-thumb:hover{background:var(--gray-500)}
.stp{position:relative;padding:0 8px;min-width:0}
.stp::after{content:"";position:absolute;top:8px;left:30px;right:-8px;height:2px;background:var(--gray-200)}
.stp:last-child::after{display:none}
.stp.done::after{background:var(--zag-blue)}
.stp .mark{width:18px;height:18px;border-radius:var(--radius-pill);border:2px solid var(--gray-300);background:#fff;
 display:flex;align-items:center;justify-content:center;color:#fff;position:relative;z-index:1}
.stp .nm{font-size:var(--fs-meta);font-weight:600;color:var(--gray-500);margin-top:8px;line-height:1.35;white-space:nowrap}
.stp .sec{font-family:var(--font-mono);font-size:var(--fs-mono-micro);color:var(--gray-400);margin-top:2px;white-space:nowrap}
/* The running stage's sub-status, in normal flow BENEATH the rail. It used to be absolutely
   positioned inside the running step; out of flow it stretches no track, but it DOES extend a
   scroll container's scrollable range, and the rail is a scroller at every width now.
   The negative top margin is deliberate: .stepper keeps its 40px bottom padding, the space the
   note used to be painted into, so the rail's own box is unchanged and the note appears exactly
   where it did. */
.stepnote{margin:-24px 16px 12px;font-size:var(--fs-meta);color:var(--gray-600);
 white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stp.done .mark{border-color:var(--zag-blue);background:var(--zag-blue)}
.stp.done .nm{color:var(--gray-800)}
.stp.running .mark{border-color:transparent;background:var(--ai-gradient);animation:markpulse 1.6s var(--ease-standard) infinite}
.stp.running .nm{color:var(--ai-purple-600);font-weight:700}
.stp.error .mark{border-color:var(--danger);background:var(--danger)}
.stp.error .nm{color:var(--danger)}
@keyframes markpulse{0%{box-shadow:0 0 0 0 rgba(122,92,255,.4)}70%{box-shadow:0 0 0 7px rgba(122,92,255,0)}100%{box-shadow:0 0 0 0 rgba(122,92,255,0)}}
@media(prefers-reduced-motion:reduce){.stp.running .mark,.badge .dot.pulse,.agent.live{animation:none}}
/* What is still different on a phone, and only that: the rail's own treatment is on the base rule
   above and needs no breakpoint. `#run{min-width:0}` was here and is gone -- #run is a block child
   of a plain block, so the automatic minimum size it defeats never applied (review of fd96538,
   Low 1). The connector's right offset moves with the padding: .stp::after is right:-8px against
   the base padding:0 8px, so at padding:0 12px it stopped 4px short of the next mark (Low 2). */
@media (max-width:480px){
 .stp{padding:0 12px}
 .stp::after{right:-12px}}

/* =========================================================== run log */
/* 「バッチ 3 / 9」 (brief item 16): one line per batch, its own log behind a disclosure. */
.batches{margin:0 16px 12px;border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden}
.batches .bhead{display:flex;flex-wrap:wrap;gap:8px;align-items:baseline;padding:8px 12px;background:var(--surface-sunken)}
.batches .bnow{font-weight:700;color:var(--gray-800)}
.batches .batch{border-top:1px solid var(--border)}
.batches .batch summary{display:flex;flex-wrap:wrap;gap:8px;align-items:center;padding:6px 12px;cursor:pointer;font-size:var(--fs-meta)}
.batches .bn{font-family:var(--font-mono);color:var(--gray-700);min-width:0}
.batches .bsize,.batches .try{color:var(--gray-500)}
.batches .blog{height:auto;max-height:180px;margin:0 12px 10px;border-radius:var(--radius-md)}
.logwrap{position:relative;margin:0 16px 16px}
.runlog{background:var(--gray-900);border-radius:var(--radius-xl);padding:12px 14px;height:210px;overflow-y:auto;
 font-family:var(--font-mono);font-size:11.5px;line-height:1.6}
.runlog .ln{display:grid;grid-template-columns:62px 68px 1fr;gap:10px;padding:1px 0;color:#B8C0D0}
.runlog .t{color:#6B7280}
.runlog .tag{font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.runlog .tag.info{color:#82A2FF}
.runlog .tag.ok{color:#57D58B}
.runlog .tag.ai{color:#B8A4FF}
.runlog .tag.warn{color:#FBBF24}
.runlog .tag.err{color:#FF6B5B}
.runlog .m{color:#D7DCE6;word-break:break-word}
.runlog .d{color:#7C8494}
.runlog .empty{color:#6B7280}
.logjump{position:absolute;right:14px;bottom:12px;display:none;background:rgba(13,13,13,.86);color:#D7DCE6;
 border:1px solid #333;border-radius:var(--radius-pill);padding:3px 10px;font-size:11px;font-family:var(--font-sans);cursor:pointer}
.logjump.on{display:block}
.logjump:hover{color:#fff;border-color:#555}

/* =========================================================== agent cards */
.agents{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:8px;padding:0 16px 16px}
.agent{border:1px solid var(--border);border-radius:var(--radius-lg);padding:9px 11px;background:var(--surface-sunken);
 display:flex;align-items:center;gap:8px;font-size:var(--fs-body-s);transition:all var(--dur-base) var(--ease-standard)}
.agent .id{font-family:var(--font-mono);font-size:11px;color:var(--gray-700);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.agent .sec{font-family:var(--font-mono);font-size:var(--fs-mono-micro);color:var(--gray-400)}
.agent.live{border-color:var(--ai-border);background:var(--ai-tint-soft);animation:agentpulse 1.6s var(--ease-standard) infinite}
@keyframes agentpulse{0%{box-shadow:0 0 0 0 rgba(122,92,255,.30)}70%{box-shadow:0 0 0 6px rgba(122,92,255,0)}100%{box-shadow:0 0 0 0 rgba(122,92,255,0)}}

/* =========================================================== summary strip */
.strip{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--border);border-radius:var(--radius-xl);overflow:hidden;background:#fff;margin:0 0 14px}
.strip-cell{padding:12px 16px;border-right:1px solid var(--border);display:flex;flex-direction:column;gap:3px}
.strip-cell:last-child{border-right:none}
.strip-cell .l{font-size:var(--fs-mono-micro);font-weight:700;letter-spacing:var(--tracking-label);text-transform:uppercase;color:var(--gray-500)}
.strip-cell .v{font-family:var(--font-display);font-weight:700;font-size:var(--fs-display-s);letter-spacing:var(--tracking-tight);color:var(--gray-900);line-height:1.1}
.strip-cell .s{font-size:var(--fs-mono-micro);color:var(--gray-500)}
.strip-cell .v.brand{color:var(--zag-blue)}
.strip-cell .v.success{color:var(--success)}
.strip-cell .v.danger{color:var(--danger)}

/* --- which category block the grid is showing, above the strip --- */
.catline{display:flex;align-items:center;gap:8px;margin:0 0 10px;font-size:var(--fs-mono-micro);color:var(--gray-500)}
.catline .l{font-weight:700;letter-spacing:var(--tracking-label);text-transform:uppercase;white-space:nowrap}
.catline .mono{font-size:var(--fs-mono-micro)}
.catline .p{color:var(--gray-900)}
/* A WEB workbook with a sheet per たの分類: how many, and which one is drawn. */
.catline .n{font-weight:700;color:var(--gray-900);white-space:nowrap}
/* The WEB tab's own count: one file, N 分類. */
.tab .tabn{margin-left:5px;font-size:.85em;opacity:.75;font-variant-numeric:tabular-nums}

/* The sheet picker, its own row directly above the fill grid rather than inside the header line
   (brief follow-up): read together with the table it drives, left-aligned, one hairline under it. */
.sheetpick{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--border);
 font-size:var(--fs-mono-micro);color:var(--gray-900);text-align:left;flex:0 0 auto}
.sheetpick select{font:inherit;font-size:var(--fs-mono-micro);color:var(--gray-900);background:#fff;border:1px solid var(--gray-300);border-radius:6px;padding:2px 6px;max-width:320px}


/* --- 「不足している情報」: a summary sentence, with the per-attribute list behind one disclosure.
   Informational, not a warning: nothing here went wrong -- the documents simply do not say it. --- */
.gaps{border:1px solid var(--border);border-radius:var(--radius-xl);background:#fff;margin:0 0 14px;overflow:hidden}
.gaps .ghdr{display:flex;align-items:center;gap:8px;padding:10px 16px;border-bottom:1px solid var(--border);
 background:var(--gray-50);color:var(--gray-900);font-weight:700;font-size:var(--fs-body-s)}
.gaps .gbody{padding:12px 16px}
.gaps .gsay{margin:0;font-size:var(--fs-body-s);line-height:1.7;color:var(--gray-800);max-width:64em}
.gaps .gdet{margin:10px 0 0}
.gaps .gdet>summary{cursor:pointer;font-size:var(--fs-body-s);color:var(--zag-blue);width:fit-content}
.gaps .gdet>summary:focus-visible{outline:2px solid var(--zag-blue);outline-offset:2px;border-radius:4px}
.glist{margin:8px 0 0;border-top:1px solid var(--border)}
.grow{display:grid;grid-template-columns:minmax(110px,1fr) 80px minmax(120px,2fr) minmax(140px,2fr);gap:10px;
 align-items:baseline;padding:7px 0;border-bottom:1px solid var(--border);font-size:var(--fs-body-s)}
.grow .ga{font-weight:700;color:var(--gray-900)}
.grow .gn{color:var(--gray-500);white-space:nowrap}
.grow .gr{color:var(--gray-500);overflow-wrap:anywhere}
.grow .gd{color:var(--gray-800)}

/* --- one run-log line's own technical detail, folded away (an error line only) --- */
.lndetail{display:inline-block;vertical-align:top;margin-left:6px}
.lndetail>summary{display:inline;cursor:pointer;color:var(--gray-400);font-size:var(--fs-mono-micro)}
.lndetail>summary:focus-visible{outline:2px solid var(--zag-blue);outline-offset:2px}
.lndetail>span{display:block;margin-top:4px;color:var(--gray-500);white-space:pre-wrap;word-break:break-word}

/* =========================================================== segmented sheet filter */
.seg{display:inline-flex;gap:2px;padding:3px;background:var(--surface-sunken);border-radius:var(--radius-lg);border:1px solid var(--border)}
.seg button{font-family:var(--font-sans);font-size:var(--fs-body-s);font-weight:600;background:transparent;color:var(--gray-600);
 border:none;padding:4px 12px;border-radius:7px;cursor:pointer;transition:all var(--dur-fast) var(--ease-standard)}
.seg button.on{background:#fff;color:var(--gray-900);box-shadow:0 1px 2px rgba(15,17,23,.08)}
.seg button:focus-visible{outline:none;box-shadow:var(--shadow-focus-blue)}

/* =========================================================== result layout */
/* One height for both columns. The grid capped itself at 64vh while the panel capped itself
   against the viewport, so a long source panel ran on past the bottom of the table beside it --
   two boxes that scroll internally and do not line up read as two unrelated pages. The ROW owns
   the height now and both sides fill it; each still scrolls inside itself. */
.layout{display:grid;grid-template-columns:minmax(0,1fr) minmax(400px,33%);gap:16px;padding:0 16px 16px;
 align-items:stretch;height:var(--result-h);min-height:380px}
.legend{display:flex;gap:14px;align-items:center;flex-wrap:wrap;padding:0 16px 10px}
.legend .item{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-meta);color:var(--gray-600)}
.legend .swatch{width:14px;height:14px;border-radius:3px;border:1px solid var(--border)}

/* fill grid */
.fillgrid{background:#fff;border:1px solid var(--border);border-radius:var(--radius-xl);overflow:hidden;min-width:0;
 display:flex;flex-direction:column;min-height:0}
.fillgrid .scrollwrap{overflow:auto;flex:1 1 auto;min-height:0}
.fillgrid table{border-collapse:separate;border-spacing:0;width:auto;min-width:100%}
.fillgrid th{position:sticky;top:0;z-index:2;background:var(--surface-sunken);padding:6px 10px;text-align:left;
 border-bottom:1px solid var(--border-strong);border-right:1px solid var(--border);white-space:nowrap;cursor:help;vertical-align:top}
.fillgrid th .hn{font-family:var(--font-mono);font-size:var(--fs-mono-micro);font-weight:600;color:var(--gray-700);display:block}
.fillgrid th .hm{display:block;margin-top:3px}
.fillgrid td{position:relative;padding:5px 10px;border-bottom:1px solid var(--border);border-right:1px solid var(--border);
 font-family:var(--font-mono);font-size:11.5px;color:var(--gray-800);max-width:280px;overflow:hidden;
 text-overflow:ellipsis;white-space:nowrap;cursor:pointer}
.fillgrid tbody tr:hover td{background:var(--surface-hover)}
.fillgrid tr.rep td{background:var(--zag-blue-wash)}
.fillgrid tr.rep:hover td{background:var(--zag-blue-tint)}
.fillgrid tr.rep td:first-child{box-shadow:inset 3px 0 0 var(--zag-blue)}
.fillgrid td.blank{background:var(--gray-50);color:var(--gray-300)}
.fillgrid tr.rep td.blank{background:var(--gray-50)}
.fillgrid td[data-has="1"]::after{content:"";position:absolute;top:0;right:0;width:0;height:0;
 border-width:0 5px 5px 0;border-style:solid;border-color:transparent var(--zag-blue) transparent transparent;opacity:.45}
.fillgrid td.sel{outline:2px solid var(--zag-blue);outline-offset:-2px;background:var(--zag-blue-tint)}
.fillgrid td:focus-visible{outline:2px solid var(--zag-blue);outline-offset:-2px}

/* provenance pane */
.panel{overflow:auto;background:#fff;min-height:0;
 border:1px solid var(--border);border-radius:var(--radius-xl);min-width:0}
.panel .phdr{padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.panel .pbody{padding:14px 16px}
.panel .empty{padding:28px 16px;text-align:center;color:var(--gray-500);font-size:var(--fs-meta)}
.panel .value{font-family:var(--font-mono);font-size:13px;font-weight:500;color:var(--gray-900);background:var(--surface-sunken);
 border:1px solid var(--border);border-radius:var(--radius-md);padding:9px 11px;white-space:pre-wrap;word-break:break-word;margin-bottom:10px}
/* A cell blanked by a read refusal: no value, but a box is still there to click. Said in the same
   box the value would sit in, not styled as if it were one. */
.panel .value.empty{font-family:var(--font-sans);font-weight:400;font-style:italic;color:var(--gray-500)}
/* A cell settled from its sibling sheets is not quite what the page was read as -- the trace has to
   say so under the value it changed, not only in the Excel comment nobody here opens. */
.panel .cellnote{margin:-6px 0 10px;font-size:var(--fs-meta);color:var(--gray-500);font-style:italic}
.panel .meta-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-bottom:4px}
.src{border-top:1px solid var(--border);margin-top:14px;padding-top:12px}
.src .shdr{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:8px}
.src .sn{font-family:var(--font-mono);font-size:var(--fs-mono-micro);font-weight:700;color:#fff;background:var(--gray-600);
 border-radius:var(--radius-xs);padding:1px 5px}
.src .fn{font-family:var(--font-mono);font-size:11px;color:var(--gray-700);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
.src .raw{font-family:var(--font-mono);font-size:11px;color:var(--gray-600);background:var(--surface-sunken);
 border-radius:var(--radius-sm);padding:6px 8px;margin-bottom:8px;word-break:break-word}
.miniwrap{overflow-x:auto;margin-bottom:8px;border-radius:var(--radius-sm)}
table.mini{border-collapse:collapse;font-family:var(--font-mono);font-size:10.5px;width:auto}
table.mini td{border:1px solid var(--border);padding:2px 6px;max-width:150px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--gray-600)}
table.mini td.hit{background:var(--warning-tint);outline:2px solid #E6A100;font-weight:700;color:var(--gray-900)}
canvas.zoom{width:100%;display:block;border:1px solid var(--border);border-radius:var(--radius-md);margin-bottom:8px}
.page{position:relative;border:1px solid var(--border);border-radius:var(--radius-md);overflow:hidden;margin-bottom:8px}
.page img{width:100%;display:block}
.box{position:absolute;border:3px solid #E6A100;background:rgba(255,215,0,.22)}
.box.col{border-style:dashed;background:rgba(0,20,223,.06);border-color:var(--zag-blue)}

/* =========================================================== degraded bar (GET /api/health) */
.degradedbar{margin:0 0 12px;padding:8px 12px;border-radius:var(--radius-md);background:var(--warning-tint);
 color:#B45309;font-size:var(--fs-body-s);display:flex;align-items:center;gap:8px}

/* =========================================================== error line on the run card */
.runerr{margin:0 16px 16px;padding:10px 12px;border-radius:var(--radius-md);background:var(--danger-tint);
 color:#A1170F;font-size:var(--fs-body-s);display:flex;align-items:center;gap:8px}

/* --- たの分類 beside the status badge, known long before the result card opens --- */
.cathdr{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border:1px solid var(--border);
 border-radius:999px;background:var(--gray-50);font-size:var(--fs-mono-micro);color:var(--gray-500);vertical-align:middle}
/* L8. Japanese breaks between any two characters, so at 400px the label wrapped as たの分／類 --
   a word split down the middle. It is a fixed label of four characters and never needs to wrap. */
.cathdr .l{font-weight:700;letter-spacing:var(--tracking-label);text-transform:uppercase;white-space:nowrap}
.cathdr .mono{font-family:var(--font-mono);font-size:var(--fs-mono-micro);color:var(--gray-800)}
.cathdr .p{color:var(--gray-900)}
/* L8 again, on the badge this time: 暫定 broke as 暫 over 定 at 400px. Japanese has no spaces, so a
   line break may fall between any two characters -- there is nothing in a two-character badge to
   stop it. */
.cathdr .prov{padding:0 6px;border-radius:999px;background:var(--warning-tint);color:#B45309;font-weight:700;white-space:nowrap}

/* --- the failure's technical detail: present, but folded away under the run log --- */
.errdetail{margin:10px 16px 0;border:1px solid var(--border);border-radius:var(--radius-md);background:var(--gray-50)}
.errdetail>summary{padding:8px 12px;cursor:pointer;font-size:var(--fs-body-s);color:var(--gray-600)}
.errdetail>summary:focus-visible{outline:2px solid var(--zag-blue);outline-offset:-2px}
.errtext{margin:0;padding:0 12px 12px;font-family:var(--font-mono);font-size:var(--fs-mono-micro);
 color:var(--gray-700);white-space:pre-wrap;word-break:break-word}

/* Stacked, the two are one column and a shared height would only squash them: each takes its own
   again, and the page scrolls as it did. */
@media(max-width:1280px){.layout{grid-template-columns:1fr;height:auto;min-height:0}
 .fillgrid .scrollwrap{max-height:64vh}.panel{max-height:calc(100vh - 88px)}}

/* --- regenerate an AI-written column -------------------------------------------------------- */
/* The ✦ the brief asks for, as an inline SVG: emoji are banned as iconography across every ZAG
   surface, and U+2726 is one. It sits in the header beside the AI pill and is the click target. */
.aigo{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-left:2px;
 padding:0;border:none;border-radius:var(--radius-xs);background:transparent;color:#7A5CFF;cursor:pointer;
 transition:background var(--dur-base) var(--ease-standard),color var(--dur-base) var(--ease-standard)}
.aigo:hover{background:var(--gray-100);color:#3D7BFF}
.aigo:focus-visible{outline:2px solid var(--zag-blue);outline-offset:1px}
/* The column being rewritten: the text on screen is the version being replaced. */
/* A regenerate, as it happens. The column being rewritten dims; a cell still queued pulses softly
   on top of that, and a cell the worker has just written fades up in its place. Per cell, because a
   row that quotes the maker is never rewritten and must not sit there pulsing. */
#grid td.busy{opacity:.4}
#grid td.pending{color:transparent;position:relative}
#grid td.pending::before{content:"";position:absolute;left:10px;right:10px;top:50%;height:9px;
 transform:translateY(-50%);border-radius:var(--radius-xs);background:var(--gray-200);
 animation:cellwait 1.6s ease-in-out infinite}
#grid td.arrived{animation:cellin .7s ease-out}
@keyframes cellwait{0%,100%{opacity:.45}50%{opacity:.9}}
@keyframes cellin{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){#grid td.pending::before{animation:none}#grid td.arrived{animation:none}}
.stp.regen-chip .mark{background:var(--ai-gradient);border-color:transparent}
.stp.regen-chip .nm{color:#7A5CFF;font-weight:700}

.regen{width:min(560px,calc(100vw - 32px));padding:0;border:1px solid var(--border);border-radius:var(--radius-xl);
 background:#fff;color:var(--gray-900);box-shadow:0 24px 64px rgba(0,0,0,.18)}
.regen::backdrop{background:rgba(17,17,17,.35)}
.regen-hdr{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid var(--border)}
.regen-hdr h3{margin:0;font-size:var(--fs-body);font-weight:700;flex:1;min-width:0}
.regen-x{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;padding:0;
 border:none;border-radius:var(--radius-xs);background:transparent;color:var(--gray-500);cursor:pointer}
.regen-x:hover{background:var(--gray-100);color:var(--gray-900)}
.regen-body{padding:14px 16px;max-height:min(60vh,520px);overflow:auto}
.regen-logic{margin:0 0 14px;border:1px solid var(--border);border-radius:var(--radius-l);background:var(--gray-50);padding:10px 12px}
.regen-logic .lg{display:grid;grid-template-columns:96px 1fr;gap:10px;padding:5px 0;font-size:var(--fs-body-s);line-height:1.65}
.regen-logic .lk{color:var(--gray-500);font-weight:700}
.regen-logic .lv{color:var(--gray-800)}
.regen-lbl{display:block;margin:0 0 6px;font-size:var(--fs-body-s);font-weight:700}
.regen textarea{width:100%;box-sizing:border-box;padding:9px 11px;border:1px solid var(--border);
 border-radius:var(--radius-l);font:inherit;font-size:var(--fs-body-s);line-height:1.6;resize:vertical}
.regen textarea:focus-visible{outline:2px solid var(--zag-blue);outline-offset:-1px;border-color:var(--zag-blue)}
.regen textarea:disabled{background:var(--gray-50);color:var(--gray-500)}
.regen-note{display:flex;gap:10px;align-items:baseline;margin:6px 0 12px}
.regen-note .note:first-child{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.regen-chk{display:flex;align-items:center;gap:8px;font-size:var(--fs-body-s)}
.regen-msg{margin:12px 0 0;padding:8px 11px;border-radius:var(--radius-l);background:var(--gray-50);
 color:var(--gray-800);font-size:var(--fs-body-s);line-height:1.6}
.regen-msg.bad{background:var(--danger-tint);color:var(--danger)}
.regen-btns{display:flex;gap:8px;padding:12px 16px;border-top:1px solid var(--border);background:var(--gray-50);
 border-radius:0 0 var(--radius-xl) var(--radius-xl);flex-wrap:wrap}
.regen-btns .btn:last-child{margin-left:auto}
@media (max-width:480px){.regen-logic .lg{grid-template-columns:1fr;gap:2px}.regen-btns .btn:last-child{margin-left:0}}
.regen-scope{margin:0 0 12px;padding:7px 11px;border-radius:var(--radius-l);background:var(--gray-50);
 color:var(--gray-600);font-size:var(--fs-body-s);line-height:1.6}

/* --- 分類と型番の確認: the question a job with no 基本情報 stops on, shown where the stepper
   would be. Pre-filled by the run and confirmed by the operator, so it reads as a form to check
   rather than an error to clear: informational header, one primary button, nothing red. --- */
.roster{margin:0 16px 16px;border:1px solid var(--border);border-radius:var(--radius-xl);background:#fff;overflow:hidden}
.roster .rqhdr{display:flex;align-items:center;gap:8px;padding:10px 16px;border-bottom:1px solid var(--border);
 background:var(--gray-50);color:var(--gray-900);font-weight:700;font-size:var(--fs-body-s)}
.roster .rqbody{padding:14px 16px}
.roster .rqsay{margin:0 0 14px;font-size:var(--fs-body-s);line-height:1.7;color:var(--gray-800);max-width:64em}
.roster .rqfield{margin:0 0 16px}
.roster .rqlbl{display:block;margin:0 0 6px;font-size:var(--fs-body-s);font-weight:700}
.roster .rqsug{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:0 0 8px}
.roster .rqsug .note{width:100%}
.roster input[type=search]{width:100%;max-width:34em;box-sizing:border-box;font-family:var(--font-sans);
 font-size:var(--fs-body-s);color:var(--gray-900);padding:9px 12px;border:1px solid var(--border-strong);
 border-radius:var(--radius-md);background:#fff}
.roster input[type=search]:focus{outline:none;border-color:var(--zag-blue);box-shadow:var(--shadow-focus-blue)}
.roster .rqhits{display:flex;flex-direction:column;gap:4px;margin:8px 0 0;max-height:240px;overflow:auto}
.rqcat{display:flex;gap:10px;align-items:baseline;width:100%;max-width:44em;text-align:left;padding:6px 10px;
 border:1px solid var(--border);border-radius:var(--radius-l);background:#fff;cursor:pointer;font:inherit;
 font-size:var(--fs-body-s);transition:background var(--dur-base) var(--ease-standard)}
.rqcat.sg{width:auto;max-width:none}
.rqcat:hover{background:var(--gray-50)}
.rqcat:focus-visible{outline:none;box-shadow:var(--shadow-focus-blue)}
.rqcat.on{border-color:var(--zag-blue);background:var(--zag-blue-tint);color:var(--zag-blue-ink);font-weight:700}
.rqcat .mono{font-family:var(--font-mono);font-size:var(--fs-mono);color:var(--gray-600)}
.rqcat.on .mono{color:var(--zag-blue-ink)}
.rqcat .p{color:var(--gray-800);overflow-wrap:anywhere}
.roster .rnone{padding:6px 2px;font-size:var(--fs-meta);color:var(--gray-500)}
.roster textarea{width:100%;max-width:34em;box-sizing:border-box;padding:9px 11px;border:1px solid var(--border);
 border-radius:var(--radius-l);font-family:var(--font-mono);font-size:var(--fs-mono);line-height:1.7;resize:vertical}
.roster textarea:focus-visible{outline:2px solid var(--zag-blue);outline-offset:-1px;border-color:var(--zag-blue)}
.roster .rqnote{display:flex;gap:10px;align-items:baseline;max-width:34em;margin:6px 0 0}
.roster .rqnote .note:first-child{flex:1;min-width:0}
.roster .rqbtns{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.roster .rqmsg{font-size:var(--fs-body-s);color:var(--gray-600)}
.roster .rqmsg.bad{color:var(--danger)}
@media (max-width:480px){.roster{margin:0 12px 12px}.rqcat{flex-direction:column;gap:2px}}

/* --- two lines under one product name: the dialog says which rows, and the endpoint refuses the
   answer until they differ. A textarea cannot mark text inside itself, so the warning names the
   name and the line numbers, which is what the operator needs to find them. --- */
.rqdupes{display:flex;gap:8px;align-items:flex-start;margin:8px 0 0;padding:8px 11px;max-width:34em;
 border-radius:var(--radius-l);background:var(--warning-tint);color:#B45309;font-size:var(--fs-body-s);line-height:1.6}
.rqdupes .dt{font-weight:700}
.rqdupes .dl{display:flex;gap:8px;align-items:baseline;margin:3px 0 0}
.rqdupes .dn{font-weight:700}
.rqdupes .note{color:#B45309}

/* --- brief item 29: how many candidates the pre-fill offered and where from, and past 100 lines a
   confirmation that names the count before the answer can be sent. --- */
.roster .rqcands{margin:0 0 6px}
.rqconfirm{display:flex;gap:8px;align-items:flex-start;margin:8px 0 0;padding:8px 11px;max-width:34em;
 box-sizing:border-box;border-radius:var(--radius-l);background:var(--warning-tint);color:#B45309;
 font-size:var(--fs-body-s);line-height:1.6;cursor:pointer}
.rqconfirm input{margin:4px 0 0;flex:none}

/* --- brief item 32: 基本情報ファイルで指定する -- a pre-filled template out, the filled file back in.
   A block of its own above the one-category picker, set off by a quiet 「または」 rule. --- */
.roster .rqfile{max-width:44em;padding:12px 14px;border:1px solid var(--border);border-radius:var(--radius-l);background:var(--gray-50)}
.roster .rqfilesay{margin:0 0 10px;line-height:1.6}
.roster .rqfilerow{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:0 0 8px}
.roster .rqfilerow .btn{text-decoration:none}
.roster .rqfilename{flex:1;min-width:12em;overflow-wrap:anywhere}
.roster .rqfile .rqmsg:empty{display:none}
.roster .rqor{display:flex;align-items:center;gap:10px;max-width:44em;margin:0 0 14px;
 font-size:var(--fs-meta);color:var(--gray-600)}
.roster .rqor::before,.roster .rqor::after{content:"";flex:1;height:1px;background:var(--border)}

/* --- brief item 35: what an enrichment did, on the result card. --- */
.enrich{margin:0 0 12px;padding:10px 12px;border:1px solid var(--border);border-radius:var(--radius-l);background:var(--gray-50);font-size:var(--fs-body-s)}
.enrich .enrhead{font-weight:700;margin:0 0 4px}
.enrich .enrcols{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 0}
.enrich .chip{padding:2px 8px;border:1px solid var(--border);border-radius:var(--radius-pill);background:#fff}
.enrich .enrblank{margin:8px 0 0}
.enrich .enrrow{display:flex;gap:10px;align-items:baseline;margin:2px 0 0}
.enrich .enrgens{display:flex;flex-wrap:wrap;gap:10px;align-items:baseline;margin:8px 0 0}

/* --- brief item 33: a batched job's stepper follows the unit it is on. The chip in front of the
   rail names it (「登録バッチ 3 / 5」, 準備, 統合と出力); it is a label, not a stage, so it has no
   mark and no connector. --- */
.stp.phase-chip::after{display:none}
.stp.phase-chip .mark{border-color:transparent;background:transparent}
.stp.phase-chip .nm{color:var(--gray-900);font-weight:700}
.stp.phase-chip{padding-right:16px;border-right:1px solid var(--border);margin-right:8px}

/* --- brief item 59: the dialog is a list to scroll -- one row per product, its たの分類 already
   chosen from its documents, other candidates one tap away. A row with nothing floats to the top in
   amber; a guess gets an amber edge. The rest of the dialog folds away under it. --- */
.roster .rqbar{display:flex;gap:10px;flex-wrap:wrap;align-items:center;justify-content:space-between;max-width:72em;margin:0 0 10px}
.roster .rqpills{display:flex;gap:6px;flex-wrap:wrap}
.roster .rqpill{display:inline-flex;align-items:center;padding:2px 10px;border:1px solid var(--border);border-radius:var(--radius-pill);
 font-size:var(--fs-meta);color:var(--gray-600)}
.roster .rqpill.ok{border-color:var(--success);color:var(--success)}
.roster .rqpill.warn{border-color:var(--warning);color:#B45309;background:var(--warning-tint)}
.roster .rqpanel{max-width:72em;margin:0 0 10px}
.roster .rqpanel > .note{margin:0 0 6px}
.roster .rqadd textarea{max-width:none;min-height:0}
.roster .rqadd .rqbtns{margin:6px 0 0}
.roster .rqrows{max-width:72em;max-height:62vh;overflow:auto;border:1px solid var(--border);border-radius:var(--radius-lg);margin:0 0 12px}
.roster .rqrow{display:grid;grid-template-columns:minmax(9em,13em) minmax(8em,1fr) minmax(18em,1.6fr);gap:12px;align-items:flex-start;
 padding:10px 12px;border-bottom:1px solid var(--border);font-size:var(--fs-body-s)}
.roster .rqrow:last-child{border-bottom:0}
.roster .rqrow.hd{position:sticky;top:0;z-index:1;padding:8px 12px;background:var(--gray-50);font-size:var(--fs-meta);color:var(--gray-600)}
.roster .rqrow.todo{background:var(--warning-tint)}
.roster .rqrow .k{font-family:var(--font-mono);font-size:var(--fs-mono);overflow-wrap:anywhere}
.roster .rqrow .nm{min-width:0;color:var(--gray-800);overflow-wrap:anywhere}
.roster .rqrow .nm .dim{color:var(--gray-500)}
.roster .rqrow .src{display:block;font-size:var(--fs-meta);color:var(--gray-500);overflow-wrap:anywhere}
.roster .rqrow .c{min-width:0}
.roster .rqsel{display:flex;gap:8px;align-items:baseline;width:100%;text-align:left;padding:7px 10px;border:1px solid transparent;
 border-radius:var(--radius-md);background:var(--zag-blue-tint);color:var(--gray-900);font:inherit;font-size:var(--fs-body-s);cursor:pointer}
.roster .rqsel .mono{font-family:var(--font-mono);font-size:var(--fs-mono);color:var(--zag-blue-ink)}
.roster .rqsel .p{flex:1;min-width:0;overflow-wrap:anywhere}
.roster .rqsel .tag{font-size:var(--fs-meta);color:var(--gray-600)}
.roster .rqsel.check{border-color:var(--warning)}
.roster .rqsel.empty{background:var(--warning-tint);border-color:var(--warning);color:#B45309;font-weight:700}
.roster .rqsel:focus-visible,.roster .rqalt:focus-visible{outline:none;box-shadow:var(--shadow-focus-blue)}
.roster .rqalts{display:flex;flex-wrap:wrap;gap:6px;align-items:baseline;margin:6px 0 0}
.roster .rqalt{padding:2px 9px;border:1px solid var(--border);border-radius:var(--radius-pill);background:#fff;font:inherit;
 font-size:var(--fs-meta);color:var(--gray-600);cursor:pointer}
.roster .rqalt .mono{font-family:var(--font-mono);color:var(--gray-900)}
.roster .rqalt:hover{background:var(--gray-50)}
.roster .rqwhy{margin:4px 0 0;font-size:var(--fs-meta);color:var(--gray-500)}
.roster .rqwhy.warn{color:#B45309}
.roster .rqlink{padding:0;border:0;background:none;font:inherit;font-size:var(--fs-meta);color:var(--zag-blue);cursor:pointer;text-decoration:underline}
.roster .rqlink:focus-visible{outline:none;box-shadow:var(--shadow-focus-blue)}
.roster .rqpicker{display:flex;flex-direction:column;gap:6px;margin:8px 0 0;padding:10px;border:1px solid var(--zag-blue);
 border-radius:var(--radius-md);background:#fff}
.roster .rqpicker.plain{margin:0;padding:0;border:0}
.roster .rqpicker .hd{font-size:var(--fs-meta);color:var(--gray-600)}
.roster .rqopts{display:flex;flex-direction:column;gap:4px}
.roster .rqpicker .rqhits{margin:4px 0 0;max-height:220px}
.roster .rqfoot{display:flex;gap:10px;flex-wrap:wrap;align-items:center;justify-content:space-between;max-width:72em;margin:12px 0 6px}
.roster .rqfoot > .note{flex:1;min-width:16em;line-height:1.6}
.roster .rqdefault{max-width:72em;margin:0 0 8px}
.roster .rqmore{max-width:72em;margin:10px 0 0;padding:10px 0 0;border-top:1px dashed var(--border);font-size:var(--fs-body-s)}
.roster .rqmore > summary{cursor:pointer;color:var(--gray-600)}
.roster .rqmore[open] > summary{margin:0 0 10px}
.roster .rqmore > .note{margin:0 0 8px}
/* --- brief item 61: a 登録する tick on every row (an unticked row stays, dimmed, and is not sent), the
   documents naming each product with 共有 on a file that names several, and the ＋ 商品を追加 form. --- */
.roster .rqpill.count{border-color:var(--zag-blue);color:var(--zag-blue-ink);background:var(--zag-blue-tint)}
.roster .rqinc-l{display:flex;gap:8px;align-items:flex-start;min-width:0;cursor:pointer}
.roster .rqinc{flex:none;margin:3px 0 0;width:16px;height:16px;accent-color:var(--zag-blue);cursor:pointer}
.roster .rqinc:focus-visible{outline:none;box-shadow:var(--shadow-focus-blue)}
.roster .rqrow.off{background:var(--gray-50)}
.roster .rqrow.off .k,.roster .rqrow.off .nm{color:var(--gray-500)}
.roster .rqrow .src .doc{display:block;margin:2px 0 0}
.roster .rqrow .src .tag{display:inline-block;margin:0 0 0 4px;padding:0 6px;border:1px solid var(--border);
 border-radius:var(--radius-pill);font-size:var(--fs-mono-micro);color:var(--gray-600);line-height:1.5;vertical-align:1px}
.roster .rqadd{padding:12px;border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--gray-50);box-sizing:border-box}
.roster .rqaddgrid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.6fr);gap:10px 12px;margin:0 0 10px}
.roster .rqaddf{display:block;min-width:0;margin:0 0 10px}
.roster .rqaddgrid .rqaddf{margin:0}
.roster .rqaddf .req{margin:0 0 0 4px;font-size:var(--fs-meta);font-weight:400;color:var(--danger)}
.roster .rqadd input[type=text]{width:100%;box-sizing:border-box;font-family:var(--font-sans);font-size:var(--fs-body-s);
 color:var(--gray-900);padding:8px 11px;border:1px solid var(--border-strong);border-radius:var(--radius-md);background:#fff}
.roster .rqadd input[type=text]:focus{outline:none;border-color:var(--zag-blue);box-shadow:var(--shadow-focus-blue)}
.roster .rqadd .rqsel{max-width:44em}
.roster .rqadd .rqmsg:empty{display:none}
.roster details.rqopts{display:block}
.roster details.rqopts pre{margin:6px 0 8px;white-space:pre-wrap;overflow-wrap:anywhere}
@media (max-width:600px){
  .roster .rqrow{grid-template-columns:1fr;gap:4px}
  .roster .rqrow.hd{display:none}
  .roster .rqaddgrid{grid-template-columns:1fr}
  .roster .rqbar .rqbtns{width:100%}
  .roster .rqbar .rqbtns .btn{flex:1 1 auto}
}
