    /* ── Review layout: fields/table (left) + provenance panel (right) ───────── */
    .review-panel { display: none; }
    .review-panel.active { display: block; }
    body.review-inspection-open { overflow: hidden; }
    body.review-inspection-open #review-panel { z-index: 10000; }
    .review-header {
      display: flex;
      align-items: center;
      gap: 12px;
      margin-bottom: 18px;
      flex-wrap: wrap;
    }
    .review-identity { min-width: min(260px, 100%); flex: 1 1 auto; }
    .review-identity h2 { margin: 0 0 2px; overflow-wrap: anywhere; }
    .review-identity .screen-intro { margin-bottom: 0; }
    .review-entry-id {
      display: block;
      font-size: 13px;
      color: var(--ink-3);
      font-variant-numeric: tabular-nums;
      overflow-wrap: anywhere;
    }
    #review-demo-guide { margin-top: 6px; }
    .section-title {
      font-size: 13px;
      font-weight: 650;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      color: var(--ink-3);
    }

    .review-summary {
      display: flex;
      align-items: center;
      gap: 12px;
      flex-wrap: wrap;
      padding: 11px 16px;
      border-radius: 12px;
      margin-bottom: 18px;
      font-size: 13px;
    }
    .review-summary.blocked { background: var(--red-tint); border: 1px solid var(--red-border); color: var(--red); }
    .review-summary.clear   { background: var(--green-tint); border: 1px solid var(--green-border); color: var(--green); }
    .review-summary.exception { background: var(--amber-tint); border: 1px solid var(--amber-border); color: var(--amber); }
    .review-summary .count { font-weight: 700; font-variant-numeric: tabular-nums; }

    .review-body { display: flex; gap: 22px; align-items: flex-start; }
    @media (min-width: 1081px) {
      /* Issue #471: full-bleed used to be bound to the COLLAPSED state, so
         opening the source panel cost width twice — the body fell from 1424px
         to the card's 1186px, and the fields column then took a share of the
         smaller box. Measured: 628px of scroller for a 1582px table, with 币制
         and HS 编码 off the right edge. No flex-basis reaches 766px from
         inside 1186px, which is why this is a two-line change and not a
         tuning exercise.
         Both states are full-bleed now; only the collapsed one becomes a grid,
         because that is what pins the 42px rail. */
      .review-body { 
        position: relative;
        left: 50%;
        width: calc(100vw - 16px);
        margin-left: calc(-50vw + 8px);
      }
      .review-body:has(> .review-side.collapsed) {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 42px;
        gap: 0;
      }
    }
    /* Issue #59 (customer): the fields column must never be squeezed so
       narrow that values truncate — keep a readable floor, and below that
       stack the source viewer UNDER the fields instead (media query). */
    /* Issue #463: .review-side asks for 46% — the minority share — but this
       column asked for a flex-basis of 0, so the free space split evenly and
       the source ended up with 46% + 27% = 73%, pinning the fields to their
       #59 floor. Matching bases makes the declared split the real one. The
       floor stays: it is #59's guarantee that values never truncate, not a
       width budget to spend. */
    /* Issue #471: 54% of the widened body is 757px and the five main columns
       plus the sign-off rail need 766px. 56% measures 769px — three pixels of
       headroom, which is why #524's "no column gets wider" sentinel is
       load-bearing rather than tidy. The source panel is not what pays: it
       goes from 536px to 633px in the same change, because full-bleed adds
       238px to the body and both columns grow. */
    .review-main { flex: 1 1 56%; min-width: min(430px, 100%); }
    .review-section {
      border-top: 1px solid var(--hairline);
    }
    .review-section:last-of-type { border-bottom: 1px solid var(--hairline); }
    .review-section-toggle {
      width: 100%;
      min-height: 52px;
      display: grid;
      grid-template-columns: minmax(0, 1fr) auto 16px;
      align-items: center;
      gap: 12px;
      border: 0;
      background: transparent;
      color: var(--ink);
      padding: 12px 2px;
      cursor: pointer;
      text-align: left;
      font: inherit;
    }
    .review-section-toggle:hover { background: var(--surface-2); }
    .review-section-toggle::after {
      content: "";
      width: 7px;
      height: 7px;
      justify-self: center;
      border-right: 1.5px solid var(--ink-3);
      border-bottom: 1.5px solid var(--ink-3);
      transform: rotate(45deg);
      transition: transform 140ms ease;
    }
    .review-section-toggle[aria-expanded="true"]::after { transform: rotate(225deg); }
    @media (prefers-reduced-motion: reduce) {
      .review-section-toggle::after { transition: none; }
    }
    .review-section-title {
      min-width: 0;
      font-size: 15px;
      font-weight: 680;
      overflow-wrap: anywhere;
    }
    .review-section-meta {
      min-width: 0;
      color: var(--ink-3);
      font-size: 13px;
      text-align: right;
      overflow-wrap: anywhere;
    }
    .review-section-meta.needs-attention { color: var(--red); font-weight: 650; }
    .review-section-meta.ready { color: var(--green); font-weight: 600; }
    .review-section-content { min-width: 0; padding: 0 0 18px; }
    .review-section-content[hidden] { display: none; }
    .review-section-note {
      margin: 0;
      color: var(--ink-3);
      font-size: 13px;
      line-height: 1.55;
    }
    .review-section-actions {
      display: flex;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap;
    }
    .customs-attached-documents { margin-bottom: 14px; }
    .customs-attached-documents .section-title { margin: 0 0 5px; }
    .review-lifecycle-host { list-style: none; margin: 0; padding: 0; }
    .review-lifecycle-host .lifecycle-panel { margin: 0; }
    #review-progress-open-btn { margin-top: 10px; }
    #review-progress-open-btn[hidden] { display: none; }
    .review-action-row {
      margin-top: 22px;
      display: flex;
      gap: 12px;
      align-items: center;
      flex-wrap: wrap;
    }
    .review-action-row[hidden] { display: none; }
    @media (max-width: 600px) {
      .app-header { flex-wrap: wrap; padding: 10px 14px; gap: 8px; }
      .app-header h1, .app-header .badge, .app-header .nav-link,
      .operator-menu-wrap { flex-shrink: 0; white-space: nowrap; }
      .app-header .badge { display: none; }
      .nav-link { padding-left: 8px; }
      .queue-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
      .queue-cell { border-bottom: 1px solid var(--hairline-soft); }
      .queue-cell + .queue-cell { border-left: 0; }
      .queue-cell:nth-child(even) { border-left: 1px solid var(--hairline-soft); }
      .queue-cell:last-child { grid-column: 1 / -1; border-bottom: 0; }
      .queue-cell .lbl { overflow-wrap: normal; }
      .stat-tile { padding: 14px 16px; }
      .demo-ticket-tools .btn { width: 100%; }
      .line-items-region:not(.inspection-mode) .line-items-toolbar { top: 95px; }
      .review-section-toggle {
        grid-template-columns: minmax(0, 1fr) 16px;
        row-gap: 3px;
      }
      .review-section-meta { grid-column: 1; grid-row: 2; text-align: left; }
      .review-section-toggle::after { grid-column: 2; grid-row: 1 / span 2; }
      .review-section-actions .btn { width: 100%; }
    }
    @media (max-width: 1080px) {
      .review-body { flex-direction: column; }
      /* Issue #92: stacked, .review-main must fill the card and be allowed to
         shrink. Without this its flex:1 1 0 (which only sizes the main axis —
         height — in a column) leaves the width at max-content (~1293px), so
         the fields grid bled past the card and the whole page scrolled
         sideways at every width below 1080px. */
      .review-main { width: 100%; min-width: 0; }
      .review-side {
        position: static !important;
        width: 100%;
        min-width: 0 !important;
        max-height: 60vh !important;
      }
    }
    /* V1 layout (issue #44): extracted fields left, the FULL source document
       right, so the reviewer eyeballs the source to catch a silently dropped
       line item that has no field to flag (enemy #1). */
    .review-side {
      flex: 1 1 46%;
      min-width: 360px;
      position: sticky;
      top: 76px;
      background: var(--surface-2);
      border: 1px solid var(--hairline-soft);
      border-radius: 12px;
      padding: 14px 16px;
      font-size: 13px;
      max-height: calc(100vh - 96px);
      /* Issue #448: without this, a long provenance caption (flex-shrink: 0,
         so it cannot shrink away) overflows the sticky panel and its tail —
         the "what now" half of the note — is clipped with no way to scroll
         to it. The collapsed state's overflow: hidden (below) has higher
         specificity and still wins when folded. */
      overflow-y: auto;
      display: flex;
      flex-direction: column;
      transition: flex-basis 180ms ease, min-width 180ms ease, padding 180ms ease;
    }
    @media (prefers-reduced-motion: reduce) {
      .review-side { transition: none; }
    }
    /* Collapsed (the DEFAULT, issue #91): the panel folds into a slim rail so
       the fields table gets the full width — the reviewer opens the
       side-by-side comparison deliberately with one click instead of paying
       half the viewport for it on every entry. */
    .review-side.collapsed {
      flex: 0 0 42px;
      min-width: 42px;
      padding: 12px 0;
      cursor: pointer;
      align-items: center;
      overflow: hidden;
    }
    .review-side.collapsed:hover { border-color: var(--hairline); }
    .review-side.collapsed > :not(.side-rail) { display: none; }
    .side-rail {
      display: none;
      writing-mode: vertical-rl;
      font-size: 13px;
      font-weight: 650;
      letter-spacing: 0.14em;
      color: var(--ink-3);
      user-select: none;
      margin-top: 2px;
      white-space: nowrap;
      border: 0;
      background: transparent;
      cursor: pointer;
      font-family: inherit;
    }
    .review-side.collapsed .side-rail { display: block; }
    @media (max-width: 1080px) {
      /* Stacked layout: the collapsed rail reads horizontally instead. */
      .review-side.collapsed { width: 100%; flex-basis: auto; padding: 9px 14px; align-items: flex-start; }
      .review-side.collapsed .side-rail { writing-mode: horizontal-tb; margin-top: 0; }
    }
    .review-side h4 {
      font-size: 13px;
      font-weight: 650;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      margin-bottom: 10px;
      color: var(--ink-3);
      display: flex;
      align-items: baseline;
      gap: 8px;
      flex-wrap: wrap;
    }
    .review-side h4 .doc-name {
      text-transform: none;
      letter-spacing: 0;
      font-weight: 600;
      color: var(--ink-2);
      font-size: 13px;
      word-break: break-all;
    }
    #review-source-content {
      min-width: 0;
      min-height: 0;
      flex: 1 1 auto;
      display: flex;
      flex-direction: column;
    }
    #review-source-content[hidden] { display: none; }
    /* Sheet switcher (Excel workbooks with >1 visible sheet). */
    .source-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
    .source-tabs button {
      font: inherit; font-size: 13px;
      padding: 3px 10px; border-radius: 999px;
      border: 1px solid var(--hairline);
      background: var(--surface); color: var(--ink-2);
      cursor: pointer;
    }
    .source-tabs button.active { background: var(--accent); border-color: var(--accent); color: #fff; }
    /* Scrollable viewport that holds the rendered source (table / pdf / image). */
    .source-viewport {
      flex: 1 1 auto;
      overflow: auto;
      background: var(--surface);
      border: 1px solid var(--hairline-soft);
      border-radius: 8px;
      min-height: 240px;
    }
    .source-viewport iframe, .source-viewport img { width: 100%; border: 0; display: block; }
    .source-viewport iframe { height: 60vh; }
    /* Excel rendered as an HTML table — a faithful grid of the source cells. */
    .source-table { border-collapse: collapse; font-size: 13px; width: max-content; min-width: 100%; }
    .source-table td {
      border: 1px solid var(--hairline-soft);
      padding: 3px 6px;
      white-space: nowrap;
      max-width: 260px;
      overflow: hidden;
      text-overflow: ellipsis;
      color: var(--ink-2);
      vertical-align: top;
    }
    .source-table td:empty::after { content: "\00a0"; }
    /* The cell that a clicked field traces back to (provenance highlight). */
    .source-table td.cell-hit {
      background: var(--accent);
      color: #fff;
      font-weight: 700;
      box-shadow: inset 0 0 0 2px var(--accent);
    }
    /* PDF text-layer view (issue #44): the locate-capable rendering of a
       native PDF's extracted text — char-offset provenance highlights here,
       since the raw <iframe> preview cannot be scripted into. */
    .pdf-page-label {
      font-size: 13px;
      font-weight: 700;
      color: var(--ink-3);
      padding: 8px 10px 2px;
    }
    .pdf-page-text {
      margin: 0;
      padding: 6px 10px 12px;
      font: 11.5px/1.65 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
      white-space: pre-wrap;
      word-break: break-word;
      color: var(--ink-2);
    }
    .pdf-page-text .pdf-hit {
      background: var(--accent);
      color: #fff;
      font-weight: 700;
      border-radius: 3px;
      padding: 0 2px;
    }
    .prov-caption {
      margin-top: 10px;
      font-size: 13px;
      color: var(--ink-3);
      line-height: 1.55;
      flex-shrink: 0;
    }
    .prov-caption .prov-status { margin-top: 6px; }
    .prov-row { display: inline-flex; gap: 5px; margin-right: 12px; }
    .prov-row .k { color: var(--ink-3); }
    .prov-row .v { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
    .prov-empty { color: var(--ink-3); line-height: 1.65; }
    .prov-raw {
      margin-top: 6px;
      background: var(--surface-2);
      border: 1px solid var(--hairline-soft);
      border-radius: 8px;
      padding: 7px 9px;
      font-family: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
      font-size: 13px;
      white-space: pre-wrap;
      word-break: break-all;
    }
    .prov-status { margin-top: 6px; padding: 6px 9px; border-radius: 8px; line-height: 1.5; }
    .prov-status.bad  { background: var(--red-tint); color: var(--red); }
    .prov-status.good { background: var(--green-tint); color: var(--green); }
    .prov-status.warn { background: var(--amber-tint); color: var(--amber); }

    /* Completeness alarm banner (issue #44): the 漏行 cross-check made LOUD.
       Top-of-review red bar the reviewer cannot miss — enemy #1 (silent
       line-item loss) turned audible. Layer 2 (no anchor, SOFT) warns without
       gating; Layer 1 (line-count mismatch, issue #43) BLOCKS Confirm and
       gets the heavier .blocking treatment plus the deliberate ADR-0030
       override entry point. */
    .completeness-banner {
      display: flex;
      gap: 10px;
      align-items: flex-start;
      padding: 12px 16px;
      border-radius: 12px;
      margin-bottom: 18px;
      background: var(--red-tint);
      border: 1px solid var(--red);
      color: var(--red);
      font-size: 13px;
      line-height: 1.5;
    }
    .completeness-banner .cb-icon { flex-shrink: 0; font-size: 15px; line-height: 1.3; }
    .completeness-banner strong { font-weight: 700; }
    .completeness-banner.blocking {
      background: var(--red);
      border-color: var(--red);
      color: #fff;
      font-size: 14px;
      box-shadow: 0 6px 18px color-mix(in srgb, var(--red) 35%, transparent);
    }
    .completeness-banner.blocking .cb-icon { font-size: 18px; }
    .completeness-banner .cb-actions { margin-top: 8px; }
    .completeness-banner.blocking .btn {
      background: #fff;
      color: var(--red);
      border: none;
      font-weight: 600;
    }
    .completeness-banner.overridden {
      background: var(--amber-tint);
      border-color: var(--amber);
      color: var(--amber);
    }

    /* Missing-document hint (issue #401): #400's guidance leaves the red
       completeness banner and becomes a dedicated reminder ABOVE the review
       summary — a to-do nudge ("we know you're missing the invoice"), not a
       risk or blocker, so U0-1.3 pins it to the --amber family. Dismissal is
       memory-only (a refresh restores it); Confirm is never affected. */
    .missing-doc-hint {
      display: flex;
      gap: 10px;
      align-items: flex-start;
      padding: 12px 16px;
      border-radius: 12px;
      margin-bottom: 18px;
      background: var(--amber-tint);
      border: 1px solid var(--amber-border);
      color: var(--amber);
      font-size: 13px;
      line-height: 1.5;
    }
    /* issue #639: same amber notice family as .missing-doc-hint — U0-6.2 forbids
       a second notice language, and --amber-bg does not exist as a token. */
    .upload-duplicate-warning {
      background: var(--amber-tint);
      border: 1px solid var(--amber-border);
      color: var(--amber);
      border-radius: 8px;
      padding: 8px 10px;
      margin-bottom: 6px;
      font-size: 13px;
      line-height: 1.5;
    }
    /* 13px is the floor for customer-facing copy (#230). This line is copy,
       not chrome: it is the operator's answer to 「依据哪一版」. */
    .codetable-basis {
      font-size: 13px;
      color: var(--ink-3);
      padding: 2px 2px 6px;
      line-height: 1.5;
    }
    .missing-doc-hint .mdh-icon { flex-shrink: 0; font-size: 15px; line-height: 1.3; }
    .missing-doc-hint .mdh-body { flex: 1; }
    .missing-doc-hint .mdh-dismiss { flex-shrink: 0; }

    /* Declaration Defaults (issue #105): a compact review-time status band,
       not editable Canonical fields. Defaults stay outside Working Truth so
       merely viewing them can never create a Correction Pair. */
    .declaration-defaults-status {
      margin: 0 0 18px;
      padding: 12px 0;
      border-top: 1px solid var(--hairline);
      border-bottom: 1px solid var(--hairline);
    }
    .fields-grid > .declaration-defaults-status {
      grid-column: 1 / -1;
      width: 100%;
    }
    .defaults-status-head {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
      margin-bottom: 8px;
      flex-wrap: wrap;
    }
    .defaults-status-head strong { font-size: 13px; color: var(--ink); }
    .defaults-status-head span {
      min-width: 0;
      font-size: 13px;
      color: var(--ink-3);
      overflow-wrap: anywhere;
    }
    .defaults-status-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
      gap: 0 18px;
    }
    .default-status-row {
      min-width: 0;
      padding: 7px 0;
      border-bottom: 1px solid var(--hairline-soft);
    }
    .default-status-row .default-label {
      display: block;
      margin-bottom: 3px;
      font-size: 13px;
      color: var(--ink-3);
    }
    .default-status-row .default-value {
      display: block;
      overflow-wrap: anywhere;
      font-size: 13px;
      font-weight: 600;
      color: var(--ink);
    }
    .default-status-row .default-origin {
      display: block;
      margin-top: 2px;
      font-size: 13px;
      color: var(--green);
    }
    /* Issue #527: none of these four states blocks anything. needsConfiguration
       只用来换一句提示语 (renderDeclarationDefaultsStatus)，不禁用 Confirm，也没有
       对应的后端校验 — 一张七项全「未配置」的单，摘要条照样是绿的「可以提交」，而
       Confirm 确实成功。所以它们是 U0-1.3 说的「待办」，归 --amber，不归 --red；
       红只留给真拦得住人的东西，否则红色本身贬值 (CLAUDE.md §三 推论)。
       仍然显眼：font-weight 600 + 「未配置」字样 + 抬头那条「联系管理员配置」链接，
       三重载体，满足 U0-1.6「颜色不能单独承载含义」与本单「不得让『没配置就导不出
       正确报文』变得不可见」的红线。 */
    .default-status-row[data-default-state="missing"] .default-value,
    .default-status-row[data-default-state="error"] .default-value,
    .default-status-row[data-default-state="unknown"] .default-value,
    .default-status-row[data-default-state="conflict"] .default-value,
    .default-status-row[data-default-state="missing"] .default-origin,
    .default-status-row[data-default-state="error"] .default-origin,
    .default-status-row[data-default-state="unknown"] .default-origin,
    .default-status-row[data-default-state="conflict"] .default-origin {
      color: var(--amber);
    }
    .default-status-row a { color: var(--accent-ink); font-weight: 600; }
    @media (max-width: 760px) {
      .defaults-status-grid { grid-template-columns: 1fr 1fr; }
    }
    @media (max-width: 520px) {
      .defaults-status-grid { grid-template-columns: 1fr; }
    }

    .fields-grid {
      display: grid;
      /* Issue #59: narrower label column — the VALUE is what the reviewer
         needs to read in full; long labels wrap to two lines instead. */
      grid-template-columns: minmax(120px, 150px) 1fr 58px;
      gap: 7px 12px;
      align-items: start;
      margin-bottom: 18px;
    }
    /* issue #82: 老沈 asked for a larger, more prominent review font — labels
       and values bumped a notch, flagged (标红) fields emphasized. */
    .fields-grid .label { font-size: 13px; color: var(--ink-2); padding-top: 8px; line-height: 1.35; }
    .crit-star { color: var(--red); margin-left: 2px; }
    /* Issue #555 / ADR-0054: the attention tier's TEXT carrier. Visible, not an
       aria-label — U0-1.6 accepts 「文字或可访问名称」 but this tier exists so the
       operator can scan without clicking. It sits in the label cell beside ★
       because .field-row is display:contents and a fourth child would claim a
       grid column of its own. */
    .attention-note {
      margin-left: 6px;
      /* Issue #230 floors customer copy at 13px (U0-2 易读性). The first version
         of this marker used 11px and turned that gate red — a reminder the
         Operator has to squint at is not a reminder. */
      font-size: 13px;
      font-weight: 600;
      color: var(--amber);
      white-space: nowrap;
    }
    .fields-grid input[type=text] {
      width: 100%;
      padding: 7px 10px;
      border: 1px solid var(--hairline);
      border-radius: 8px;
      font-size: 14.5px;
      font-family: inherit;
      background: var(--surface);
      color: var(--ink);
      transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
    }
    .fields-grid input[type=text]:focus {
      outline: none;
      border-color: var(--accent);
      box-shadow: 0 0 0 3.5px rgba(0, 113, 227, 0.18);
    }
    /* Issue #555 / ADR-0054: three in-place tiers, and the third one is NOT a
       copy of these rules. Each tier only declares which existing tokens it
       wears; one rule set below reads them. --tier-* may resolve to nothing but
       an existing token — enforced by scripts/check_frontend_contract.py, not
       by hoping. (U0-6.2 forbids local variables used to smuggle in colours;
       choosing between two authorised tokens smuggles nothing, and it removes
       the 复制组件样式 U0-6.2 forbids in the same breath.) */
    .fields-grid .field-flagged {
      --tier-ink: var(--red);
      --tier-tint: var(--red-tint);
      --tier-border: var(--red);
    }
    .fields-grid .field-attention {
      --tier-ink: var(--amber);
      --tier-tint: var(--amber-tint);
      --tier-border: var(--amber-border);
    }
    .field-flagged .label,
    .field-attention .label { color: var(--tier-ink); font-weight: 600; }
    .field-flagged input[type=text],
    .field-attention input[type=text] {
      background: var(--tier-tint);
      border-color: var(--tier-border);
      font-weight: 600;
    }
    /* issue #81: an empty field the source never provided reads as a clearly
       fillable slot (dashed outline + inviting placeholder), so 老沈 sees at a
       glance which fields still need manual filling and can click straight in.
       Neutral, not an alarm — red is reserved for flagged fields above. A
       flagged empty field keeps the red treatment (this only styles non-
       flagged empties). */
    .fields-grid input[type=text].is-empty:not(:focus) {
      border-style: dashed;
      border-color: var(--accent);
      background: rgba(0, 113, 227, 0.04);
    }
    /* Must stay AFTER the fillable-slot rule above: both are (0,4,1), so source
       order is what decides. Get it wrong and a flagged EMPTY field renders as
       an ordinary blue slot — less visible than an unflagged one, which is the
       case U0-1.6's last sentence forbids and #555's computed-colour assertion
       measures. */
    .field-flagged input[type=text].is-empty:not(:focus),
    .field-attention input[type=text].is-empty:not(:focus) {
      border-color: var(--tier-border);
      background: var(--tier-tint);
    }
    .fields-grid input[type=text]::placeholder { color: var(--accent); opacity: 0.7; }
    .field-flagged input[type=text]::placeholder,
    .field-attention input[type=text]::placeholder { color: var(--tier-ink); opacity: 0.7; }
    /* The attention tier deliberately has no extra focus ring: the base focus
       style already satisfies U0-1.6's 「焦点必须清楚可见」, and an amber ring
       would need a new rgba literal, which U0-1.4 forbids. The red ring below
       is an existing declaration and stays a blocking-only emphasis. */
    .field-flagged input[type=text]:focus { box-shadow: 0 0 0 3.5px rgba(215, 0, 21, 0.15); }
    .fields-grid .field-row { display: contents; }
    .port-search-editor { position: relative; min-width: 0; }
    .port-search-options {
      position: absolute;
      z-index: 40;
      top: calc(100% + 4px);
      left: 0;
      right: 0;
      max-height: 240px;
      overflow-y: auto;
      border: 1px solid var(--hairline);
      border-radius: 6px;
      background: var(--surface);
      box-shadow: 0 10px 24px rgba(15, 23, 42, 0.16);
    }
    .port-search-options[hidden] { display: none; }
    .port-search-option,
    .port-search-status {
      display: block;
      width: 100%;
      padding: 8px 10px;
      border: 0;
      border-bottom: 1px solid var(--hairline-soft);
      border-radius: 0;
      background: var(--surface);
      color: var(--ink);
      text-align: left;
      font: 13px/1.4 inherit;
    }
    .port-search-option:last-child,
    .port-search-status:last-child { border-bottom: 0; }
    .port-search-option { cursor: pointer; }
    .port-search-option:hover,
    .port-search-option.is-active { background: var(--accent-tint); }
    .port-search-option strong { margin-right: 6px; color: var(--accent-ink); }
    .port-search-status { color: var(--ink-3); }
    /* Very narrow (rare for this desktop tool): trim the label column so the
       value input keeps a usable width instead of getting squeezed (issue
       #92; #59's readable-value principle held small). Placed AFTER the base
       .fields-grid rule so it wins the cascade (same specificity). */
    @media (max-width: 560px) {
      .fields-grid { grid-template-columns: minmax(76px, 100px) 1fr 54px; column-gap: 8px; }
    }

    .signoff-dot {
      width: 23px; height: 23px;
      border-radius: 50%;
      border: 1.5px solid var(--hairline);
      background: var(--surface);
      cursor: pointer;
      font-size: 13px;
      line-height: 20px;
      text-align: center;
      color: var(--ink-3);
      flex-shrink: 0;
      transition: transform 0.15s, background 0.15s, border-color 0.15s;
    }
    .signoff-dot:not(:disabled):hover { transform: scale(1.12); }
    .signoff-dot.needs-signoff { border-color: var(--red); color: var(--red); background: var(--red-tint); }
    .signoff-dot.signed { border-color: var(--green); background: var(--green); color: #fff; }
    .signoff-dot.rule-verified { border-color: var(--green-border); color: var(--green); background: var(--green-tint); }
    .signoff-dot:disabled { cursor: default; opacity: 0.85; transform: none; }
    .signoff-dot.rule-conflict {
      width: 54px;
      border-radius: 5px;
      border-color: var(--red);
      background: var(--red);
      color: #fff;
      font-size: 13px;
      font-weight: 700;
      line-height: 20px;
    }
    .signoff-dot.rule-overridden {
      width: 54px;
      border-radius: 5px;
      border-color: var(--amber);
      background: var(--amber-tint);
      color: var(--amber);
      font-size: 13px;
      font-weight: 700;
      line-height: 20px;
    }
    .rule-conflict-inline {
      display: inline-flex;
      align-items: center;
      margin-top: 5px;
      padding: 2px 6px;
      border: 1px solid var(--red);
      border-radius: 4px;
      background: var(--red);
      color: #fff;
      cursor: pointer;
      font: 700 11px/1.35 inherit;
    }
    .rule-conflict-inline:hover { background: #b80012; }
    .rule-conflict-inline.rule-overridden {
      border-color: var(--amber);
      background: var(--amber-tint);
      color: var(--amber);
    }
    .rule-conflict-inline.rule-overridden:hover { background: #ffefc8; }
    .field-overridden input[type=text]:not(:focus),
    .line-items-table td.cell-overridden {
      border-color: var(--amber-border);
      background: var(--amber-tint);
      color: var(--amber);
    }
    .line-items-table td.cell-overridden { box-shadow: inset 0 0 0 1px var(--amber-border); }

    .dynamic-modal-overlay {
      position: fixed;
      inset: 0;
      z-index: 10000;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 20px;
      background: rgba(0, 0, 0, 0.55);
    }
    .override-modal {
      width: min(540px, 100%);
      border-radius: 8px;
      padding: 24px;
      background: var(--surface);
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    }
    .override-modal h2 { margin-bottom: 12px; font-size: 17px; }
    /* Issue #523: the catalogue lookup and the classification assistant used to
       render inside the 72px HS column. Nothing here is new design — same
       surface, radius, shadow and tokens as .override-modal above; only the
       width differs, because the whole point is that a question, an answer box
       and a candidate list need somewhere to be read. */
    .lookup-modal {
      width: min(680px, 100%);
      max-height: min(78vh, 720px);
      display: flex;
      flex-direction: column;
      border-radius: 8px;
      padding: 24px;
      background: var(--surface);
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    }
    .lookup-modal h2 { margin-bottom: 4px; font-size: 17px; }
    .lookup-modal-sub { margin-bottom: 14px; font-size: 13px; color: var(--ink-3); }
    .lookup-modal-body { flex: 1 1 auto; overflow-y: auto; font-size: 13px; }
    /* The panel keeps writing into the same node it always did, so every child
       style it relies on still applies — it is only the container that moved. */
    .lookup-modal-body .sub { color: var(--ink-3); }
    .lookup-modal-body .assist-answer-row input { width: 100%; }
    .lookup-modal-actions { display: flex; justify-content: flex-end; margin-top: 16px; }
    .override-conflict-message {
      margin-bottom: 16px;
      padding: 12px;
      border: 1px solid var(--red-border);
      border-radius: 6px;
      background: var(--red-tint);
      color: var(--red);
      line-height: 1.65;
    }
    .override-ack {
      display: flex;
      gap: 9px;
      align-items: flex-start;
      margin-bottom: 18px;
      cursor: pointer;
      font-size: 13px;
      line-height: 1.6;
    }
    .override-ack input { margin-top: 3px; flex: 0 0 auto; }
    .override-actions { display: flex; justify-content: flex-end; gap: 10px; }

    /* 成品预览 modal (#206) — the export Workbook rendered as an HTML grid. */
    .declaration-preview-modal {
      width: min(1150px, 96vw);
      max-height: 92vh;
      display: flex;
      flex-direction: column;
      border-radius: 8px;
      padding: 22px;
      background: var(--surface);
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    }
    .declaration-preview-modal h2 { margin-bottom: 4px; font-size: 17px; }
    .declaration-preview-scroll {
      flex: 1;
      overflow: auto;
      margin: 10px 0 16px;
      border: 1px solid var(--line);
      border-radius: 6px;
      background: #fff;
      padding: 8px;
    }
    .declaration-preview-grid td {
      border: 1px solid #d8d8dc;
      font-size: 13px;
      line-height: 1.5;
      padding: 2px 5px;
      color: #1d1d1f;
      vertical-align: top;
      word-break: break-all;
    }

    /* Fee ledger modal (T10) — 已确认工单详情的费用记录区. */
    .fee-modal {
      width: min(640px, 100%);
      max-height: min(80vh, 720px);
      overflow-y: auto;
      border-radius: 8px;
      padding: 24px;
      background: var(--surface);
      box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
    }
    .fee-modal h2 { margin-bottom: 4px; font-size: 17px; }
    .fee-modal .fee-modal-sub { margin-bottom: 16px; font-size: 13px; color: var(--ink-3); }
    .fee-table { width: 100%; border-collapse: collapse; margin-bottom: 16px; font-size: 13px; }
    .fee-table th {
      background: var(--surface-2);
      text-align: left;
      padding: 7px 8px;
      border-bottom: 1px solid var(--hairline);
      font-weight: 600;
      font-size: 13px;
      color: var(--ink-2);
      white-space: nowrap;
    }
    .fee-table td { padding: 6px 8px; border-bottom: 1px solid var(--hairline-soft); vertical-align: middle; }
    .fee-table td.fee-amount-cell { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
    .fee-table td input {
      width: 100%;
      border: 1px solid transparent;
      background: transparent;
      font-size: 13px;
      font-family: inherit;
      color: var(--ink);
      padding: 3px 4px;
      border-radius: 4px;
    }
    .fee-table td input:focus { outline: none; border-color: var(--hairline); box-shadow: 0 0 0 2.5px rgba(0, 113, 227, 0.22); }
    .fee-table .fee-empty-row td { color: var(--ink-3); padding: 14px 8px; text-align: center; }
    /* Issue #671: `1.4fr` means `minmax(auto, 1.4fr)`, so each track's floor
       is its content's min-content width. Four text inputs at their default
       intrinsic width already exceed the modal's ~592px content box, and the
       `auto` track — the 「添加费用」 button — was pushed past the right edge
       entirely. minmax(0, …) lets the inputs shrink; the button keeps its own
       width because `auto` sizes to content.
       Not fixed by widening the modal (that would re-lay the ledger table) or
       by dropping 备注 (a product field). */
    .fee-add-row { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.9fr) minmax(0, 0.6fr) minmax(0, 1.4fr) auto; gap: 8px; align-items: center; margin-bottom: 14px; }
    .fee-add-row input { min-width: 0; }
    .fee-add-row input { padding: 7px 9px; font-size: 13px; border: 1px solid var(--hairline); border-radius: 6px; background: var(--surface); color: var(--ink); }
    .fee-modal-total { font-size: 13px; font-weight: 600; margin-bottom: 14px; text-align: right; }
    .fee-modal-actions { display: flex; justify-content: flex-end; }
    .fee-modal-msg { font-size: 13px; margin-bottom: 10px; display: none; }
    .fee-modal-msg.error { display: block; color: var(--red); }
    @media (max-width: 600px) {
      .fee-add-row { grid-template-columns: 1fr; }
      .fee-add-row .btn { width: 100%; }
      .fee-table { display: block; overflow-x: auto; }
    }

    .line-items-toolbar {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 10px;
      margin: 18px 0 10px;
    }
    .line-items-toolbar .section-title { margin: 0; }
    #review-section-items-content .line-items-toolbar {
      justify-content: flex-end;
      margin-top: 0;
    }
    .line-items-region:not(.inspection-mode) .line-items-toolbar {
      position: sticky;
      top: 64px;
      z-index: 2;
      background: var(--surface);
    }
    #review-section-items-content .line-items-toolbar .section-title { display: none; }
    .line-items-expand-btn {
      width: 30px;
      height: 30px;
      padding: 0;
      font-size: 17px;
      line-height: 1;
    }
    .line-items-anchor,
    .line-items-region { min-width: 0; }
    .line-items-region.inspection-mode {
      position: fixed;
      inset: 0;
      z-index: 10000;
      display: flex;
      flex-direction: column;
      padding: 12px;
      overflow: auto;
      background: var(--surface);
    }
    .line-items-region.inspection-mode .line-items-toolbar {
      flex: 0 0 auto;
      justify-content: space-between;
      margin: 0 0 10px;
    }
    #review-section-items-content .line-items-region.inspection-mode .section-title {
      display: block;
    }
    .line-items-region.inspection-mode .line-items-scroll {
      flex: 1 1 auto;
      min-height: 0;
      max-width: none;
    }
    .line-items-scroll { max-width: 100%; overflow-x: auto; overscroll-behavior-inline: contain; }
    /* Issue #463: the table stays wider than its window even after the column
       split is corrected, so it keeps scrolling — but a header cut
       mid-character with no scrollbar and no edge treatment reads as broken,
       not as scrollable. Say the state out loud (ADR-0027 in interface form).
       Driven by data-overflowing so the cue can never disagree with the
       measurement it describes. */
    .line-items-scroll[data-overflowing="true"] {
      position: relative;
      scrollbar-color: var(--hairline) transparent;
    }
    .line-items-scroll[data-overflowing="true"]::-webkit-scrollbar {
      height: 10px;
    }
    .line-items-scroll[data-overflowing="true"]::-webkit-scrollbar-thumb {
      background: var(--hairline);
      border-radius: 5px;
    }
    /* U0-2.1: 13px floor — this line carries a next step ("slide sideways"),
       so it is not the auxiliary metadata that may drop to 12px.
       It lives in the sticky toolbar and toggles `visibility`, never
       `display`: #214 pins the page against jumping when the inspection
       overlay opens, and a hint that adds or removes a block would move the
       document under the reader. Height comes from the 30px button beside it,
       so showing the hint costs no layout. */
    .line-items-overflow-hint {
      visibility: hidden;
      margin-right: auto;
      font-size: 13px;
      color: var(--ink-3);
      white-space: nowrap;
      min-width: 0;
    }
    .line-items-region[data-overflowing="true"] .line-items-overflow-hint {
      visibility: visible;
    }
    .line-items-table { width: 100%; border-collapse: collapse; margin-bottom: 18px; font-size: 13px; }
    #line-items-table { table-layout: fixed; min-width: 1582px; font-size: 13px; }
    .line-items-table th {
      background: var(--surface-2);
      text-align: left;
      padding: 8px 9px;
      border-bottom: 1px solid var(--hairline);
      border-top: 1px solid var(--hairline-soft);
      font-weight: 600;
      font-size: 13px;
      color: var(--ink-2);
      white-space: nowrap;
      letter-spacing: 0.01em;
    }
    #line-items-table th {
      min-width: 0;
      padding: 6px 3px;
      font-size: 13px;
      line-height: 1.25;
      letter-spacing: 0;
      white-space: normal;
      overflow-wrap: anywhere;
      word-break: break-word;
    }
    /* Issue #524: only where renderLineItemHeaderLabel supplies a <wbr>.
       keep-all with no break opportunity overflows the column, so this pair
       must stay in step with that function. */
    #line-items-table th[data-column="legal_quantity_1"],
    #line-items-table th[data-column="legal_quantity_2"] {
      word-break: keep-all;
      overflow-wrap: normal;
    }
    .line-items-table td {
      padding: 6px 9px;
      border-bottom: 1px solid var(--hairline-soft);
      vertical-align: middle;
    }
    #line-items-table td { min-width: 0; padding: 4px 3px; }
    .line-items-table td input,
    .line-items-table td textarea {
      width: 100%;
      border: none;
      background: transparent;
      font-size: 13px;
      font-family: inherit;
      color: var(--ink);
      padding: 3px 2px;
      border-radius: 4px;
    }
    #line-items-table td input,
    #line-items-table td textarea { min-width: 0; font-size: 13px; padding: 3px 1px; }
    .line-items-table td input:focus,
    .line-items-table td textarea:focus { outline: none; box-shadow: 0 0 0 2.5px rgba(0, 113, 227, 0.22); }
    .line-items-table td.long-text-cell { min-width: 0; vertical-align: top; }
    #line-items-table th.line-item-index-cell,
    #line-items-table td.line-item-index-cell { min-width: 0; text-align: center; }
    #line-items-table th.line-item-numeric-cell,
    #line-items-table td.line-item-numeric-cell {
      min-width: 0;
      text-align: right;
      font-variant-numeric: tabular-nums;
    }
    #line-items-table td.line-item-numeric-cell { padding-inline: 0; }
    #line-items-table td.line-item-numeric-cell textarea {
      min-width: 0;
      padding: 0;
      text-align: right;
      white-space: pre-wrap;
      font-size: 13px;
      font-stretch: condensed;
      font-variant-numeric: tabular-nums;
    }
    #line-items-table th.line-item-short-cell,
    #line-items-table td.line-item-short-cell { min-width: 0; text-align: center; }
    #line-items-table td.line-item-short-cell { padding-inline: 0; }
    #line-items-table td.line-item-short-cell textarea {
      min-width: 0; padding: 0; text-align: center; white-space: pre-wrap; font-size: 13px;
      font-stretch: condensed;
    }
    #line-items-table th.line-item-code-cell,
    #line-items-table td.line-item-code-cell { min-width: 0; }
    #line-items-table td.line-item-code-cell { padding-inline: 0; }
    #line-items-table td.line-item-code-cell textarea {
      min-width: 0; padding: 0; white-space: pre-wrap; font-size: 13px;
      font-stretch: condensed;
    }
    #line-items-table td.legal-quantity-cell { min-width: 0; }
    #line-items-table textarea.wrap-capable-line-item-editor {
      display: block;
      min-height: 0;
      line-height: 1.2;
      resize: none;
      overflow-x: hidden;
      overflow-y: hidden;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      word-break: break-word;
    }
    /* One inspectable width budget: protect long text first, then compact short fields. */
    #line-items-table col[data-column="item_no"] { width: 24px; }
    #line-items-table col[data-column="description"] { width: 188px; }
    #line-items-table col[data-column="legal_quantity_1"] { width: 72px; }
    #line-items-table col[data-column="legal_quantity_2"] { width: 72px; }
    #line-items-table col[data-column="quantity"] { width: 72px; }
    #line-items-table col[data-column="quantity_unit"] { width: 44px; }
    #line-items-table col[data-column="unit_price"] { width: 72px; }
    #line-items-table col[data-column="total_price"] { width: 72px; }
    #line-items-table col[data-column="currency"] { width: 44px; }
    #line-items-table col[data-column="hs_code"] { width: 72px; }
    #line-items-table col[data-column="country_of_origin"] { width: 90px; }
    #line-items-table col[data-column="destination_country"] { width: 90px; }
    #line-items-table col[data-column="domestic_source"] { width: 90px; }
    #line-items-table col[data-column="net_weight_kg"] { width: 72px; }
    #line-items-table col[data-column="gross_weight_kg"] { width: 72px; }
    #line-items-table col[data-column="packages"] { width: 72px; }
    #line-items-table col[data-column="brand"] { width: 90px; }
    #line-items-table col[data-column="declaration_elements"] { width: 240px; }
    #line-items-table col[data-column="signoff"] { width: 34px; }

    /* Issue #389: the table is min-width:1582px and the sign-off column is the
       last of twenty, so on any ordinary screen the Operator had to drag right
       to tick and back left to read the values they were attesting to — one
       round trip per row, on an entry with 115 fields awaiting signature.

       Pinning both ends fixes the loop rather than the symptom: the tick stays
       reachable wherever the table is scrolled, and the row number stays
       readable so nobody ticks the wrong line. Everything between still
       scrolls normally; no column was narrowed or dropped (the width budget is
       #259's, not this issue's).

       border-collapse:collapse drops a sticky cell's own borders, so the
       separating hairline is drawn as a shadow on the edge that faces the
       scrolling content. */
    #line-items-table th.line-item-index-cell,
    #line-items-table td.line-item-index-cell {
      position: sticky;
      left: 0;
      z-index: 3;
      box-shadow: 1px 0 0 var(--hairline-soft);
    }
    #line-items-table th[data-column="signoff"],
    #line-items-table td[data-column="signoff"] {
      position: sticky;
      right: 0;
      z-index: 3;
      box-shadow: -1px 0 0 var(--hairline-soft);
    }
    /* Opaque so the scrolling columns pass underneath instead of showing
       through. Header cells keep their own surface-2 tone. */
    #line-items-table td.line-item-index-cell,
    #line-items-table td[data-column="signoff"] { background: var(--surface); }
    #line-items-table th.line-item-index-cell,
    #line-items-table th[data-column="signoff"] { background: var(--surface-2); }
    /* Containers (issue #107) */
    #containers-table td.containers-empty { color: var(--ink-3); font-size: 13px; padding: 10px 9px; }
    #containers-table .container-delete-btn { white-space: nowrap; }
    .legal-quantity-editor { display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; align-items: center; }
    .legal-unit-readonly {
      min-width: 0; width: 100%; padding: 3px 2px; border-radius: 6px;
      background: var(--surface-2); color: var(--ink-2); font-weight: 650;
      text-align: center; white-space: normal; overflow-wrap: anywhere;
    }
    .legal-unit-readonly.unavailable {
      min-width: 0;
      color: var(--ink-3);
      background: var(--surface-2);
      border: 1px solid var(--hairline-soft);
      font-size: 13px;
      font-weight: 500;
    }
    .line-items-table textarea.auto-grow-textarea {
      box-sizing: border-box;
      display: block;
      min-height: 48px;
      line-height: 1.45;
      resize: none;
      overflow-x: hidden;
      overflow-y: hidden;
      white-space: pre-wrap;
      overflow-wrap: anywhere;
      word-break: break-word;
    }
    @media (max-width: 600px) {
      #line-items-table td input,
      #line-items-table td textarea { font-size: 13px; }
      #line-items-table .element-template-editor input { font-size: 13px; }
    }
    .line-items-table td.cell-flagged {
      --tier-ink: var(--red);
      --tier-tint: var(--red-tint);
      --tier-border: var(--red-border);
    }
    /* ADR-0054: line-item cells DO carry text (the rule-conflict inline control
       lives in them), so they are in scope for the attention tier. Container
       cells are not — see renderContainers' allowAttention:false. */
    .line-items-table td.cell-attention {
      --tier-ink: var(--amber);
      --tier-tint: var(--amber-tint);
      --tier-border: var(--amber-border);
    }
    .line-items-table td.cell-flagged,
    .line-items-table td.cell-attention {
      background: var(--tier-tint);
      box-shadow: inset 0 0 0 1px var(--tier-border);
    }
    .line-items-table td.cell-flagged input,
    .line-items-table td.cell-flagged textarea,
    .line-items-table td.cell-attention input,
    .line-items-table td.cell-attention textarea { font-weight: 600; color: var(--tier-ink); }

    /* ── Declaration-elements split panel (T4 ②, absorbs #81) ──────────────── */
    .element-split-toggle {
      display: block;
      margin-top: 4px;
      padding: 4px 8px;
      border-radius: var(--radius-control);
      border: 1px solid var(--hairline);
      background: transparent;
      color: var(--ink-2);
      font-size: 13px;
      cursor: pointer;
    }
    .element-split-toggle:hover { background: var(--surface-2); }
    #line-items-table td[data-column="declaration_elements"] { position: relative; }
    .element-split-panel {
      position: fixed;
      z-index: 10002;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: min(400px, calc(100vw - 24px));
      max-height: min(calc(100vh - 24px), 620px);
      box-sizing: border-box;
      overflow: auto;
      padding: 8px;
      border: 1px solid var(--hairline);
      border-radius: var(--radius-control);
      background: var(--surface);
      box-shadow: var(--shadow-card);
      display: flex;
      flex-direction: column;
      gap: 6px;
    }
    .element-template-head,
    .element-template-meta,
    .element-template-editor {
      display: flex;
      align-items: center;
      gap: 6px;
    }
    .element-template-head { justify-content: space-between; flex-wrap: wrap; }
    .element-template-head strong,
    .element-template-note,
    .element-template-loading,
    .element-template-error,
    .element-template-name,
    .element-template-status { font-size: 13px; }
    .element-template-note { color: var(--ink-3); line-height: 1.45; }
    .element-template-loading { color: var(--ink-3); line-height: 1.45; }
    .element-template-error {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 8px;
      color: var(--red);
      line-height: 1.45;
    }
    .element-template-error button {
      flex: 0 0 auto;
      padding: 4px 8px;
      border: 1px solid var(--red-border);
      border-radius: var(--radius-control);
      background: var(--red-tint);
      color: var(--red);
      font: inherit;
      font-size: 13px;
      cursor: pointer;
    }
    .element-template-row {
      display: grid;
      gap: 4px;
      min-width: 0;
      padding: 5px 0;
      border-bottom: 1px solid var(--hairline-soft);
    }
    .element-template-meta { justify-content: space-between; min-width: 0; }
    .element-template-name { color: var(--ink-2); overflow-wrap: anywhere; }
    .element-template-status { color: var(--ink-3); font-weight: 600; }
    .element-template-required { color: var(--red); }
    .element-template-row.is-missing .element-template-name,
    .element-template-row.is-missing .element-template-status { color: var(--red); }
    .element-template-editor input[type=text] {
      flex: 1;
      min-width: 0;
      font: inherit;
      font-size: 13px;
      padding: 5px 7px;
      border: 1px solid var(--hairline);
      border-radius: var(--radius-control);
      background: var(--surface);
      color: var(--ink);
    }
    .element-template-row.is-missing input[type=text] {
      border-color: var(--red);
      background: var(--red-tint);
    }
    .element-template-editor input[type=text]:focus {
      outline: none;
      box-shadow: 0 0 0 2.5px rgba(0, 113, 227, 0.22);
    }
    .element-split-remove {
      flex-shrink: 0;
      width: 28px;
      height: 28px;
      padding: 0;
      border-radius: var(--radius-control);
      border: 1px solid var(--red-border);
      background: var(--red-tint);
      color: var(--red);
      font-size: 18px;
      cursor: pointer;
    }
    .element-copy-missing {
      flex: 0 0 auto;
      padding: 4px 8px;
      border: 1px solid var(--hairline);
      border-radius: var(--radius-control);
      background: var(--surface);
      color: var(--ink-2);
      font: inherit;
      font-size: 13px;
      cursor: pointer;
    }
    .element-copy-missing:disabled,
    .element-split-actions button:disabled {
      cursor: default;
      opacity: 0.55;
    }
    .element-split-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
    .element-split-actions button {
      font: inherit;
      font-size: 13px;
      padding: 4px 10px;
      border-radius: var(--radius-control);
      border: 1px solid var(--hairline);
      background: var(--surface);
      color: var(--ink-2);
      cursor: pointer;
    }
    .element-split-confirm {
      border-color: var(--green-border) !important;
      background: var(--green-tint) !important;
      color: var(--green) !important;
      font-weight: 600;
    }
    .field-origin-badge {
      display: inline-block;
      margin: 3px 2px 0;
      padding: 1px 5px;
      border-radius: 4px;
      font-size: 13px;
      line-height: 1.35;
      white-space: nowrap;
      background: var(--amber-tint);
      color: var(--amber);
    }
    td[data-origin="operator_correction"] .field-origin-badge {
      background: var(--accent-tint);
      color: var(--accent-ink);
    }

    /* ── HS Suggestion (ADR-0008): minimal inline hint, never auto-filled ──── */
    .hs-suggestion {
      margin-top: 3px;
      font-size: 13px;
      line-height: 1.4;
      color: var(--ink-2);
      white-space: normal;
      /* Issue #91: with the source panel collapsed the table runs full width —
         keep advisory text a readable measure instead of a viewport-wide line. */
      max-width: 100%;
      min-width: 0;
      overflow-wrap: anywhere;
      word-break: break-word;
    }
    .hs-suggestion:hover { text-decoration: underline; text-decoration-color: var(--hairline); text-underline-offset: 2px; }
    .hs-suggestion .code { font-weight: 650; color: var(--green); font-variant-numeric: tabular-nums; }
    .hs-suggestion .valid-mark { color: var(--green); margin-left: 3px; }
    .hs-suggestion.expired { color: var(--amber); }
    .hs-suggestion.expired .code { color: var(--amber); }
    .hs-suggestion .expired-mark { color: var(--red); font-weight: 600; margin-left: 3px; }
    /* #743: 未核验 is neither good news nor an alarm — subdued ink, no weight. */
    .hs-suggestion .unverified-mark { color: var(--ink-subtle); margin-left: 3px; }

    /* ── 归类助手 assist panel (issue #85): extends .hs-suggestion above ────── */
    .hs-suggestion .warn { color: var(--red); font-weight: 650; }
    .hs-suggestion .assist-answer-row { display: flex; gap: 6px; align-items: center; margin: 3px 0; }
    .hs-suggestion .assist-answer-row input {
      font-size: 13px;
      padding: 3px 6px;
      border: 1px solid var(--hairline);
      border-radius: 5px;
      background: var(--bg);
      color: var(--ink);
      flex: 1;
      min-width: 0;
    }

    /* ── 监管证件 requirement notice (issue #84): loud, advisory only ──────── */
    .supervision-note {
      margin-top: 3px;
      font-size: 13px;
      line-height: 1.45;
      color: var(--red);
      font-weight: 650;
      white-space: normal;
    }
    .supervision-note .sub { font-weight: 400; font-size: 13px; color: var(--ink-3); }
    /* Issue #119: 官方表已标废止/停用的证件代码——仍然显示（不隐藏），但用醒目色区分。 */
    .supervision-note .sub.deprecated-note { color: var(--red); font-weight: 600; }

    /* ── Price Reference (ADR-0021): minimal inline hint, never auto-filled ── */
    .price-suggestion {
      margin-top: 3px;
      font-size: 13px;
      line-height: 1.4;
      color: var(--ink-2);
      white-space: normal;
      cursor: pointer;
    }
    .price-suggestion:hover { text-decoration: underline; text-decoration-color: var(--hairline); text-underline-offset: 2px; }
    .price-suggestion .code { font-weight: 650; color: var(--green); font-variant-numeric: tabular-nums; }

    /* ── Exit-port advice (issue #111): separate from the editable value ───── */
    .exit-port-suggestion,
    .exit-port-candidates,
    .exit-port-advice-warning {
      grid-column: 2 / 4;
      margin: -2px 0 4px;
      font-size: 13px;
      line-height: 1.45;
      white-space: normal;
    }
    .exit-port-suggestion {
      padding: 0;
      border: 0;
      border-radius: var(--radius-control);
      background: transparent;
      color: var(--ink-2);
      font: inherit;
      text-align: left;
      cursor: pointer;
    }
    .exit-port-suggestion:hover { text-decoration: underline; text-underline-offset: 2px; }
    .exit-port-suggestion .code {
      color: var(--green);
      font-weight: 650;
      font-variant-numeric: tabular-nums;
    }
    .exit-port-candidates {
      color: var(--ink-2);
      font-variant-numeric: tabular-nums;
    }
    .exit-port-candidate-picker {
      display: flex;
      align-items: center;
      flex-wrap: wrap;
      gap: 6px;
      margin-top: 6px;
    }
    .exit-port-candidate-select {
      flex: 1 1 320px;
      max-width: 100%;
      min-width: 0;
      padding: 6px 10px;
      border: 1px solid var(--hairline);
      border-radius: var(--radius-control);
      background: var(--surface);
      color: var(--ink);
      font: inherit;
      font-size: 13px;
    }
    .exit-port-candidate-action {
      border-radius: var(--radius-control);
      white-space: normal;
      font-variant-numeric: tabular-nums;
    }
    .exit-port-advice-warning { color: var(--red); font-weight: 600; }

    /* ── Sum reconciliation write-back (T4 ③): same separate-until-clicked
       presentation as the exit-port suggestion above — never pre-filled. ── */
    .sum-writeback-suggestion {
      grid-column: 2 / 4;
      margin: -2px 0 4px;
      font-size: 13px;
      line-height: 1.45;
      white-space: normal;
      color: var(--ink-2);
      cursor: pointer;
    }
    .sum-writeback-suggestion:hover { text-decoration: underline; text-underline-offset: 2px; }
    .sum-writeback-suggestion .code {
      color: var(--green);
      font-weight: 650;
      font-variant-numeric: tabular-nums;
    }

