/* TAPAK web UI: tokens, shell and components. Pages use only the classes listed here.

   COMPONENT REFERENCE (class or hook: usage)
   Layout
     .page                 vertical rhythm for a page body (gap 32 px); first child usually .page-head
     .page-head            title block: p.eyebrow, h1, p (lead); optional div.page-actions sits right
     .stack / .stack-sm / .stack-lg   flex column, gap 24 / 12 / 32 px
     .row / .row-between   wrapping flex row, gap 12 px (between: space-between)
     .grid-2 / .grid-3     equal columns that collapse on narrow content (auto-fit)
     .split                main + aside (about 5:3) when the content area is >= 880 px wide
     .split-even           two equal columns when the content area is >= 760 px wide
     .section-head         h2 left, meta or actions right (outside cards)
   Surfaces
     .card                 surface, hairline border, radius 14, padding 24; h2 + .lead inside
     .card-head            row: <div><h2>..</h2><p class="lead">..</p></div> + actions on the right
     .card-flush           card without padding (edge-to-edge table or list); pad parts with
       .card-section       (siblings get a divider); tables in it get 24 px outer cell padding
     .well                 sunken inset area inside a card (radius 10)
   Type
     .eyebrow / .label     Plex Mono 12 px uppercase, letter-spaced, muted (.kicker is an alias)
     .lead / .hint         muted intro paragraph (65ch) / 13 px helper text
     .mono / .hash         Plex Mono for ids and hashes (.hash truncates: put the full value in title)
     .num                  right-aligned tabular figures (table cells and inline numbers)
     .tnum                 tabular figures only (a count in running text or a card head)
     .muted .small .nowrap .link .keep-case .sr-only .hide-sm (hidden below 640 px)
     Sizes: --fs-label 12, --fs-sm 13, --fs-ui 14, --fs-body 15, --fs-lead 16 (intro and verdict
       lines; --fs-input), --fs-h2 18, --fs-title 20, --fs-h1 28 (--fs-stat; both 24 on phones),
       --fs-display 36. Use these tokens, never a raw px font size.
   Buttons
     .btn + .btn-primary | .btn-secondary | .btn-ghost | .btn-danger ; .btn-small ; .btn-block
       one primary per view; icons go first inside the button: {{ ikon("download") }}Unduh
     Loading (submit buttons): hx-disabled-elt="find button[type=submit]" on the form, and either
       <span class="htmx-indicator spinner" aria-hidden="true"></span><span class="idle-label">Lacak</span>
       <span class="htmx-indicator">Menganalisis bukti...</span> inside the button, or
       data-loading-text="Menganalisis bukti..." on it (tapak.js swaps the text, adds a spinner).
     .copy-btn data-copy="text" or data-copy-target="#id": copies; label reads "Tersalin" for 1.5 s.
       Inner: {{ ikon("copy") }}{{ ikon("check") }}<span class="copy-label">Salin</span>
   Forms
     .field: label (or span.field-label) + control + .hint + error slot <span id="err-x">
     .field-optional       "(opsional)" after a label or a heading: muted, 14 px, regular weight
     .field-error          message with a drawn "!" icon (keep inside the err-/-err slots)
     input, select, textarea: styled bare (16 px, 44 px tall); aria-invalid="true" turns them red
     .check-row            <label class="check-row"><input type=checkbox><span>..</span></label>
     fieldset.field        a group of radios: legend.field-label + optional .hint + .choices
     .choices > .choice    option cards side by side (stacked when narrow):
       <label class="choice"><input type="radio" ..><span class="choice-body">
         <span class="choice-title">..</span><span class="choice-desc" id=..>..</span></span></label>
       the checked card is tinted olive; give the radio aria-describedby="<desc id> <err slot>"
     .dropzone             <label class="dropzone" data-dropzone> around the real input:
       <input type="file" id=.. name=..>  (stays focusable; covers the zone; keep its aria wiring)
       <span class="dropzone-icon" aria-hidden="true">{{ ikon("file-up") }}</span>
       <span class="dropzone-text"><span class="dropzone-title">..</span>
         <span class="dropzone-hint">atau <span class="dropzone-link">pilih berkas</span> ..</span></span>
       <ul class="dropzone-files" data-dropzone-files></ul>   (tapak.js lists name + size)
       states set automatically: .is-dragover, .has-files, red border when input[aria-invalid]
     details.more          disclosure with a chevron summary
     .segmented            div role="group" aria-label=.. of <button aria-pressed>; tone classes
                           .btn-approve (olive when pressed) and .btn-reject (red when pressed)
   Status and data
     .stamp + .stamp-ok | .stamp-warn | .stamp-danger | .stamp-neutral ; .stamp-lg (verdicts)
       the only status mark: verdict, registry, visual gate, variant status, Tercatat,
       Melewati ambang, Galat. Not for counts, optional fields or notes (use .muted .tnum,
       .field-optional, .hint). Text in sentence case in the markup; CSS uppercases it.
     .stats > .stat (.stat-warn | .stat-danger) > .stat-label + .stat-value + .stat-note
     .meter (.meter-below)  {% set m = meter(score, threshold) %}
       <div class="meter" style="--value: {{ m.value }}; --mark: {{ m.mark }}">
         <div class="meter-track"><div class="meter-fill"></div><div class="meter-mark"></div></div>
         <div class="meter-legend"><span>Skor <b>..</b></span><span>Ambang Z <b>..</b></span></div></div>
       (leave out .meter-mark when m.mark is none; .meter-below paints the fill amber)
     .bar style="--value: .4"  small inline bar (96 px) for a 0..1 share; .bar-muted ; .bar-warn
     table (or .table) inside .table-wrap; th/td.num for numbers
     .kv                   <dl class="kv"><dt>Label</dt><dd>value</dd></dl>
     .notice (info) | .notice-error | .notice-warn | .notice-ok   icon drawn by CSS
     .stepper              <ol class="stepper" aria-label=..> of li.stepper-item (.is-done or
                           aria-current="step") > span.stepper-num + span.stepper-label
     .thumb                <span class="thumb"><img src=.. alt=..></span> page-1 thumbnail;
                           .thumb-sm 44 px wide, default 120 px, .thumb-lg 200 px
     .list > li.list-row > .thumb + .list-main (.list-title + .list-meta) + .list-actions
     mark.diff             changed words; produced by {{ original|beda(variant) }}
     .empty-state          dashed box: h2 or h3 + optional p + one action; .empty = one muted line
     .spinner ; .busy.htmx-indicator > span.busy-bar + text (inside the form, visible while
                           its request runs; tapak.js reads the text out through #status-global)
     .code-row > .code (mono block, give it an id) + .copy-btn data-copy-target="#id"
   Jinja helpers (app.py): ikon(name, size) and logo() from _ikon.html; filter beda(variant);
     filter pangkat (writes '9,5 x 10^-6' as on the verdict, with a spoken form for screen readers);
     meter(value, mark, peak) (peak: one scale for a group of meters); pratinjau_url(manifest)
     (None for DOCX masters); registry_status();
     thumbnails: /dokumen/<16-hex>/pratinjau.png and /terbitkan/<sid>/pratinjau.png
   Data hooks (tapak.js)
     data-autofocus        focus target after an htmx swap
     data-focus-fallback   (with tabindex="-1") focus target after a swap only when the swap
                           left focus on <body> and the requesting button is gone
     data-enter            on an hx-target: its new children fade in (always on for #alur,
                           #hasil, #merkle, #galat-global)
     data-preview-from="#select" on an <img>: its src follows the selected option's
                           data-pratinjau URL (empty: no image); without that attribute,
                           data-preview-src="/dokumen/{}/pratinjau.png" is filled with the value
     data-quiet            htmx element whose requests skip the busy bar and notice clearing
   Icons: {% from "_ikon.html" import ikon %}, {{ ikon("files") }}; 18 px by default, sized by
     the component they sit in (.btn 18, .btn-small and .segmented 16). Names: home files scan
     ledger file-up file-text users image upload download copy check x alert info arrow-right
     arrow-left refresh shield key bird sparkline type settings. */

/* Latin subsets of IBM Plex (OFL); the Sans files also carry <= and >= from the full IBM release */
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/static/fonts/IBMPlexSans-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+2264-2265, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/static/fonts/IBMPlexSans-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+2264-2265, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans";
  src: url("/static/fonts/IBMPlexSans-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+2264-2265, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/static/fonts/IBMPlexMono-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/static/fonts/IBMPlexMono-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------------------------------------------------------------- tokens */

:root {
  color-scheme: light dark;

  /* paper and ink (warm neutrals, hue 88 to 100) */
  --paper: oklch(0.962 0.008 88);
  --sidebar: oklch(0.945 0.011 88);
  --surface: oklch(0.99 0.004 90);
  --surface-sunken: oklch(0.966 0.007 88);
  --surface-raised: oklch(1 0 0);
  --hover: oklch(0.3 0.03 100 / 0.06);
  --track: oklch(0.905 0.012 90);
  --ink: oklch(0.235 0.016 100);
  --ink-muted: oklch(0.46 0.016 100);
  --ink-faint: oklch(0.56 0.014 100);
  --border: oklch(0.3 0.03 100 / 0.13);
  --border-strong: oklch(0.3 0.03 100 / 0.22);
  --border-input: oklch(0.3 0.03 100 / 0.56);

  /* olive drab accent */
  --accent: oklch(0.46 0.085 122);
  --accent-hover: color-mix(in oklch, var(--accent) 86%, black);
  --accent-text: oklch(0.42 0.08 122);
  --accent-soft: oklch(0.93 0.035 120);
  --accent-line: oklch(0.42 0.08 122 / 0.45);
  --on-accent: oklch(0.985 0.006 100);

  /* stamp red: classification banner, errors, destructive actions only */
  --danger: oklch(0.5 0.165 27);
  --danger-solid: oklch(0.5 0.165 27);
  --danger-soft: oklch(0.955 0.02 27);
  --danger-line: oklch(0.5 0.165 27 / 0.35);
  --banner: oklch(0.48 0.16 27);
  --on-banner: oklch(0.985 0.004 90);

  /* amber: warnings and "tidak cukup bukti" */
  --warn: oklch(0.5 0.105 65);
  --warn-fill: oklch(0.6 0.125 70);  /* 3:1 on --track, for the meter and bar fills */
  --warn-soft: oklch(0.955 0.04 85);
  --warn-line: oklch(0.5 0.105 65 / 0.4);

  --focus: oklch(0.5 0.1 122);
  --disabled-bg: oklch(0.915 0.008 90);
  --disabled-text: oklch(0.55 0.01 95);
  --scroll-shadow: oklch(0.2 0.02 100 / 0.14);
  --shadow-pop: 0 0 0 1px oklch(0.3 0.03 100 / 0.06), 0 2px 4px oklch(0.3 0.03 100 / 0.06),
                0 12px 28px -8px oklch(0.3 0.03 100 / 0.18);
  --thumb-outline: rgb(0 0 0 / 0.1);
  --thumb-filter: none;

  /* type */
  --font-sans: "IBM Plex Sans", system-ui, "Segoe UI", Roboto, "Noto Sans", "DejaVu Sans", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, "DejaVu Sans Mono", monospace;
  --fs-label: 12px;
  --fs-sm: 13px;
  --fs-ui: 14px;
  --fs-body: 15px;
  --fs-input: 16px;
  --fs-lead: 16px;
  --fs-h2: 18px;
  --fs-title: 20px;
  --fs-h1: 28px;
  --fs-stat: 28px;
  --fs-display: 36px;

  /* shape and space */
  --r-1: 6px;
  --r-2: 10px;
  --r-3: 14px;
  --r-stamp: 3px;
  --banner-h: 24px;
  --sidebar-w: 248px;
  --gutter: 48px;
  --content-max: 1100px;
  --ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
  --ease-enter: cubic-bezier(0.32, 0.72, 0, 1);  /* things arriving */
  --ease-run: cubic-bezier(0.22, 1, 0.36, 1);    /* a bar running out to its value */
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: oklch(0.175 0.012 110);
    --sidebar: oklch(0.16 0.011 110);
    --surface: oklch(0.215 0.013 110);
    --surface-sunken: oklch(0.19 0.012 110);
    --surface-raised: oklch(0.255 0.014 110);
    --hover: oklch(0.95 0.02 100 / 0.06);
    --track: oklch(0.3 0.012 110);
    --ink: oklch(0.93 0.012 95);
    --ink-muted: oklch(0.74 0.014 95);
    --ink-faint: oklch(0.62 0.014 95);
    --border: oklch(0.29 0.014 110);
    --border-strong: oklch(0.36 0.016 110);
    --border-input: oklch(0.53 0.016 110);

    --accent: oklch(0.72 0.095 122);
    --accent-hover: color-mix(in oklch, var(--accent) 88%, white);
    --accent-text: oklch(0.8 0.095 122);
    --accent-soft: oklch(0.3 0.045 122);
    --accent-line: oklch(0.55 0.08 122);
    --on-accent: oklch(0.18 0.02 115);

    --danger: oklch(0.72 0.14 27);
    --danger-solid: oklch(0.56 0.15 27);
    --danger-soft: oklch(0.29 0.06 27);
    --danger-line: oklch(0.5 0.12 27);
    --banner: oklch(0.48 0.16 27);

    --warn: oklch(0.8 0.12 75);
    --warn-fill: oklch(0.76 0.12 75);
    --warn-soft: oklch(0.3 0.05 75);
    --warn-line: oklch(0.55 0.09 75);

    --focus: oklch(0.78 0.1 122);
    --disabled-bg: oklch(0.27 0.01 110);
    --disabled-text: oklch(0.56 0.012 100);
    --scroll-shadow: oklch(1 0 0 / 0.16);
    --shadow-pop: 0 0 0 1px oklch(0.4 0.016 110), 0 12px 28px -8px oklch(0 0 0 / 0.5);
    --thumb-outline: rgb(255 255 255 / 0.1);
    --thumb-filter: brightness(0.9);
  }
}

/* ---------------------------------------------------------------- base */

*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 1.55;
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
h1, h2, h3, h4 { margin: 0; font-weight: 600; text-wrap: balance; }
h1 { font-size: var(--fs-h1); line-height: 1.15; letter-spacing: -0.015em; }
h2 { font-size: var(--fs-h2); line-height: 1.3; letter-spacing: -0.005em; }
h3 { font-size: var(--fs-body); line-height: 1.4; }
p { margin: 0; }
p, li { text-wrap: pretty; }
img { display: block; max-width: 100%; }
sup { line-height: 0; }  /* a superscript must not push its line down */
b, strong { font-weight: 600; }
a { color: inherit; }
::selection { background: var(--accent-soft); color: var(--ink); }
/* anchors, scrolled-to results and controls reached by Shift+Tab land below the sticky banner */
html { scroll-padding-top: calc(var(--banner-h) + 16px); }

:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
[data-autofocus]:focus, [data-focus-fallback]:focus, main:focus, #galat-global:focus { outline: none; }
[data-autofocus]:focus-visible, [data-focus-fallback]:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; border-radius: 2px; }

.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; }
/* icons from the ikon() macro: sized by the macro argument, or by the component they sit in */
.icon { display: inline-block; flex: none; vertical-align: -0.2em; }

/* ---------------------------------------------------------------- shell */

.skip-link {
  position: absolute;
  top: calc(var(--banner-h) + 8px);
  left: 8px;
  z-index: 100;
  padding: 10px 16px;
  border-radius: var(--r-1);
  background: var(--ink);
  color: var(--paper);
  font-weight: 500;
  text-decoration: none;
}
.skip-link:not(:focus) { width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }

/* classification banner, as on every classified system screen; stays in view while scrolling */
.banner {
  position: sticky;
  top: 0;
  z-index: 50;
  height: var(--banner-h);
  display: grid;
  place-items: center;
  background: var(--banner);
  color: var(--on-banner);
  font: 500 var(--fs-label)/1 var(--font-mono);
  letter-spacing: 0.32em;
  padding-left: 0.32em;
  text-transform: uppercase;
}

/* thin bar along the banner's lower edge while an htmx request without its own indicator
   runs longer than 300 ms (tapak.js sets html.is-busy) */
.top-progress {
  position: fixed;
  top: var(--banner-h);
  left: 0;
  right: 0;
  z-index: 49;
  height: 2px;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 150ms ease-out;
}
.top-progress::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 30%;
  background: var(--accent);
  animation: busy 1.2s cubic-bezier(0.45, 0, 0.55, 1) infinite paused;
}
html.is-busy .top-progress { opacity: 1; transition-delay: 300ms; }
html.is-busy .top-progress::after { animation-play-state: running; }

/* the sidebar column is painted by the shell, so it runs the full page height while the
   sidebar itself is one window tall and sticks below the banner (nav and registry status stay
   in view on long pages) */
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  min-height: calc(100dvh - var(--banner-h));
  background: linear-gradient(to right, var(--sidebar) calc(var(--sidebar-w) - 1px), var(--border) 0 var(--sidebar-w), transparent 0);
}

.sidebar {
  display: flex;
  flex-direction: column;
  gap: 28px;
  padding: 24px 16px 20px;
}
@media (min-width: 1024px) {
  .sidebar {
    position: sticky;
    top: var(--banner-h);
    align-self: start;
    height: calc(100dvh - var(--banner-h));
    overflow-y: auto;
  }
}

.brand {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  column-gap: 10px;
  row-gap: 6px;
  padding: 0 8px;
  color: inherit;
  text-decoration: none;
  border-radius: var(--r-1);
}
.brand-mark { width: 30px; height: 30px; color: var(--ink); }
.brand-name { font-size: var(--fs-h2); font-weight: 600; letter-spacing: 0.14em; line-height: 1; }
.brand-sub { grid-column: 1 / -1; font-size: var(--fs-label); line-height: 1.35; color: var(--ink-muted); text-wrap: balance; }

.nav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.nav a {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 0 12px;
  border-radius: var(--r-1);
  color: var(--ink-muted);
  font-size: var(--fs-ui);
  font-weight: 500;
  text-decoration: none;
  transition: background-color 150ms ease-out, color 150ms ease-out;
}
.nav a:hover { color: var(--ink); background: var(--hover); }
.nav a[aria-current="page"] { color: var(--accent-text); background: var(--accent-soft); }
.nav svg { width: 18px; height: 18px; }

.sidebar-status {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 12px;
  border-top: 1px solid var(--border);
  font-size: var(--fs-sm);
}
.status-line { display: flex; align-items: baseline; gap: 8px; color: var(--ink-muted); }
.status-line b { color: var(--ink); font-weight: 500; }
.status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex: none; transform: translateY(-1px); }
.status-registry {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  color: inherit;
  text-decoration: none;
  border-radius: var(--r-1);
}
.status-registry:hover .status-meta { color: var(--ink); }
.status-meta { color: var(--ink-muted); font-variant-numeric: tabular-nums; }

.main-col { display: flex; flex-direction: column; min-width: 0; }
main {
  flex: 1;
  width: 100%;
  max-width: calc(var(--content-max) + 2 * var(--gutter));
  margin: 0 auto;
  padding: 40px var(--gutter) 64px;
  container-type: inline-size;
  container-name: main;
}
#galat-global:not(:empty) { margin-bottom: 24px; }
.instans-publik { margin-bottom: 24px; }

.footer { border-top: 1px solid var(--border); color: var(--ink-muted); font-size: var(--fs-label); }
.footer-inner {
  max-width: calc(var(--content-max) + 2 * var(--gutter));
  margin: 0 auto;
  padding: 16px var(--gutter) 24px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  justify-content: space-between;
}
.footer-inner > span { overflow-wrap: anywhere; min-width: 0; }

/* ---------------------------------------------------------------- layout */

.page { display: flex; flex-direction: column; gap: 32px; }
.page-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 32px;
  row-gap: 8px;
  align-items: end;
  margin-bottom: 32px;
}
.page > .page-head { margin-bottom: 0; }
.page-head > * { grid-column: 1; margin: 0; }
.page-head > p { color: var(--ink-muted); max-width: 65ch; }
.page-head > .eyebrow + h1 { margin-top: 2px; }
.page-head > .page-actions { grid-column: 2; grid-row: 1 / span 4; display: flex; gap: 12px; flex-wrap: wrap; }

.stack { display: flex; flex-direction: column; gap: 24px; }
.stack-sm { display: flex; flex-direction: column; gap: 12px; }
.stack-lg { display: flex; flex-direction: column; gap: 32px; }
.row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.row-between { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: 16px; }
.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); gap: 16px; }
.split, .split-even { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; align-items: start; }
@container main (min-width: 880px) {
  .split { grid-template-columns: minmax(0, 5fr) minmax(0, 3fr); }
}
@container main (min-width: 760px) {
  .split-even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.section-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px 16px; margin-bottom: 12px; }

/* ---------------------------------------------------------------- type helpers */

.eyebrow, .label, .kicker {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.lead { color: var(--ink-muted); max-width: 65ch; }
.mono { font-family: var(--font-mono); font-size: 0.92em; overflow-wrap: anywhere; }
.hash { font-family: var(--font-mono); font-size: 0.92em; display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tnum { font-variant-numeric: tabular-nums; }
.muted { color: var(--ink-muted); }
.small { font-size: var(--fs-sm); }
.nowrap { white-space: nowrap; }
.keep-case { text-transform: none; letter-spacing: 0; }
.link {
  color: var(--accent-text);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--accent-line);
  text-underline-offset: 3px;
  transition: text-decoration-color 150ms ease-out;
}
.link:hover { text-decoration-color: currentColor; }
.hint { font-size: var(--fs-sm); color: var(--ink-muted); max-width: 65ch; }

/* ---------------------------------------------------------------- surfaces */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-3);
  padding: 24px;
  min-width: 0;
}
.card > h2 { margin: 0 0 4px; }
.card > h2:last-child { margin-bottom: 0; }
.card > h2 + :not(p) { margin-top: 16px; }
h2 > .stamp { margin-inline: 0 10px; vertical-align: 2px; }
h2 > .stamp:not(:first-child) { margin-inline: 10px 0; }
.card > .lead { margin: 0 0 20px; }
.card-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px 24px; margin-bottom: 20px; }
.card-head > div { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1 1 18rem; }

.card-flush { padding: 0; overflow: hidden; }
.card-flush > .card-head, .card-section { padding: 20px 24px; margin: 0; }
.card-section + .card-section, .card-flush > .card-head + .card-section { border-top: 1px solid var(--border); }
.card-flush > .card-head { padding-bottom: 0; }
.card-flush > .card-head + .card-section { border-top: 0; }
.well { background: var(--surface-sunken); border-radius: var(--r-2); padding: 16px; }

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 18px;
  border: 1px solid transparent;
  border-radius: var(--r-1);
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 150ms ease-out, border-color 150ms ease-out, color 150ms ease-out, transform 120ms ease-out;
}
.btn svg { width: 18px; height: 18px; }
.btn:active:not(:disabled):not([aria-disabled="true"]) { transform: scale(0.97); }
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); }
/* controls sit on the raised surface, a step above the card they are on (lightest on top) */
.btn-secondary { background: var(--surface-raised); color: var(--ink); border-color: var(--border-strong); }
.btn-secondary:hover:not(:disabled) { background: color-mix(in oklch, var(--surface-raised) 94%, var(--ink)); border-color: var(--border-input); }
.btn-ghost { background: transparent; color: var(--ink); }
.btn-ghost:hover:not(:disabled) { background: var(--hover); }
.btn-danger { background: var(--danger-solid); color: oklch(0.985 0.004 90); }
.btn-danger:hover:not(:disabled) { background: color-mix(in oklch, var(--danger-solid) 88%, black); }
.btn-small { min-height: 34px; padding: 0 12px; font-size: var(--fs-ui); }
.btn-small svg { width: 16px; height: 16px; }
.btn-block { width: 100%; }
.btn:disabled { background: var(--disabled-bg); color: var(--disabled-text); border-color: transparent; cursor: not-allowed; }
.btn[aria-disabled="true"] { color: var(--ink-muted); }
.btn[data-loading] { cursor: progress; }
/* a button whose request is running is busy, not unavailable: it keeps its colors */
.btn-primary:disabled[data-loading] { background: var(--accent); color: var(--on-accent); opacity: 0.85; }
.btn-secondary:disabled[data-loading] { background: var(--surface-raised); color: var(--ink); border-color: var(--border-strong); opacity: 0.85; }
.btn[data-loading]:not(:has(.spinner))::before {
  content: "";
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@media (pointer: coarse) {
  .btn-small { min-height: 44px; min-width: 44px; }  /* touch targets never below 44 px */
}

.copy-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 42px;  /* as tall as a one-line .code beside it */
  padding: 0 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-1);
  background: var(--surface-raised);
  color: var(--ink-muted);
  font: 500 var(--fs-sm)/1 var(--font-sans);
  cursor: pointer;
  white-space: nowrap;
  transition: color 150ms ease-out, border-color 150ms ease-out, background-color 150ms ease-out, transform 120ms ease-out;
}
.copy-btn svg { width: 15px; height: 15px; }
.copy-btn:hover { color: var(--ink); border-color: var(--border-input); }
.copy-btn:active { transform: scale(0.97); }
/* copy -> check: both icons stay in place, one shrinks and blurs into the other */
.copy-btn { position: relative; }
.copy-btn .icon-copy, .copy-btn .icon-check {
  transition: opacity 300ms cubic-bezier(0.25, 0.1, 0, 1), transform 300ms cubic-bezier(0.25, 0.1, 0, 1),
              filter 300ms cubic-bezier(0.25, 0.1, 0, 1);
}
.copy-btn .icon-check { position: absolute; left: 10px; top: calc(50% - 7.5px); opacity: 0; transform: scale(0.3); filter: blur(3px); }
.copy-btn[data-copied] { color: var(--accent-text); border-color: var(--accent-line); }
.copy-btn[data-copied] .icon-copy { opacity: 0; transform: scale(0.3); filter: blur(3px); }
.copy-btn[data-copied] .icon-check { opacity: 1; transform: none; filter: none; }
@media (pointer: coarse) { .copy-btn { min-height: 44px; } }

/* ---------------------------------------------------------------- forms */

form { display: flex; flex-direction: column; gap: 20px; }
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field > label, .field-label { font-size: var(--fs-ui); font-weight: 500; color: var(--ink); }
.field-optional { color: var(--ink-muted); font-size: var(--fs-ui); font-weight: 400; letter-spacing: 0; }

input[type="text"], input[type="number"], input[type="email"], input[type="search"], input[type="url"],
input[type="password"], input[type="tel"], select, textarea {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--border-input);
  border-radius: var(--r-1);
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: var(--fs-input);
  line-height: 1.4;
  transition: border-color 150ms ease-out, background-color 150ms ease-out;
}
input::placeholder, textarea::placeholder { color: var(--ink-faint); opacity: 1; }
input:hover, select:hover, textarea:hover { border-color: var(--ink-faint); }
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; border-color: var(--focus); }
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] { border-color: var(--danger); }
textarea { min-height: 128px; resize: vertical; line-height: 1.5; }
select {
  appearance: none;
  padding-right: 40px;
  cursor: pointer;
  text-overflow: ellipsis;
  /* chevron drawn with two gradients, so it follows the theme without an image */
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-muted) 50%),
    linear-gradient(135deg, var(--ink-muted) 50%, transparent 50%);
  background-position: calc(100% - 20px) 50%, calc(100% - 15px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
select option { background: var(--surface); color: var(--ink); }
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); width: 18px; height: 18px; margin: 0; }
/* a file input outside a .dropzone */
input[type="file"] {
  width: 100%;
  padding: 6px;
  border: 1px solid var(--border-input);
  border-radius: var(--r-1);
  background: var(--surface-sunken);
  color: var(--ink-muted);
  font: inherit;
  font-size: var(--fs-ui);
}
input[type="file"]::file-selector-button {
  min-height: 34px;
  margin-right: 12px;
  padding: 0 12px;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  background: var(--surface);
  color: var(--ink);
  font: 500 var(--fs-ui)/1 var(--font-sans);
  cursor: pointer;
}

.field-error {
  position: relative;
  display: block;
  padding-left: 24px;
  font-size: var(--fs-sm);
  line-height: 1.45;
  color: var(--danger);
}
.field-error::before {
  content: "!";
  content: "!" / "";  /* drawn icon, not text: the message is read without it */
  position: absolute;
  left: 0;
  top: 1px;
  width: 16px;
  height: 16px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--danger);
  color: var(--surface);
  font: 600 11px/1 var(--font-mono);
}

.check-row {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 10px 12px;
  border-radius: var(--r-1);
  cursor: pointer;
  transition: background-color 150ms ease-out;
}
.check-row:hover { background: var(--hover); }
.check-row input { margin-top: 3px; flex: none; }
.check-row[aria-disabled="true"] { cursor: default; }
form > .check-row + .check-row { margin-top: -14px; }
.check-row[aria-disabled="true"]:hover { background: transparent; }

/* radio groups: a fieldset laid out like any .field; the legend is not a flex item, so it
   carries the gap below it */
fieldset.field { margin: 0; padding: 0; border: 0; }
fieldset.field > legend { padding: 0; margin-bottom: 6px; }
.choices { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: 12px; margin-top: 2px; }
.choice {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  min-width: 0;
  padding: 14px 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-2);
  background: var(--surface);
  cursor: pointer;
  transition: background-color 150ms ease-out, border-color 150ms ease-out;
}
.choice:hover { background: var(--surface-sunken); }
.choice:has(> input:checked) { border-color: var(--accent-line); background: color-mix(in oklch, var(--accent-soft) 35%, var(--surface)); }
.choice:has(> input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; }
.choice:has(> input[aria-invalid="true"]) { border-color: var(--danger-line); }
.choice > input { margin-top: 2px; flex: none; }
/* the card carries the focus ring; without :has() the radio keeps its own */
@supports selector(:has(*)) { .choice > input:focus-visible { outline: none; } }
.choice-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.choice-title { font-weight: 500; }
.choice-desc { font-size: var(--fs-sm); color: var(--ink-muted); }

/* drop zone: a label around the real file input; the input covers the zone (invisible but
   focusable), so click, keyboard and native drop all work without script */
.dropzone {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 4px 14px;
  min-height: 112px;
  padding: 20px;
  border: 1.5px dashed var(--border-input);
  border-radius: var(--r-2);
  background: var(--surface-sunken);
  cursor: pointer;
  transition: border-color 150ms ease-out, background-color 150ms ease-out, box-shadow 150ms ease-out;
}
.dropzone > input[type="file"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  font-size: 0;
}
.dropzone:hover { border-color: var(--ink-faint); background: var(--surface); }
.dropzone:has(> input:focus-visible) { outline: 2px solid var(--focus); outline-offset: 2px; border-color: var(--focus); }
.dropzone-icon {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: var(--r-1);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--ink-muted);
}
.dropzone-icon svg { width: 20px; height: 20px; }
.dropzone-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dropzone-title { font-weight: 500; color: var(--ink); }
.dropzone-hint { font-size: var(--fs-sm); color: var(--ink-muted); }
.dropzone-link { color: var(--accent-text); text-decoration: underline; text-decoration-color: var(--accent-line); text-underline-offset: 3px; }
.dropzone-files { grid-column: 1 / -1; list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.dropzone-files:empty { display: none; }
.dropzone-files li {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 8px 10px;
  border-radius: var(--r-1);
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: var(--fs-ui);
  min-width: 0;
}
.dropzone-files .file-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.dropzone-files .file-size { flex: none; font-family: var(--font-mono); font-size: var(--fs-label); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.dropzone.has-files { border-style: solid; border-color: var(--accent-line); background: var(--surface); }
.dropzone.has-files .dropzone-icon { color: var(--accent-text); background: var(--accent-soft); border-color: transparent; }
.dropzone.is-dragover {
  border-style: solid;
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: var(--shadow-pop);
}
.dropzone.is-dragover .dropzone-icon { color: var(--accent-text); border-color: var(--accent-line); }
.dropzone:has(> input[aria-invalid="true"]) { border-color: var(--danger); }

details.more { border-top: 1px solid var(--border); padding-top: 12px; }
details > summary { list-style: none; cursor: pointer; width: fit-content; }
details > summary::-webkit-details-marker { display: none; }
details.more > summary {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 36px;
  padding-right: 8px;
  border-radius: var(--r-1);
  color: var(--ink-muted);
  font-size: var(--fs-ui);
  font-weight: 500;
  transition: color 150ms ease-out;
}
details.more > summary:hover { color: var(--ink); }
details > summary::before {
  content: "";
  width: 6px;
  height: 6px;
  margin: 0 3px 0 2px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform 150ms ease-out;
  flex: none;
}
details:not(.more) > summary { display: inline-flex; align-items: center; gap: 8px; min-height: 32px; }
details[open] > summary::before { transform: rotate(45deg) translate(-1px, -1px); }
details.more[open] > summary { margin-bottom: 12px; }
@media (pointer: coarse) { details > summary, details.more > summary { min-height: 44px; } }

/* segmented control: toggle buttons with aria-pressed */
.segmented {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--border);
  border-radius: var(--r-2);
  background: var(--surface-sunken);
  flex: none;
}
.segmented > button, .segmented-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 34px;
  padding: 0 14px;
  border: 1px solid transparent;
  border-radius: 7px;
  background: transparent;
  color: var(--ink-muted);
  font: 500 var(--fs-ui)/1.2 var(--font-sans);
  cursor: pointer;
  transition: background-color 150ms ease-out, color 150ms ease-out, border-color 150ms ease-out, transform 120ms ease-out;
}
.segmented > button:hover { color: var(--ink); background: var(--hover); }
.segmented > button:active { transform: scale(0.97); }
.segmented > button[aria-pressed="true"] { background: var(--surface-raised); color: var(--ink); border-color: var(--border-strong); }
.segmented > .btn-approve[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent-text); border-color: var(--accent-line); }
.segmented > .btn-reject[aria-pressed="true"] { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-line); }
.segmented svg { width: 16px; height: 16px; }
@media (pointer: coarse) { .segmented > button { min-height: 44px; } }

/* ---------------------------------------------------------------- status and data */

/* the stamp: one status mark for verdicts, registry state, gates and variant status */
.stamp {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px calc(8px - 0.12em) 2px 8px;
  border: 1.5px solid currentColor;
  border-radius: var(--r-stamp);
  color: var(--ink-muted);
  font: 500 var(--fs-label)/1.4 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  vertical-align: middle;
}
/* below 2x a 1.5 px border snaps to one device pixel; give the stamp its full weight there */
@media (max-resolution: 1.99dppx) {
  .stamp { padding-block: 1.5px; border-width: 2px; }
}
.stamp-ok { color: var(--accent-text); }
.stamp-warn { color: var(--warn); }
.stamp-danger { color: var(--danger); }
.stamp-neutral { color: var(--ink-muted); }
.stamp-lg {
  padding: 8px calc(14px - 0.12em) 8px 14px;
  border-width: 2px;
  font-size: var(--fs-body);
  outline: 1px solid currentColor;
  outline-offset: -6px;
}

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)); gap: 12px; }
.stat {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: var(--r-2);
  background: var(--surface);
}
.card .stat { background: var(--surface-sunken); border-color: transparent; }
.stat-label { font: 500 var(--fs-label)/1.4 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-muted); }
.stat-value { font-size: var(--fs-stat); font-weight: 600; line-height: 1.1; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.stat-value .stamp { font-size: var(--fs-ui); }
.stat-note { font-size: var(--fs-sm); color: var(--ink-muted); }
.stat-warn .stat-value { color: var(--warn); }
.stat-danger .stat-value { color: var(--danger); }

/* score meter: fill to --value, tick at --mark (both 0..1 of the same scale) */
.meter { display: flex; flex-direction: column; gap: 8px; --value: 0; --mark: 0; }
.meter-track { position: relative; height: 10px; border-radius: 5px; background: var(--track); }
.meter-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--value) * 100%);
  border-radius: 5px;
  background: var(--accent);
}
.meter-below .meter-fill { background: var(--warn-fill); }
.meter-mark {
  position: absolute;
  top: -5px;
  bottom: -5px;
  left: calc(var(--mark) * 100%);
  width: 2px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--ink);
  box-shadow: 0 0 0 2px var(--surface); /* a gap where the mark crosses the fill */
}
.meter-legend { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 4px 16px; font-size: var(--fs-sm); color: var(--ink-muted); }
.meter-legend b { color: var(--ink); font-variant-numeric: tabular-nums; }

.bar {
  --value: 0;
  position: relative;
  display: inline-block;
  width: 96px;
  height: 6px;
  border-radius: 3px;
  background: var(--track);
  overflow: hidden;
  vertical-align: middle;
}
.bar::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--value) * 100%);
  min-width: min(6px, var(--value) * 100000px); /* a tiny nonzero value still shows a dot */
  border-radius: 3px;
  background: var(--accent);
}
.bar-muted::before { background: var(--ink-faint); }
.bar-warn::before { background: var(--warn-fill); }

/* tables; a table wider than its card scrolls sideways, with a soft shadow at the clipped
   edge (the covers scroll with the content and hide the shadow once an edge is reached) */
.table-wrap {
  overflow-x: auto;
  background:
    linear-gradient(to right, var(--surface) 40%, transparent) left / 28px 100% no-repeat local,
    linear-gradient(to left, var(--surface) 40%, transparent) right / 28px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, var(--scroll-shadow), transparent) left / 12px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, var(--scroll-shadow), transparent) right / 12px 100% no-repeat scroll;
}
table, .table { width: 100%; border-collapse: collapse; font-size: var(--fs-ui); }
th {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border-strong);
  color: var(--ink-muted);
  font: 500 var(--fs-label)/1.4 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
  vertical-align: bottom;
}
td { padding: 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
tbody tr:last-child td { border-bottom: none; }
th.num, td.num { text-align: right; }
td.num { font-variant-numeric: tabular-nums; }
th:first-child, td:first-child { padding-left: 0; }
th:last-child, td:last-child { padding-right: 0; }
.card-flush th:first-child, .card-flush td:first-child { padding-left: 24px; }
.card-flush th:last-child, .card-flush td:last-child { padding-right: 24px; }
td .bar { margin-right: 8px; }

.kv { display: grid; grid-template-columns: max-content minmax(0, 1fr); align-items: baseline; gap: 10px 24px; margin: 0; }
.kv dt { font: 500 var(--fs-label)/1.6 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-muted); }
.kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

/* notices: icon drawn in CSS, so plain notices from app.py and tapak.js get it too */
.notice {
  position: relative;
  padding: 12px 14px 12px 42px;
  border: 1px solid var(--border);
  border-radius: var(--r-2);
  background: var(--surface-sunken);
  color: var(--ink);
  font-size: var(--fs-ui);
  overflow-wrap: anywhere;
}
.notice::before {
  content: "i";
  content: "i" / "";
  position: absolute;
  left: 14px;
  top: 13px;
  width: 18px;
  height: 18px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--ink-muted);
  color: var(--surface);
  font: 600 11px/1 var(--font-mono);
}
.notice-error { background: var(--danger-soft); border-color: var(--danger-line); color: var(--danger); }
.notice-error::before { content: "!"; content: "!" / ""; background: var(--danger); }
.notice-error .link { color: inherit; text-decoration-color: currentColor; }
.notice-warn { background: var(--warn-soft); border-color: var(--warn-line); }
.notice-warn::before { content: "!"; content: "!" / ""; background: var(--warn); }
.notice-ok { background: var(--accent-soft); border-color: var(--accent-line); }
.notice-ok::before { background: var(--accent); content: ""; }
.notice-ok::after {
  content: "";
  position: absolute;
  left: 20px;
  top: 17px;
  width: 6px;
  height: 9px;
  border-right: 2px solid var(--on-accent);
  border-bottom: 2px solid var(--on-accent);
  transform: rotate(45deg);
}
.notice .notes { color: inherit; }
/* the merged manipulation warning: one sentence per line after the bold finding */
.notice .notice-part { display: block; margin-top: 6px; }
.notice .notice-rate { color: var(--ink-muted); }

/* stepper */
.stepper { list-style: none; margin: 0; padding: 0; display: flex; align-items: center; gap: 0; counter-reset: none; }
.stepper-item { display: flex; align-items: center; gap: 10px; flex: 1 1 auto; min-width: 0; color: var(--ink-muted); font-size: var(--fs-ui); }
.stepper-item:last-child { flex: none; }
.stepper-item:not(:last-child)::after { content: ""; flex: 1; min-width: 16px; height: 1px; margin: 0 12px 0 2px; background: var(--border-strong); }
.stepper-num {
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  flex: none;
  border: 1.5px solid var(--border-strong);
  border-radius: var(--r-stamp);
  font: 500 var(--fs-label)/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.stepper-label { white-space: nowrap; }
.stepper-item.is-done { color: var(--ink); }
.stepper-item.is-done .stepper-num { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.stepper-item.is-done::after { background: var(--accent-line); }
.stepper-item[aria-current="step"] { color: var(--ink); font-weight: 600; }
.stepper-item[aria-current="step"] .stepper-num { border-color: var(--accent); color: var(--accent-text); }
/* a step that does not apply to this document (step 3 of a master whose pages are all scans):
   a dashed ring with a dash, the label muted with a short tag; progress passes it */
.stepper-item.is-na .stepper-num { border-style: dashed; color: var(--ink-muted); }
.stepper-item.is-na::after { background: var(--accent-line); }
.stepper-na { margin-left: 8px; font: 500 var(--fs-label)/1 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-muted); }
@container main (max-width: 620px) {
  .stepper-item:not([aria-current="step"]) .stepper-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }
  .stepper-item:not(:last-child)::after { margin: 0 8px; }
}

/* document page thumbnail: real page 1 on paper, in both themes */
.thumb {
  --thumb-w: 120px;
  display: block;
  flex: none;
  width: var(--thumb-w);
  aspect-ratio: 210 / 297;
  margin: 0;
  overflow: hidden;
  border-radius: 4px;
  background: oklch(0.99 0.004 90);
  outline: 1px solid var(--thumb-outline);
  outline-offset: -1px;
}
.thumb img, img.thumb { width: 100%; height: 100%; object-fit: cover; object-position: top; filter: var(--thumb-filter); }
img.thumb { height: auto; }
.thumb-sm { --thumb-w: 44px; border-radius: 3px; }
.thumb-lg { --thumb-w: 200px; }

.list { list-style: none; margin: 0; padding: 0; }
.list-row { display: flex; align-items: center; gap: 16px; padding: 14px 0; border-top: 1px solid var(--border); min-width: 0; }
.list-row:first-child { border-top: 0; padding-top: 0; }
.list-row:last-child { padding-bottom: 0; }
.list-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* a long name wraps: a cut name could only be read from a tooltip, out of reach of keyboard and touch */
.list-title { font-weight: 500; overflow-wrap: anywhere; }
.list-meta { font-size: var(--fs-sm); color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.list-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; flex: none; }
.list-more { margin-top: 16px; }

mark.diff {
  padding: 0 2px;
  border-radius: 2px;
  background: var(--accent-soft);
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--accent-line);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

.code-row { display: flex; align-items: flex-start; gap: 12px; }
.code {
  flex: 1;
  min-width: 0;
  padding: 10px 12px;
  border-radius: var(--r-1);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  font: 400 var(--fs-sm)/1.5 var(--font-mono);
  overflow-wrap: anywhere;
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  padding: 24px;
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--r-3);
}
.empty-state > h2, .empty-state > h3 { font-size: var(--fs-body); }
.empty-state > p { color: var(--ink-muted); max-width: 56ch; }
.empty-state > .row, .empty-state > .btn { margin-top: 4px; }
.empty { color: var(--ink-muted); font-size: var(--fs-ui); padding: 8px 0; }

/* busy states */
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { display: inline-flex; }
.htmx-request .busy.htmx-indicator, .htmx-request.busy.htmx-indicator { display: flex; }
.htmx-request .idle-label { display: none; }
.spinner {
  width: 14px;
  height: 14px;
  flex: none;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
.busy { flex-wrap: wrap; align-items: center; gap: 8px 12px; font-size: var(--fs-ui); color: var(--ink-muted); }
/* a long line (scanned pages) goes under the bar where it would be squeezed beside it */
.busy > span:not(.busy-bar) { flex: 1 1 16rem; min-width: 0; }
.busy-bar { position: relative; flex: 0 0 120px; height: 4px; border-radius: 2px; background: var(--track); overflow: hidden; }
.busy-bar::after {
  content: "";
  position: absolute;
  inset: 0;
  width: 40%;
  border-radius: 2px;
  background: var(--accent);
  animation: busy 1.1s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes busy { from { transform: translateX(-100%); } to { transform: translateX(260%); } }

/* ---------------------------------------------------------------- Beranda hero */

.hero {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: center;
  min-height: 340px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--r-3);
  background: var(--surface-sunken);
}
.hero-art { position: absolute; inset: 0; z-index: -1; }
.hero-art img { width: 100%; height: 100%; object-fit: cover; object-position: 78% 50%; }
.hero-body { display: flex; flex-direction: column; gap: 14px; max-width: 36rem; padding: 40px; }
.hero h1 { font-size: var(--fs-display); line-height: 1.1; letter-spacing: -0.02em; }
.hero-body > p:not(.eyebrow) { font-size: var(--fs-lead); color: var(--ink-muted); max-width: 42ch; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; }
@container main (max-width: 720px) {
  .hero { flex-direction: column; align-items: stretch; min-height: 0; }
  .hero-art { position: relative; inset: auto; z-index: auto; aspect-ratio: 2 / 1; border-bottom: 1px solid var(--border); }
  .hero-art img { object-position: 92% 45%; }
  .hero-body { padding: 24px; max-width: none; }
  .hero h1 { font-size: var(--fs-h1); }
  .hero-actions .btn { flex: 1 1 auto; }
}

/* ---------------------------------------------------------------- verdict, notes, review list */

.verdict { display: flex; flex-direction: column; gap: 12px; padding: 24px; border: 1px solid var(--border); border-radius: var(--r-3); background: var(--surface); }
.verdict-found { border-color: var(--accent-line); }
.verdict-none { border-color: var(--warn-line); }
.verdict .who { display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: baseline; }
.verdict .facts { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 24px; font-size: var(--fs-ui); }
.verdict .facts b { font-variant-numeric: tabular-nums; }
.notes { margin: 0; padding-left: 18px; color: var(--ink-muted); font-size: var(--fs-ui); display: flex; flex-direction: column; gap: 4px; }
.notes li { overflow-wrap: anywhere; min-width: 0; }
/* htmx replaces #semantik on every review decision; without this, Chromium's scroll anchoring
   jumps the page to the bottom while the old content is removed. */
#semantik { overflow-anchor: none; }

/* ---------------------------------------------------------------- responsive shell */

@media (max-width: 1279px) {
  :root { --gutter: 40px; }
}

@media (max-width: 1023px) {
  :root { --gutter: 32px; }
  /* top bar above the content; the footer still sits at the bottom of a short page */
  .shell { display: flex; flex-direction: column; background: none; }
  .main-col { flex: 1; }
  .sidebar {
    background: var(--sidebar);
    flex-direction: row;
    align-items: center;
    gap: 24px;
    padding: 10px var(--gutter);
    border-bottom: 1px solid var(--border);
  }
  /* 44 px to tap, laid out as 30 and in place; the ring is drawn inside the box, so it clears
     the banner and the nav row the box reaches into */
  .brand { padding: 0 6px; min-height: 44px; margin: -7px -6px; }
  .brand:focus-visible { outline-offset: -2px; }
  .brand-sub, .sidebar-status { display: none; }
  .nav { margin-left: auto; }
  .nav ul { flex-direction: row; gap: 4px; }
  .nav a { gap: 8px; }
  main { padding-top: 32px; }
}

@media (max-width: 639px) {
  :root { --gutter: 16px; --fs-h1: 24px; --fs-stat: 24px; }
  .sidebar { flex-direction: column; align-items: stretch; gap: 8px; padding: 12px var(--gutter) 6px; }
  .nav { margin-left: 0; }
  .nav ul { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 4px; }
  .nav a { flex-direction: column; justify-content: center; gap: 3px; min-height: 52px; padding: 4px 2px; font-size: var(--fs-label); }
  main { padding-top: 24px; padding-bottom: 48px; }
  .page-head { grid-template-columns: minmax(0, 1fr); margin-bottom: 24px; }
  .page-head > .page-actions { grid-column: 1; grid-row: auto; }
  .page { gap: 24px; }
  .card { padding: 18px; }
  .card-flush { padding: 0; }
  .stat { padding: 14px; }
  .card-flush > .card-head, .card-section { padding: 16px 18px; }
  .card-flush th:first-child, .card-flush td:first-child { padding-left: 18px; }
  .card-flush th:last-child, .card-flush td:last-child { padding-right: 18px; }
  .hide-sm { display: none; }
  .dropzone { min-height: 96px; padding: 16px; }
  .kv { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .kv dd + dt { margin-top: 10px; }
  /* tables fit a phone screen: tighter cells, wrapping headers and stamps */
  th, td { padding-left: 6px; padding-right: 6px; }
  th { letter-spacing: 0.04em; }
  th.num { white-space: normal; }
  td .stamp { white-space: normal; }
  .list-row { flex-wrap: wrap; }
  .list-actions { width: 100%; justify-content: flex-start; padding-left: 60px; }
}

@media (max-width: 360px) {
  .card { padding: 14px; }
  .card-flush { padding: 0; }
  th { font-size: 11px; letter-spacing: 0; }
}

/* ---------------------------------------------------------------- motion
   One-shot entrances for what the user just asked for. tapak.js marks a swapped verdict or copy
   list .is-new and a newly finished step .just-done, so nothing here plays on a plain page load. */

/* the verdict lands: its stamp, then the score runs out past (or short of) the threshold tick */
.verdict.is-new .stamp-lg { animation: stamp-in 320ms var(--ease-enter) 120ms both; }
.verdict.is-new .meter-mark { animation: fade-in 200ms ease-out 180ms both; }
.verdict.is-new .meter-fill { transform-origin: left center; animation: grow-x 760ms var(--ease-run) 280ms both; }
.verdict.is-new .who + .who .meter-fill { animation-delay: 400ms; }
.verdict.is-new .facts > :nth-child(2) { animation: rise-in 260ms var(--ease-enter) 560ms both; }
.verdict.is-new .facts > :nth-child(3) { animation: rise-in 260ms var(--ease-enter) 620ms both; }

/* issued copies are dealt into the list one after another */
.copies.is-new tbody tr { animation: rise-in 240ms var(--ease-enter) both; animation-delay: calc(120ms + min(var(--i, 0), 24) * 18ms); }

/* a step just finished: its check pops and its connector fills toward the next step */
.stepper-item.just-done .stepper-num { animation: pop-in 260ms var(--ease-enter) both; }
.stepper-item.just-done::after { transform-origin: left center; animation: grow-x 420ms cubic-bezier(0.65, 0, 0.35, 1) 120ms both; }

/* a field's message, and a file just chosen, arrive where they are read */
.field-error { animation: drop-in 180ms var(--ease-out) both; }
.dropzone-files.is-new li { animation: rise-in 200ms var(--ease-enter) both; }
.dropzone-files.is-new li:nth-child(2) { animation-delay: 40ms; }
.dropzone-files.is-new li:nth-child(n + 3) { animation-delay: 80ms; }

@keyframes stamp-in { from { opacity: 0; transform: scale(1.14) rotate(-4deg); } }
@keyframes grow-x { from { transform: scaleX(0); } }
@keyframes fade-in { from { opacity: 0; } }
@keyframes rise-in { from { opacity: 0; transform: translateY(8px); } }
@keyframes drop-in { from { opacity: 0; transform: translateY(-4px); } }
@keyframes pop-in { from { opacity: 0; transform: scale(0.6); } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
  .btn:active, .copy-btn:active, .segmented > button:active { transform: none !important; }
  .top-progress::after, .busy-bar::after { width: 100%; transform: none; opacity: 0.6; }
}

/* the sidebar's way back to the product page (/), and the language switch */
.sidebar-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; margin-top: 12px; }
.sidebar-foot .link { color: var(--ink-muted); }

/* ID | EN: the current language is filled */
.lang-switch { display: inline-flex; padding: 3px; gap: 2px; border-radius: 999px; box-shadow: inset 0 0 0 1px var(--border-strong); }
.lang-switch a { display: inline-grid; place-items: center; min-width: 36px; min-height: 28px; padding-inline: 8px; border-radius: 999px;
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: 0.06em; color: var(--ink-muted); text-decoration: none;
  transition: color 150ms ease, background-color 150ms ease; }
.lang-switch a[aria-current] { background: var(--ink); color: var(--paper); }
@media (hover: hover) and (pointer: fine) { .lang-switch a:not([aria-current]):hover { color: var(--ink); background: var(--hover); } }
@media (pointer: coarse) { .lang-switch a { min-height: 44px; min-width: 44px; } }
