/* Clean admin theme, adapted from docs/moudi-license-commerce-final.html
   ("clean-theme" light palette) to the actual BEM-ish class names used by
   templates/base.html and the Release / Reporting / Update Campaign
   templates. No framework, no JS-driven theme — same rule as before
   (master prompt §53); only the visual language changed. */

:root {
  --bg: #f6f7f9;
  --panel: #ffffff;
  --panel2: #f3f5f7;
  --line: #e4e7ec;
  --text: #101828;
  --muted: #667085;
  --primary: #2563eb;
  --primary2: #1d4ed8;
  --danger: #d92d20;
  --success: #16a36a;
  --warn: #b7791f;
  --info: #087ea4;
  --radius: 12px;
  --radius-sm: 9px;
  --shadow: 0 1px 2px rgba(16, 24, 40, .03), 0 8px 24px rgba(16, 24, 40, .05);

  /* Back-compat aliases: pre-existing class names below still reference the
     original token names, so both point at the same clean-theme values. */
  --color-bg: var(--bg);
  --color-surface: var(--panel);
  --color-text: var(--text);
  --color-muted: var(--muted);
  --color-border: var(--line);
  --color-primary: var(--primary);
  --color-danger: var(--danger);
  --color-success: var(--success);
}

* { box-sizing: border-box; }
html { color-scheme: light; }

/* Bugfix 2026-08-18: any component rule below that sets its own `display`
   on a class (e.g. `.field { display: flex; }`, `.version-info__latest
   { display: flex; }`) has equal CSS specificity to the browser's built-in
   `[hidden] { display: none }` UA rule — and author rules always beat UA
   rules at equal specificity, regardless of source order. So an element
   like `<div class="field" id="release-type-field" hidden>` rendered its
   `hidden` attribute correctly into the HTML, and JS toggled it correctly,
   but stayed visually *visible* the whole time: this is the CSS half of
   why the Step 1 Jenis Rilis picker kept showing even once the server- and
   JS-side FIRST_RELEASE logic were both already correct. `!important` here
   is deliberate and standard practice for this exact guarantee (the same
   pattern normalize.css/Bootstrap ship) — `[hidden]` must always win. */
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.5;
  font-size: 13px;
}

a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; }

/* ---------------------------------------------------------------------- */
/* App shell: sidebar + topbar                                            */
/* ---------------------------------------------------------------------- */

.app { display: grid; grid-template-columns: 264px 1fr; min-height: 100vh; }

.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: auto;
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: 18px 12px;
}

.brand { display: flex; align-items: center; gap: 10px; padding: 4px 10px 22px; }
.brandmark {
  width: 38px; height: 38px; border-radius: 10px;
  background: var(--text); color: #fff;
  display: grid; place-items: center; font-size: 15px; font-weight: 800;
  flex-shrink: 0;
}
.brand b { display: block; font-size: 14px; color: var(--text); letter-spacing: -.02em; }
.brand small { display: block; font-size: 9px; color: #98a2b3; margin-top: 3px; }

.navgroup { margin: 5px 0 14px; }
.navlabel {
  padding: 9px 11px 5px; color: #98a2b3; font-size: 9px;
  font-weight: 800; letter-spacing: .9px; text-transform: uppercase;
}

.nav a, .nav .nav-disabled {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px; border-radius: var(--radius-sm);
  margin: 1px 0; font-size: 11px; font-weight: 600;
  color: #475467;
}
.nav a:hover { background: #f8fafc; color: var(--text); }
.nav a.active { background: #eff6ff; color: #175cd3; box-shadow: inset 3px 0 0 var(--primary); }
.nav .ico {
  width: 20px; height: 20px; flex: 0 0 20px;
  display: inline-flex; align-items: center; justify-content: center;
  color: #98a2b3;
}
.nav .ico svg { width: 18px; height: 18px; }
.nav a.active .ico { color: var(--primary); }
/* Kotak Pos: one icon of its own colour so it is found at a glance (owner,
   21 Sep 2026). `--info` teal from the existing palette — distinct from the
   primary blue, and unlike red/amber it carries no danger/warning meaning. */
.nav .ico--kotak-pos, .nav a.active .ico--kotak-pos { color: var(--info); }
.nav .nav-disabled { color: #c0c5d1; cursor: not-allowed; justify-content: space-between; }
.nav .nav-disabled .ico { opacity: .55; }
.nav .nav-disabled .soon {
  font-size: 8px; font-weight: 800; letter-spacing: .4px; text-transform: uppercase;
  background: var(--panel2); color: #98a2b3; border-radius: 999px; padding: 2px 6px;
}

.sidebar-foot {
  margin-top: 14px; padding: 11px; border: 1px solid #eaecf0; border-radius: 10px;
  background: #f9fafb; color: var(--muted); font-size: 9px; line-height: 1.55;
}

.main { min-width: 0; background: var(--bg); }

.topbar {
  height: 72px; position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 26px; background: rgba(255, 255, 255, .93);
  border-bottom: 1px solid var(--line); backdrop-filter: blur(14px);
}
.topbar h1 { font-size: 17px; margin: 0; color: var(--text); font-weight: 750; letter-spacing: -.02em; }
.topbar p { margin: 3px 0 0; color: var(--muted); font-size: 10px; }
.top-actions { display: flex; align-items: center; gap: 10px; }
.top-actions form { margin: 0; }
/* The panel-wide "Semua waktu WIB" note (owner audit, 25 Aug 2026, point 2)
   — stated once here in the topbar, not repeated next to every formatted
   timestamp. Reuses `.hint`'s muted small-text look; only the inherited
   margin-top needs zeroing to sit level with the pill/logout button beside
   it in this flex row. */
.top-actions__tz-note { margin: 0; white-space: nowrap; }

.pill {
  display: inline-flex; align-items: center; padding: 6px 10px;
  border: 1px solid var(--line); border-radius: 999px;
  background: #fff; color: var(--muted); font-size: 11px;
}
.pill .dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: #12b76a; margin-right: 6px; }

.content { padding: 26px; max-width: 1480px; margin: 0 auto; }

/* ---------------------------------------------------------------------- */
/* Cards, tables, badges                                                  */
/* ---------------------------------------------------------------------- */

.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 20px 22px; margin-bottom: 16px;
}
.card--narrow { max-width: 420px; margin: 3rem auto; }
.card h1 { font-size: 19px; margin: 0 0 6px; letter-spacing: -.02em; }
.card h2 { font-size: 14px; margin: 20px 0 10px; color: var(--text); }
.card p { color: var(--muted); font-size: 12.5px; line-height: 1.65; }

.card__header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-bottom: 14px;
}
.card__header h1 { margin: 0; }

.table-wrap { overflow-x: auto; }
table.table, table { border-collapse: collapse; width: 100%; font-size: 12.5px; }
.table th, .table td, table th, table td {
  padding: 11px 13px; border-bottom: 1px solid #eaecf0; text-align: left;
}
.table th, table th { background: #f9fafb; color: var(--muted); font-size: 10.5px; font-weight: 700; }
.table td, table td { color: #344054; }
.table tbody tr:hover, table tbody tr:hover { background: #fcfcfd; }
.table code, table code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px;
  background: #f9fafb; border-radius: 4px; padding: 1px 4px;
}

.badge {
  display: inline-flex; align-items: center; padding: 4px 8px;
  border-radius: 999px; font-size: 10px; font-weight: 750;
  background: var(--panel2); color: var(--muted);
}
.badge--draft, .badge--uploaded { background: #eff8ff; color: #175cd3; }
.badge--verifying { background: #fffaeb; color: #93370d; }
.badge--verified, .badge--ready { background: #eff8ff; color: #175cd3; }
.badge--published { background: #ecfdf3; color: #067647; }
.badge--deprecated, .badge--withdrawn { background: #fef3f2; color: #b42318; }
/* "Testing Release" (Release Flow audit, 2026-08-19) — deliberately distinct
   from every status color above so a testing release is never mistaken for
   a production one at a glance. */
.badge--testing { background: #f4f3ff; color: #5925dc; }

/* ---------------------------------------------------------------------- */
/* Buttons, forms, alerts                                                 */
/* ---------------------------------------------------------------------- */

.button, .link-button {
  display: inline-flex; align-items: center; gap: 6px;
  border-radius: var(--radius-sm); padding: 9px 14px;
  background: var(--primary); color: #fff; border: 1px solid var(--primary);
  font-weight: 700; font-size: 12px; cursor: pointer; transition: .15s ease;
}
.button:hover { background: var(--primary2); border-color: var(--primary2); }
.button--primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.button--secondary { background: var(--text); border-color: var(--text); color: #fff; }
.button--danger, .button.danger { background: #fff; border-color: #fecdca; color: #b42318; }
.button--danger:hover { background: #fef3f2; }
.button.ghost, .button.secondary.small { background: #fff; border: 1px solid #d0d5dd; color: #344054; }
.button.ghost:hover { background: #f9fafb; border-color: #98a2b3; }
.button.small { padding: 6px 10px; font-size: 10.5px; }

.link-button {
  background: none; border: none; color: var(--primary); padding: 0;
  box-shadow: none; font-weight: 600;
}
.link-button:hover { background: none; color: var(--primary2); text-decoration: underline; }

/* ---------------------------------------------------------------------- */
/* Confirmation modal — Hapus/Nonaktifkan (Produk & SKU UX fix, 2026-08-21) */
/* A native <dialog>, wired by static/js/confirm_modal.js. Chosen over a
   raw `confirm()`/`prompt()` (the pattern release_list.js already uses for
   simpler one-line confirmations) because a destructive delete needs to
   show *why* it is refused — a dependency list — which a browser dialog
   cannot render at all. `showModal()` renders in the browser's top layer,
   so this is never affected by a card/table's own overflow or z-index. */
/* ---------------------------------------------------------------------- */

dialog.confirm-modal {
  border: none; border-radius: var(--radius); padding: 0;
  width: min(440px, calc(100vw - 32px));
  box-shadow: var(--shadow); background: var(--panel); color: var(--text);
}
dialog.confirm-modal::backdrop { background: rgba(16, 24, 40, .45); }
.confirm-modal__body { padding: 20px 22px; }
.confirm-modal__body h3 { margin: 0 0 8px; font-size: 15px; color: var(--text); }
.confirm-modal__body p { margin: 0 0 12px; font-size: 12.5px; color: var(--muted); line-height: 1.6; }
.confirm-modal__reasons { margin: 0 0 12px; padding-left: 18px; font-size: 12px; color: var(--text); }
.confirm-modal__reasons li { margin-bottom: 4px; }
.confirm-modal__actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; flex-wrap: wrap; }

.actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }
.action-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.action-form label { font-size: 11px; color: var(--muted); font-weight: 650; }

.field { margin-bottom: 14px; display: flex; flex-direction: column; gap: 6px; max-width: 460px; }
.field label { font-weight: 650; font-size: 11px; color: #475467; }
.field input, .field select, .field textarea,
form p input, form p select, form p textarea,
input[type=text], input[type=date], input[type=email], input[type=password],
input[type=number], select, textarea {
  width: 100%; border: 1px solid #d0d5dd; background: #fff; color: var(--text);
  border-radius: var(--radius-sm); padding: 9px 11px; font-size: 12.5px;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .03);
}
.field input:focus, .field select:focus, .field textarea:focus,
input:focus, select:focus, textarea:focus {
  outline: 0; border-color: #84adff; box-shadow: 0 0 0 3px rgba(37, 99, 235, .08);
}
.field textarea { min-height: 90px; resize: vertical; }
.field__error, .errorlist { color: var(--danger); font-size: 11px; margin: 2px 0 0; list-style: none; padding: 0; }
.field__hint, .hint { font-size: 11px; color: #98a2b3; margin: 4px 0 0; }
form p { margin: 0 0 12px; }
form p label { display: block; font-weight: 650; font-size: 11px; color: #475467; margin-bottom: 4px; }

.alert { padding: 12px 14px; border-radius: var(--radius-sm); margin-bottom: 14px; font-size: 12px; }
.alert--error { background: #fef3f2; color: var(--danger); border: 1px solid #fecdca; }
.alert--info { background: #eff8ff; color: #175cd3; border: 1px solid #b2ddff; }

.table-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

.messages { list-style: none; padding: 0; margin: 14px 26px 0; }
.messages__item { padding: 10px 14px; border-radius: var(--radius-sm); margin-bottom: 8px; font-size: 12.5px; }
.messages__item--error { background: #fef3f2; color: var(--danger); border: 1px solid #fecdca; }
.messages__item--success { background: #ecfdf3; color: var(--success); border: 1px solid #abefc6; }
.messages__item--warning { background: #fffaeb; color: var(--warn); border: 1px solid #fedf89; }
.messages__item--info { background: #eff8ff; color: var(--info); border: 1px solid #b2ddff; }

.muted { color: var(--muted); }

/* dl.review — key/value detail panels used on release/campaign detail */
dl.review { display: grid; grid-template-columns: 220px 1fr; gap: 6px 14px; margin: 14px 0; }
dl.review dt { color: var(--muted); font-size: 11px; }
dl.review dd { margin: 0; color: #344054; font-size: 12.5px; }
.review__changelog {
  white-space: pre-wrap; background: #f9fafb; border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: 12px 14px; font-size: 12px; color: #344054;
}
.review__denied { color: var(--danger); }
.review__note { color: var(--muted); font-size: 11px; }
.review__verification-summary { font-size: 13px; margin: 0 0 12px; color: #067647; }
.review__verification-summary--incomplete { color: var(--danger); }

/* subnav (reporting), filters, pagination, wizard-nav, verification */
.subnav { display: flex; gap: 6px; flex-wrap: wrap; margin: 4px 0 18px; border-bottom: 1px solid #eaecf0; padding-bottom: 8px; }
.subnav a { padding: 7px 10px; border-radius: var(--radius-sm); font-size: 11px; font-weight: 650; color: var(--muted); }
.subnav a:hover { background: #f8fafc; color: var(--text); }
.subnav a.active { background: #eff6ff; color: #175cd3; }

.filters { display: flex; align-items: end; gap: 10px; flex-wrap: wrap; margin-bottom: 18px; }
.filters label { font-size: 11px; color: var(--muted); font-weight: 650; }
.filters input, .filters select { width: auto; }

.pagination { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-top: 12px; font-size: 11.5px; color: var(--muted); }
.pagination a { color: var(--primary); font-weight: 650; }
/* Shared pagination component (pagination-uniformity audit, 24 Aug 2026,
   templates/common/_pagination.html) — the Previous/Next controls reuse
   the existing .button.ghost.small look everywhere in the panel, so this
   only adds the bits that look are missing: the summary text and the
   disabled (first/last page) state. */
.pagination__nav { display: flex; align-items: center; gap: 8px; }
.pagination__pages { white-space: nowrap; }
.pagination__nav-disabled { opacity: .4; pointer-events: none; }
/* Page-number links (owner audit, 25 Aug 2026, point 1) — replaces the old
   plain "Halaman X / Y" text with real, clickable page numbers whenever
   there's more than one page; `page_range` (apps/common/templatetags/
   ui.py) elides a long run down to "1 2 3 … 15 16" so this never turns
   into one link per page on a big list. */
.pagination__numbers { display: flex; align-items: center; gap: 2px; }
.pagination__page {
  min-width: 24px; height: 24px; padding: 0 5px; border-radius: var(--radius-sm);
  display: inline-flex; align-items: center; justify-content: center; font-weight: 650;
}
.pagination__page:not(.pagination__page--current):hover { background: #f8fafc; }
.pagination__page--current { background: var(--primary); color: #fff; }
.pagination__ellipsis { padding: 0 3px; }

/* .wizard-nav / .wizard-nav__step / .wizard-nav__number: see the "Wizard
   layout" component further down — the vertical step-rail definition there
   is the one in effect. */

.verification { border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; margin: 14px 0; background: #fcfcfd; }
.verification--published, .verification--verified, .verification--ready { border-color: #abefc6; background: #f6fffb; }
.verification--verifying { border-color: #fedf89; background: #fffdf6; }
.verification__state { margin: 0 0 8px; font-size: 12.5px; }
.verification__detail { display: grid; grid-template-columns: 160px 1fr; gap: 4px 12px; margin: 8px 0; font-size: 11.5px; }
.verification__detail dt { color: var(--muted); }
.verification__detail dd { margin: 0; color: #344054; }
.verification__error { color: var(--danger); font-size: 11.5px; margin: 8px 0 0; }
.verification__hint, .verification__empty { color: var(--muted); font-size: 11.5px; margin: 6px 0 0; }

/* Step 2 — Upload APK: file info, progress, checklist, attempt history
   (Release Management UX pass, 2026-08-18, spec §F/§I/§P). */
.apk-file-info { font-size: 12.5px; margin: 10px 0; color: var(--text); }
.apk-file-info b { word-break: break-all; }
.apk-file-info span { color: var(--muted); }

.apk-upload-progress { margin: 12px 0; }
.apk-upload-progress__status { font-size: 12.5px; margin: 0 0 6px; font-weight: 600; }
.apk-progress-bar { height: 10px; border-radius: 999px; background: var(--panel2); overflow: hidden; border: 1px solid var(--line); }
.apk-progress-bar__fill { height: 100%; width: 0%; background: var(--primary); transition: width .15s ease; }
.apk-upload-progress__detail { font-size: 11.5px; color: var(--muted); margin: 6px 0 0; }
.apk-upload-progress--done .apk-progress-bar__fill { background: var(--success); }
.apk-upload-progress--error .apk-upload-progress__status { color: var(--danger); }

.verification-headline { font-size: 13.5px; font-weight: 600; margin: 0 0 10px; }
.verification-headline--ok { color: #067647; }
.verification-headline--fail { color: var(--danger); }

.verification-checklist { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 6px; }
.verification-checklist li { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; }
.verification-checklist__mark { width: 16px; flex: none; font-weight: 700; text-align: center; }
.verification-checklist--ok .verification-checklist__mark { color: #067647; }
.verification-checklist--failed .verification-checklist__mark { color: var(--danger); }
.verification-checklist--pending .verification-checklist__mark, .verification-checklist--skipped .verification-checklist__mark { color: var(--muted); }
.verification-checklist--checking .verification-checklist__mark { color: var(--primary); }
.verification-checklist__detail { color: var(--muted); font-size: 11px; }

.verification-config-link { margin: 0 0 12px; font-size: 12px; }

.verification-timing { margin: 0 0 12px; font-size: 11.5px; color: var(--muted); }
.verification-expected-wait { margin: 0 0 12px; font-size: 11.5px; color: var(--muted); }

/* UX + observability pass (2026-08-19), spec §7 — "this looks stuck"
   warning + its two recovery buttons, past STUCK_AFTER_SECONDS. Same amber
   palette as `.verification--verifying`, one step more insistent. */
.verification-stuck {
  border: 1px solid #fedf89; background: #fffaf0; border-radius: var(--radius-sm);
  padding: 10px 12px; margin: 0 0 14px;
}
.verification-stuck__text { margin: 0 0 8px; font-size: 12.5px; color: #93500a; }
.verification-stuck__actions { display: flex; gap: 8px; flex-wrap: wrap; }
.verification-stuck__actions form { margin: 0; }

/* 53-minute-stuck bugfix (2026-08-19), spec item 2 — past
   TIMEOUT_AFTER_SECONDS with no progress at all, one step more insistent
   again: the same danger red as a genuine FAILED result
   (`.verification-headline--fail`), not the softer amber "still just
   slow" warning above. */
.verification-stuck--timeout {
  border-color: #fda29b; background: #fef3f2;
}
.verification-stuck--timeout .verification-stuck__text { color: var(--danger); }

/* Release verification blueprint check (2026-08-18), §2 — one card per
   critical check (Package ID / Version Code / SHA-256 / Signing Identity),
   each showing its own actual value alongside the status glyph. */
.verification-cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 10px; margin: 0 0 14px;
}
.verification-card {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 10px 12px; background: var(--panel);
}
.verification-card__head {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 11.5px; font-weight: 650; color: var(--muted);
  text-transform: uppercase; letter-spacing: .03em; margin-bottom: 6px;
}
.verification-card__mark { font-size: 13px; }
.verification-card--ok .verification-card__mark { color: #067647; }
.verification-card--failed .verification-card__mark { color: var(--danger); }
.verification-card--checking .verification-card__mark { color: var(--primary); }
.verification-card--pending .verification-card__mark, .verification-card--skipped .verification-card__mark { color: var(--muted); }
.verification-card--failed { border-color: #fecdca; background: #fef3f2; }
.verification-card--ok { border-color: #b8dfcb; }
.verification-card__value {
  display: block; font-size: 11px; color: var(--text); word-break: break-all;
  background: var(--panel2); border-radius: 6px; padding: 4px 6px; margin: 0 0 6px;
}
.verification-card__status { margin: 0; font-size: 12px; font-weight: 600; color: var(--text); }
.verification-card--failed .verification-card__status { color: var(--danger); }
.verification-card--ok .verification-card__status { color: #067647; }
.verification-card--checking .verification-card__status { color: var(--primary); }
.verification-card__detail { display: block; font-weight: 400; color: var(--muted); font-size: 11px; margin-top: 2px; }

.tech-detail { margin: 8px 0 0; font-size: 11.5px; }
.tech-detail summary { cursor: pointer; color: var(--muted); }
.tech-detail code { display: block; margin-top: 6px; padding: 8px 10px; background: var(--panel2); border-radius: var(--radius-sm); word-break: break-all; color: #344054; }

.apk-active-card { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 10px 12px; margin: 10px 0; background: var(--panel); }
.apk-active-card__label { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); display: block; margin-bottom: 4px; }

.apk-history { margin: 14px 0 0; font-size: 12px; }
.apk-history summary { cursor: pointer; color: var(--muted); }
.apk-history table { width: 100%; border-collapse: collapse; margin-top: 8px; font-size: 11.5px; }
.apk-history th, .apk-history td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line); }
.apk-history th { color: var(--muted); font-weight: 500; }

.finding { }
.finding--critical { background: #fef3f2; }
.finding--warning { background: #fffaeb; }

/* Overview (dashboard) navigation cards */
.overview-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; margin-top: 16px; }
.overview-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 18px 20px; display: flex; flex-direction: column; gap: 10px;
}
.overview-card__icon {
  width: 36px; height: 36px; border-radius: 10px; background: #eff6ff; color: #175cd3;
  display: grid; place-items: center; font-size: 16px;
}
.overview-card h3 { margin: 0; font-size: 13.5px; color: var(--text); }
.overview-card p { margin: 0; color: var(--muted); font-size: 11.5px; line-height: 1.6; flex: 1; }
.overview-card--disabled { opacity: .6; }
.overview-card--disabled .overview-card__icon { background: var(--panel2); color: #98a2b3; }

/* Auth pages (login / MFA / reauth / 403) — standalone, no sidebar.
   Redesigned 2026-08-21 (login page polish request): true viewport
   centering (both axes) plus a branded, elevated card instead of the flat
   grey backdrop — `.card--narrow` here is exclusively the auth-family
   card (grep confirms no other template uses it), so this upgrade lands
   consistently on login/2FA/reauth/403 with no template changes needed
   beyond login.html's own copy. */
.auth-shell {
  min-height: 100vh; width: 100%; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 26px; padding: 32px 24px;
  background:
    radial-gradient(760px 420px at 12% -8%, rgba(37, 99, 235, .12), transparent 60%),
    radial-gradient(620px 360px at 108% 8%, rgba(29, 78, 216, .10), transparent 55%),
    var(--bg);
  box-sizing: border-box;
}
.auth-brand { display: flex; align-items: center; gap: 12px; }
.auth-brand .brandmark {
  width: 46px; height: 46px; font-size: 17px; border-radius: 13px;
  background: linear-gradient(155deg, #1d4ed8, #2563eb 55%, #3b82f6);
  box-shadow: 0 6px 16px rgba(29, 78, 216, .28);
}
.auth-brand b { display: block; font-size: 15.5px; color: var(--text); letter-spacing: -.01em; }
.auth-brand small { display: block; font-size: 10px; color: #98a2b3; margin-top: 2px; letter-spacing: .01em; }

.auth-shell .messages { width: 100%; max-width: 440px; margin: 0; }

.auth-shell .card--narrow {
  width: 100%; max-width: 440px; margin: 0; padding: 34px 36px 30px;
  border-radius: 18px; box-shadow: 0 2px 4px rgba(16, 24, 40, .04), 0 20px 48px -12px rgba(16, 24, 40, .16);
  position: relative; overflow: hidden;
}
.auth-shell .card--narrow::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
  background: linear-gradient(90deg, #1d4ed8, #3b82f6 50%, #60a5fa);
}
.auth-card__eyebrow {
  display: inline-block; font-size: 9.5px; font-weight: 800; letter-spacing: 1.1px;
  text-transform: uppercase; color: #175cd3; margin-bottom: 8px;
}
.auth-shell .card--narrow h1 { font-size: 21px; margin: 0 0 6px; }
.auth-card__subtitle { color: var(--muted); font-size: 12.5px; line-height: 1.6; margin: 0 0 24px; }
.auth-shell .card--narrow form { margin-top: 4px; }
.auth-shell .card--narrow .field { max-width: none; margin-bottom: 18px; }
.auth-shell .card--narrow .field input {
  padding: 11px 13px; font-size: 13px;
}
.auth-shell .card--narrow .button {
  width: 100%; justify-content: center; padding: 11px 14px; font-size: 13px; font-weight: 700;
  margin-top: 4px;
}
.auth-footer { text-align: center; color: #98a2b3; font-size: 10.5px; max-width: 440px; }
.auth-footer a { color: #175cd3; font-weight: 650; }

/* ---------------------------------------------------------------------- */
/* Page header (hero) — kicker + title + description + primary action     */
/* ---------------------------------------------------------------------- */

.page-header {
  display: flex; justify-content: space-between; align-items: center; gap: 24px;
  padding: 26px 28px; background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow); margin-bottom: 18px;
}
.page-header__kicker {
  display: inline-block; font-size: 9px; font-weight: 800; letter-spacing: 1.1px;
  text-transform: uppercase; color: #175cd3; margin-bottom: 8px;
}
.page-header__title { margin: 0; color: var(--text); font-size: 22px; letter-spacing: -.03em; }
.page-header__desc { max-width: 720px; margin: 8px 0 0; color: var(--muted); font-size: 12px; line-height: 1.65; }
.page-header__actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; flex-shrink: 0; }
.page-header__body { min-width: 0; }

/* `--compact` modifier (Manajemen Rilis, 24 Aug 2026 — owner: "kartu
   penjelasan berguna sekali saat pertama kali dipakai, jadi beban setiap
   hari sesudahnya"). Title and action button are untouched — only the
   description shrinks: smaller type, tighter line-height, and a smaller
   top margin. Scoped to `.page-header__desc` under this modifier so every
   other page's `.page-header` (59 templates) keeps its current size. */
.page-header--compact { padding: 18px 28px; }
.page-header--compact .page-header__desc { font-size: 11px; line-height: 1.4; margin-top: 4px; }

/* ---------------------------------------------------------------------- */
/* Metric grid — summary cards driven only by real counts/aggregates       */
/* ---------------------------------------------------------------------- */

.metric-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin: 0 0 18px; }
.metric-grid--3 { grid-template-columns: repeat(3, 1fr); }
.metric-grid--2 { grid-template-columns: repeat(2, 1fr); }
.metric-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 16px 18px;
}
.metric-card__icon { font-size: 15px; opacity: .8; margin-bottom: 6px; display: block; }
.metric-card__value { display: block; font-size: 24px; font-weight: 760; color: var(--text); letter-spacing: -.03em; margin: 4px 0 2px; }
.metric-card__label { display: block; font-size: 10.5px; color: var(--muted); }
/* A `.status` badge used as a metric-card's second line (Email Center
   Branding & Kontak, System Settings Integration Status) — replaces the
   repeated `style="margin-top:8px;"` inline style (UI consistency pass,
   2026-08-18, §22 "remove visual debt"). */
.metric-card__status { display: block; margin-top: 8px; }
.metric-card__trend { display: block; font-size: 9.5px; color: var(--success); margin-top: 6px; }
.metric-card__trend--warn { color: var(--warn); }
.metric-card__trend--danger { color: var(--danger); }
.metric-card__value--sm { font-size: 14px; }

/* ---------------------------------------------------------------------- */
/* Stat strip — compact one-row alternative to `.metric-grid` for a page   */
/* whose numbers are read at a glance, not dwelt on (Manajemen Rilis        */
/* density pass #2, 24 Aug 2026: "empat kotak statistik ... padatkan jadi   */
/* satu baris ringkas"). Same card chrome as `.metric-card` (panel/line/    */
/* shadow) but one shared row instead of 4 separate ~70px-tall cards, each  */
/* item separated by a hairline divider rather than its own bordered box.  */
/* ---------------------------------------------------------------------- */

.stat-strip {
  display: flex; flex-wrap: wrap; align-items: stretch;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 4px 4px; margin: 0 0 18px;
}
.stat-strip__item {
  display: flex; align-items: baseline; gap: 7px; padding: 10px 20px;
  border-right: 1px solid var(--line);
}
.stat-strip__item:last-child { border-right: 0; }
.stat-strip__value { font-size: 18px; font-weight: 760; color: var(--text); letter-spacing: -.02em; }
.stat-strip__label { font-size: 10.5px; color: var(--muted); white-space: nowrap; }

@media (max-width: 600px) {
  .stat-strip__item { flex: 1 1 45%; padding: 8px 12px; border-right: 0; border-bottom: 1px solid var(--line); }
  .stat-strip__item:nth-child(2n) { border-right: 0; }
}

/* ---------------------------------------------------------------------- */
/* Section head — divider between blocks of a page                        */
/* ---------------------------------------------------------------------- */

.section-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; margin: 26px 0 12px;
}
.section-head:first-child { margin-top: 0; }
.section-head--flush { margin-top: 0; }
.section-head h2 { margin: 0; font-size: 15px; color: var(--text); letter-spacing: -.015em; }
.section-head p { margin: 4px 0 0; color: var(--muted); font-size: 10.5px; }
.section-head__actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.release-filter { border: 1px solid #d0d5dd; background: #fff; color: #344054; border-radius: 8px; padding: 7px 9px; font-size: 11px; }

/* ---------------------------------------------------------------------- */
/* Table card — table wrapped in its own card instead of floating raw     */
/* ---------------------------------------------------------------------- */

.table-card {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); overflow: hidden; margin-bottom: 18px;
}
.table-card .table-wrap { overflow-x: auto; }
.data-table { border-collapse: collapse; width: 100%; font-size: 12px; }
.data-table th, .data-table td { padding: 11px 14px; border-bottom: 1px solid #eaecf0; text-align: left; }
.data-table th { background: #f9fafb; color: var(--muted); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .3px; }
.data-table td { color: #344054; }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover { background: #fcfcfd; }
.data-table small { color: #98a2b3; font-size: 9.5px; }
/* Audit log's Correlation column (owner audit, 25 Aug 2026, point 3): a
   full correlation id is a long unbroken hyphenated string — left to wrap
   normally it broke across lines and read as cut off/unreadable. Keeping
   it on one line instead makes the table itself the thing that overflows,
   which `.table-card .table-wrap`'s own `overflow-x: auto` already scrolls
   on its own, without the whole page shifting sideways. */
.data-table td.nowrap { white-space: nowrap; }
/* One shared datetime format across the whole panel (owner audit, 25 Aug
   2026, point 2) — apps.common.templatetags.ui.id_datetime wraps every
   formatted value in this span so "25 Agu 2026, 06:45" never breaks
   across lines inside a table cell, without editing markup at each of
   its ~55 call sites individually. tabular-nums keeps the digits steady
   when several rows stack in a column. */
.dt { white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------------- */
/* Status pill — canonical 5-variant system                               */
/* ---------------------------------------------------------------------- */

.status {
  display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px;
  border-radius: 999px; font-size: 9.5px; font-weight: 750; white-space: nowrap;
  background: var(--panel2); color: var(--muted);
}
.status--success { background: #ecfdf3; color: #067647; }
.status--warning { background: #fffaeb; color: #93370d; }
.status--danger { background: #fef3f2; color: #b42318; }
.status--info { background: #eff8ff; color: #175cd3; }
.status--neutral { background: var(--panel2); color: var(--muted); }

/* ---------------------------------------------------------------------- */
/* Button aliases — same visual system as .button, requested class names  */
/* ---------------------------------------------------------------------- */

.btn { display: inline-flex; align-items: center; gap: 6px; border-radius: var(--radius-sm); padding: 9px 14px; font-weight: 700; font-size: 12px; cursor: pointer; border: 1px solid transparent; }
.btn-primary { background: var(--primary); border-color: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary2); border-color: var(--primary2); }
.btn-secondary { background: var(--text); border-color: var(--text); color: #fff; }
.btn-ghost { background: #fff; border-color: #d0d5dd; color: #344054; }
.btn-ghost:hover { background: #f9fafb; border-color: #98a2b3; }
.btn-danger { background: #fff; border-color: #fecdca; color: #b42318; }
.btn-danger:hover { background: #fef3f2; }
.btn-sm { padding: 6px 10px; font-size: 10.5px; }

/* ---------------------------------------------------------------------- */
/* Form grid — multi-column form layout (also applies to bare form.as_p)  */
/* ---------------------------------------------------------------------- */

.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 4px 16px; align-items: start; }
.form-grid--1 { grid-template-columns: 1fr; }
.form-grid--3 { grid-template-columns: repeat(3, 1fr); }
.form-grid > p { margin: 0 0 12px; grid-column: span 1; }
.form-grid .field, .form-grid > p { margin-bottom: 12px; }
.form-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.form-field--full { grid-column: 1 / -1; }
.form-field label, .form-grid label { font-weight: 650; font-size: 11px; color: #475467; }
.form-help { font-size: 11px; color: #98a2b3; margin: 4px 0 0; }
.form-errors { color: var(--danger); font-size: 11px; margin: 2px 0 8px; list-style: none; padding: 0; }
.form-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; grid-column: 1 / -1; }
.form-actions--center { justify-content: center; }

/* Branded 403/404/500 error pages (UI consistency pass, 2026-08-18, §18) —
   a centered card, same family as `.auth-shell`/`.card--narrow`. */
.error-card { text-align: center; }
.error-card .empty-state__icon { margin: 0 auto 14px; }
.error-card h1 { font-size: 18px; margin: 0 0 8px; }
.error-card p { color: var(--muted); font-size: 12.5px; margin: 0 0 4px; }

/* Release wizard Step 1 — auto-derived version info (2026-08-18 UX pass)   */
.release-type-legend {
  list-style: none; margin: -4px 0 12px; padding: 10px 12px; grid-column: 1 / -1;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel2);
  font-size: 11px; color: var(--muted); line-height: 1.6;
}
.release-type-legend b { color: var(--text); }
.version-info {
  margin: 4px 0 16px; padding: 12px 14px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--panel2); max-width: 460px;
}
.version-info__latest { margin: 0 0 10px; font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.version-info__grid { display: flex; gap: 24px; margin: 0; flex-wrap: wrap; }
.version-info__grid dt { font-size: 11px; color: var(--muted); margin: 0 0 2px; }
.version-info__grid dd { font-size: 13px; font-weight: 650; color: var(--text); margin: 0; }
details.advanced-settings { margin-bottom: 16px; }
details.advanced-settings summary { cursor: pointer; font-size: 11.5px; font-weight: 650; color: var(--muted); padding: 4px 0; }
details.advanced-settings[open] summary { color: var(--text); }
details.advanced-settings .form-grid { margin-top: 10px; }

/* ---------------------------------------------------------------------- */
/* Landing Page Builder — Halaman Penjualan                               */
/* ---------------------------------------------------------------------- */

/* ~62% editor / ~38% preview on large screens (product requirement:
   60-65% / 35-40%) — fr-ratio rather than a fixed preview width, so the
   split holds proportionally as the viewport grows instead of leaving the
   editor column awkwardly wide on large monitors. */
.builder-grid { display: grid; grid-template-columns: minmax(0, 1.65fr) minmax(380px, 1fr); gap: 18px; align-items: start; }
.builder-col { min-width: 0; }

.section-card {
  display: flex; align-items: flex-start; gap: 12px; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel);
  box-shadow: var(--shadow); margin-bottom: 10px;
}
.section-card__order { display: flex; flex-direction: column; gap: 2px; }
.section-card__order button {
  width: 26px; height: 22px; border: 1px solid #d0d5dd; background: #fff; border-radius: 6px;
  cursor: pointer; font-size: 11px; color: #475467; line-height: 1;
}
.section-card__order button:disabled { opacity: .35; cursor: not-allowed; }
.section-card__body { flex: 1; min-width: 0; }
.section-card__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.section-card__type { font-weight: 700; font-size: 12.5px; color: var(--text); }
.section-card__summary { font-size: 11.5px; color: var(--muted); margin: 2px 0 0; line-height: 1.5; word-break: break-word; }
.section-card__actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.pill { display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px; font-size: 9.5px; font-weight: 700; letter-spacing: .02em; }
.pill--on { background: #ecfdf3; color: #067647; }
.pill--off { background: var(--panel2); color: var(--muted); }

.type-picker { margin-bottom: 16px; max-width: 460px; }
.type-fieldset { display: none; }
.type-fieldset.is-active { display: block; }

/* Per-page brand theme editor (color-theme feature, 2026-08-23). */
.theme-presets { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 4px; }
.theme-preset-btn {
  display: flex; align-items: center; gap: 6px; border: 1px solid #d0d5dd; background: #fff;
  border-radius: 999px; padding: 6px 12px 6px 6px; font-size: 11px; color: var(--text); cursor: pointer;
}
.theme-preset-btn:hover { border-color: #98a2b3; background: #f9fafb; }
.theme-preset-swatch { width: 18px; height: 18px; border-radius: 50%; border: 1px solid rgba(0, 0, 0, .12); flex-shrink: 0; }
.color-field { display: flex; align-items: center; gap: 10px; }
.color-field input[type="color"] {
  width: 44px; height: 34px; padding: 2px; border-radius: 8px; cursor: pointer; flex-shrink: 0;
}
.color-hex { font-size: 11.5px; font-weight: 650; color: #475467; font-variant-numeric: tabular-nums; }
.contrast-hint { font-size: 11px; margin: 4px 0 0; display: none; }
.contrast-hint.is-visible { display: block; }
.contrast-hint--warning { color: #b54708; }

.repeater-row {
  display: grid; grid-template-columns: 1fr auto; gap: 8px 12px; align-items: start;
  padding: 12px; border: 1px solid #eaecf0; border-radius: 10px; background: #fcfcfd; margin-bottom: 10px;
}
.repeater-row__fields { display: flex; flex-direction: column; gap: 8px; }
.repeater-row__remove {
  border: 1px solid #fecdca; background: #fff; color: #b42318; border-radius: 8px;
  padding: 6px 10px; font-size: 10.5px; cursor: pointer; white-space: nowrap;
}
.repeater-add {
  border: 1px dashed #98a2b3; background: #fff; color: #344054; border-radius: 8px;
  padding: 8px 12px; font-size: 11px; cursor: pointer; margin-bottom: 14px;
}
.repeater-add:hover { border-color: var(--primary); color: var(--primary); }
.fingerprint-input { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .01em; }

.image-upload {
  border: 1px solid #eaecf0; border-radius: 10px; background: #fcfcfd; padding: 14px; max-width: 460px;
}
.image-upload__preview { display: flex; gap: 12px; align-items: center; }
.image-upload__thumb {
  width: 96px; height: 64px; object-fit: cover; border-radius: 8px; border: 1px solid #eaecf0; background: #fff;
  flex-shrink: 0;
}
.image-upload__meta { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.image-upload__filename { font-size: 11px; color: #475467; font-weight: 650; word-break: break-word; }
.image-upload__actions { display: flex; gap: 6px; flex-wrap: wrap; }
.image-upload__empty { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.image-upload__picker { cursor: pointer; }
.image-upload__status { font-size: 10.5px; color: var(--muted); margin: 8px 0 0; min-height: 13px; }
.image-upload__status:not(:empty) { color: var(--danger); }
.image-upload__status--ok:not(:empty) { color: #067647; }

.public-url-box {
  margin-top: 14px; padding: 12px 14px; border: 1px solid #d1fae5; border-radius: 10px;
  background: #f0fdf9; display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.public-url-box__label { font-size: 8.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; color: #067647; }
.public-url-box__value {
  flex: 1; min-width: 180px; font-size: 11.5px; color: #065f46; word-break: break-all; font-family: monospace;
}
.public-url-box__actions { display: flex; gap: 6px; flex-wrap: wrap; }

.preview-panel {
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel);
  box-shadow: var(--shadow); overflow: hidden; position: sticky; top: 88px; z-index: 5;
}
.preview-panel__bar {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap;
  padding: 10px 14px; border-bottom: 1px solid var(--line); font-size: 11.5px; font-weight: 650; color: #475467;
}
.preview-panel__controls { display: flex; gap: 4px; flex-wrap: wrap; }
.preview-panel__control {
  padding: 5px 10px; border: 1px solid #d0d5dd; background: #fff; border-radius: 7px;
  font-size: 10.5px; font-weight: 650; cursor: pointer; color: #475467; text-decoration: none; line-height: 1.4;
}
.preview-panel__control:hover { border-color: var(--primary); color: var(--primary); }
.preview-panel__control.is-active { background: var(--primary); border-color: var(--primary); color: #fff; }
.preview-panel__body {
  height: clamp(560px, 72vh, 800px); background: #f4f5f7; overflow: auto;
}
/* Desktop mode (default, no `--mobile` modifier), 24 Aug 2026 fix — the
   frame-wrap used to just be `width: 100%`, i.e. whatever the builder's
   right-hand grid column happened to be (`.builder-grid`'s
   `minmax(380px, 1fr)`), which is phone-width on most screens. That made
   "Desktop" render the sales page at its own mobile breakpoint — hero
   title wrapping onto 3 lines, everything cramped — never what a real
   desktop visitor sees. `min-width: 1280px` forces a genuine desktop
   viewport; `.preview-panel__body`'s own `overflow: auto` (above) gives a
   horizontal scrollbar on that same 1280px canvas whenever the panel
   column itself is narrower. Mobile mode is unaffected — its own rule
   below sets `width: 390px` unconditionally, overriding this entirely. */
.preview-panel__frame-wrap { width: 100%; min-width: 1280px; height: 100%; }
.preview-panel__frame { width: 100%; height: 100%; border: 0; display: block; background: #fff; }

/* Mobile device-frame mode — a simple frame, not a pixel-accurate phone
   mockup, just enough to show the actual responsive layout at a phone-ish
   viewport size. */
.preview-panel--mobile .preview-panel__body {
  display: flex; align-items: flex-start; justify-content: center; padding: 20px; background: #e5e7eb;
}
.preview-panel--mobile .preview-panel__frame-wrap {
  /* `min-width: 0` here overrides the desktop rule's `min-width: 1280px`
     above — properties cascade individually, not per-rule, so without
     this reset the mobile device frame would still be forced 1280px wide
     underneath its own explicit `width: 390px`. */
  width: 390px; min-width: 0; max-width: 100%; height: 844px; max-height: 100%; flex-shrink: 0;
  border: 10px solid #111827; border-radius: 40px; overflow: hidden; background: #000;
  box-shadow: 0 20px 40px rgba(17, 24, 39, .28);
}

@media (max-width: 1150px) {
  .builder-grid { grid-template-columns: 1fr; }
  .preview-panel { position: static; }
  .preview-panel__body { height: 480px; }
  .preview-panel--mobile .preview-panel__frame-wrap { height: 70vh; }
}

/* ---------------------------------------------------------------------- */
/* Empty state                                                            */
/* ---------------------------------------------------------------------- */

.empty-state {
  text-align: center; padding: 48px 24px; border: 1px dashed #d0d5dd;
  border-radius: var(--radius); background: #fcfcfd;
}
.empty-state__icon {
  width: 44px; height: 44px; margin: 0 auto 14px; border-radius: 12px;
  background: #eff6ff; color: #175cd3; display: grid; place-items: center; font-size: 20px;
}
.empty-state h3 { margin: 0 0 6px; font-size: 13px; color: var(--text); }
.empty-state p { margin: 0 auto 16px; max-width: 380px; color: var(--muted); font-size: 11.5px; line-height: 1.6; }

/* ---------------------------------------------------------------------- */
/* Detail grid — metadata cards on detail pages                           */
/* ---------------------------------------------------------------------- */

.detail-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin: 14px 0; }
.detail-grid--3 { grid-template-columns: repeat(3, 1fr); }
.detail-item {
  padding: 12px 13px; border: 1px solid #eaecf0; border-radius: 10px; background: #fcfcfd;
}
.detail-item span { display: block; font-size: 8.5px; color: #98a2b3; text-transform: uppercase; letter-spacing: .4px; }
.detail-item b { display: block; margin-top: 5px; font-size: 11.5px; color: #344054; font-weight: 650; word-break: break-word; }
/* A cell that should span the full row width regardless of the grid's
   column count — e.g. a SHA-256 hash next to shorter fields (Release
   Management redesign, 2026-08-19, Step 2 "Informasi APK" card). */
.detail-item--full { grid-column: 1 / -1; }
.alamat-pengambil { margin: 6px 0 0; padding-left: 16px; font-size: 11.5px; color: #344054; }
.alamat-pengambil li { margin: 2px 0; }

/* ---------------------------------------------------------------------- */
/* Wizard layout — vertical step rail + content card (release wizard)     */
/* ---------------------------------------------------------------------- */

.wizard-layout { display: grid; grid-template-columns: 220px 1fr; gap: 18px; align-items: start; }
.wizard-layout__content { min-width: 0; }

.wizard-nav {
  display: flex; flex-direction: column; gap: 4px; list-style: none; padding: 12px;
  margin: 0; background: #f9fafb; border: 1px solid var(--line); border-radius: var(--radius);
  position: sticky; top: 88px;
}
.wizard-nav__step {
  display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 9px;
  border: none; font-size: 10.5px; color: #667085;
}
.wizard-nav__step--current { background: #eff6ff; color: #175cd3; }
.wizard-nav__number {
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  background: #fff; border: 1px solid #d0d5dd; font-size: 9.5px; font-weight: 800; color: #475467;
  flex-shrink: 0;
}
.wizard-nav__step--current .wizard-nav__number { background: var(--primary); border-color: var(--primary); color: #fff; }
.wizard-nav__step--done .wizard-nav__number { background: #ecfdf3; border-color: #abefc6; color: #067647; }
.wizard-nav__step a, .wizard-nav__step span { color: inherit; font-weight: 650; }

/* ---------------------------------------------------------------------- */
/* Product summary cards (Produk & SKU product list)                      */
/* ---------------------------------------------------------------------- */

.product-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 18px; }
/* Bugfix (Produk & SKU manual-test findings, 2026-08-21): this used to be a
   3-column grid (`40px 1fr auto`) holding 4 children (icon, copy, status
   badge, "Manage" button) — the 4th child had nowhere to go on row 1, so
   the grid auto-flowed it onto a new implicit row starting back at column
   1, stacking it underneath the icon instead of beside the status badge.
   That is what "outline/border keluar dari area tombol/card" actually was:
   not a border bug, a misplaced grid item. Flex column with an explicit
   top row (icon/copy/status) and a footer actions row fixes it and gives
   the actions row room to hold Manage + Edit + Hapus without any of them
   needing a fixed width. */
.product-card {
  display: flex; flex-direction: column; gap: 12px; min-width: 0;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px;
}
.product-card__top { display: flex; align-items: center; gap: 12px; min-width: 0; }
.product-card__icon {
  flex: none; width: 40px; height: 40px; border-radius: 10px; background: #eff6ff; color: #175cd3;
  display: grid; place-items: center; font-size: 11px; font-weight: 800;
}
.product-card__copy { flex: 1 1 auto; min-width: 0; }
.product-card__copy b {
  display: block; color: var(--text); font-size: 11.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.product-card__copy span { display: block; color: var(--muted); font-size: 9.5px; margin-top: 3px; }
.product-card__status { flex: none; }
.product-card__actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
.product-card__actions .button { flex: none; }

@media (max-width: 900px) {
  .product-grid { grid-template-columns: 1fr; }
}
@media (max-width: 600px) {
  /* Actions stay easy to tap full-width on a narrow phone instead of
     wrapping into a cramped, uneven row. */
  .product-card__actions { justify-content: stretch; }
  .product-card__actions .button { flex: 1 1 auto; justify-content: center; }
}

/* ---------------------------------------------------------------------- */
/* Product summary rows (Release Management "Produk" overview)            */
/* One dense row per product — name, code, latest release version, count, */
/* last-created date, action — density pass, 24 Aug 2026. Deliberately a  */
/* flex row list rather than `.data-table`: a `<table>` can only ever     */
/* scroll sideways on a narrow phone, never reflow, and the owner audit   */
/* ruled that out for this specific section (contrast the "Riwayat        */
/* Rilis" `<table>` lower on the same page, a detail drill-down where     */
/* horizontal scroll is an acceptable trade-off, not an at-a-glance       */
/* overview). Below `max-width: 680px` each row stacks into a small       */
/* key/value list instead, using the `data-label` attribute the template  */
/* sets on the version/count/date cells.                                  */
/* ---------------------------------------------------------------------- */

.product-summary-list { display: flex; flex-direction: column; }
.product-summary-row {
  display: flex; align-items: center; gap: 16px; padding: 8px 16px 8px 11px;
  border-bottom: 1px solid #eaecf0; font-size: 12px; color: #344054;
  /* Product identity color accent, 24 Aug 2026 — thin left edge only (see
     the template's own comment for the "never the only differentiator"
     reasoning); `border-left-color` is set inline per-row from
     `Product.display_identity_color()`, always a validated hex, never an
     arbitrary string. `#98A2B3` here is just `NEUTRAL_IDENTITY_COLOR`
     (apps/catalog/models.py) so a row somehow missing the inline style
     still reads as a deliberate neutral, not a rendering bug.
     Widened 3px -> 5px, 24 Aug 2026 (owner audit: it *was* already per-row
     with distinct colors, just too thin at 3px to read as a per-row
     differentiator at a glance — looked like one uniform hairline down the
     whole list instead of a color-coded one). Left padding trimmed 13px ->
     11px to hold the same ~16px total inset the row's other sides use. */
  border-left: 5px solid #98A2B3;
}
.product-summary-row:last-child { border-bottom: 0; }
.product-summary-row:hover { background: #fcfcfd; }
.product-summary-row__id { flex: 1 1 220px; min-width: 0; }
.product-summary-row__id b {
  display: block; color: var(--text); font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.product-summary-row__id span { display: block; color: var(--muted); font-size: 10px; margin-top: 2px; }
.product-summary-row__version { flex: 1 1 260px; min-width: 0; }
.product-summary-row__version .status { margin-left: 4px; }
.product-summary-row__count { flex: 0 0 90px; text-align: center; }
.product-summary-row__date { flex: 0 0 110px; color: var(--muted); font-size: 11px; }
.product-summary-row__action { flex: 0 0 auto; margin-left: auto; }
/* Density pass #2, 24 Aug 2026 (owner: "tombol itu tidak perlu setinggi
   sekarang") — tighter than the sitewide `.button.small` (padding 6px
   10px) it would otherwise inherit; scoped to this one row action so
   every other `.button.small` elsewhere in the app is untouched. */
.product-summary-row__action .button.small { padding: 4px 9px; font-size: 10px; }

@media (max-width: 680px) {
  .product-summary-row { flex-direction: column; align-items: stretch; gap: 8px; padding: 10px 14px 10px 9px; }
  .product-summary-row__id, .product-summary-row__version, .product-summary-row__count,
  .product-summary-row__date, .product-summary-row__action { flex: none; text-align: left; }
  .product-summary-row__version, .product-summary-row__count, .product-summary-row__date {
    display: flex; justify-content: space-between; align-items: center; gap: 10px;
  }
  .product-summary-row__version::before, .product-summary-row__count::before,
  .product-summary-row__date::before {
    content: attr(data-label); flex: none; font-size: 10px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .3px; color: var(--muted);
  }
  .product-summary-row__action { display: flex; }
  .product-summary-row__action .button { flex: 1 1 auto; justify-content: center; }
}

/* ---------------------------------------------------------------------- */
/* Activity list (dashboard recent audit)                                 */
/* ---------------------------------------------------------------------- */

.activity-list { display: flex; flex-direction: column; }
.activity-row {
  display: flex; justify-content: space-between; gap: 14px; padding: 11px 0;
  border-bottom: 1px solid #f2f4f7; font-size: 11px;
}
.activity-row:last-child { border-bottom: 0; }
.activity-row__main b { color: #344054; font-weight: 650; }
.activity-row__main span { display: block; color: #98a2b3; font-size: 10px; margin-top: 2px; }
.activity-row__time { color: #98a2b3; font-size: 10px; white-space: nowrap; }

@media (max-width: 1150px) {
  .metric-grid { grid-template-columns: repeat(2, 1fr); }
  .product-grid, .detail-grid, .detail-grid--3 { grid-template-columns: 1fr; }
}

/* ---------------------------------------------------------------------- */
/* Public storefront — landing page renderer (not the admin shell)         */
/* ---------------------------------------------------------------------- */

.browser {
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden; background: #fff;
  box-shadow: var(--shadow); margin-bottom: 18px;
}
.browser-bar { height: 38px; background: #f2f4f7; border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 7px; padding: 0 13px; }
.browser-dot { width: 8px; height: 8px; border-radius: 50%; background: #d0d5dd; display: inline-block; }
.browser-url { margin-left: 8px; flex: 1; padding: 5px 10px; border-radius: 7px; background: #fff; border: 1px solid var(--line); color: var(--muted); font-size: 10px; }
/* Storefront brand tokens. Defaults come from the MoudiPOS design (the
   first real page built on this system) — deliberately scoped to
   `.storefront` alone, never `:root`, so the shared admin dashboard keeps
   its own blue theme untouched. A future product wanting a different
   palette overrides these three custom properties, not the class rules
   below them (no second copy of the storefront CSS needed per product —
   this is the "reusable section type" principle applied to styling too).
   Not yet exposed as a per-LandingPage admin field — every storefront page
   shares this palette today; noted as an open item in the sales-page
   report rather than built speculatively ahead of a second product
   actually needing a different one.

   FALLBACK VALUES (2026-08-24 incident): every `var(--store-x)` USAGE
   below carries a second argument, e.g. `var(--store-primary, #d32f2f)` —
   these are NOT the same thing as the DEFAULTS declared inside `.storefront{}`
   just below. The `.storefront{}` defaults only apply once something has
   actually put the `.storefront` class on an ancestor element AND the
   page's own theme.css `<link>` has loaded (or failed open, matching this
   same DEFAULT_THEME — see apps.landing.colors). A page that renders these
   classes WITHOUT either of those two things (checkout's purchase_entry.html
   shipped with the `store-cta` class but no theme.css link, 2026-08-24 —
   `.storefront` was never in scope for that page at all) gets a fully
   UNDEFINED custom property, which computes to nothing at all, not to any
   of the values below — `background: var(--store-primary)` becomes
   `background: <nothing>` = transparent, `color: #fff` stays white, and the
   button disappears with no error anywhere. The inline fallback is the
   safety net: it makes a page that forgot to wire up the theme render in
   the DEFAULT_THEME colors instead of invisibly, so a missing `.storefront`
   scope or a missing theme.css `<link>` fails ugly-but-visible, not
   silently. Keep every fallback here in sync with DEFAULT_THEME (apps/
   landing/colors.py) and the declarations directly below — the CSS-only
   test `apps.landing.tests.test_theme_fallbacks` enforces that every
   `var(--store-*)` usage in this file has one. */
.storefront {
  /* --store-primary, --store-primary-dark, --store-accent, --store-dark
     are the four per-page theme colors (color-theme feature, 2026-08-23) —
     these are the DEFAULTS matching LandingPage's own model field
     defaults exactly, so an untouched page's `<style>` override
     (landing_public/_render.html) re-declares the identical values and
     changes nothing visually. Never edit these four without also updating
     apps/landing/models.py's field defaults, or a fresh page and an
     already-published page would render two different colors. */
  --store-primary: #d32f2f;
  --store-primary-dark: #9a0007;
  --store-accent: #ffb300;
  --store-dark: #111111;
  /* Fixed decorative colors — NOT part of the admin-configurable theme. */
  --store-check: #2e7d32;
  --store-text: #1e1e1e;
  --store-muted: #666666;
  --store-line: #e0e0e0;
  --store-bg-alt: #f8f9fa;
  --store-badge-bg: #ffebee;
  font-family: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: var(--panel); min-height: 200px; color: var(--store-text, #1e1e1e);
  /* Clearance for the fixed floating CTA bar below (bottom-right on
     desktop, full-width strip on <=600px) so it never sits over the
     footer or the last section's content. */
  padding-bottom: 92px;
}
.store-nav {
  position: sticky; top: 0; z-index: 50;
  height: 64px; display: flex; align-items: center; justify-content: space-between;
  padding: 0 32px; border-bottom: 1px solid var(--store-line, #e0e0e0); background: #fff;
}
.store-logo { font-size: 22px; font-weight: 800; color: var(--store-primary, #d32f2f); }
/* `.store-nav-cta` (nav's own buy button) removed 24 Aug 2026 — see the
   template's own comment in `landing_public/_render.html`'s `<nav>`
   block for why. */
/* Default hero: single column, centered, no image slot at all — the
   sales-page design has none (2026-08-23 visual revision: what used to
   render here as a black placeholder box was never "an image not yet
   uploaded", it was a slot the design doesn't have). `--with-image` is an
   opt-in variant for a future landing page whose Hero actually has an
   uploaded image (apps/landing/forms.py still supports uploading one) —
   it keeps the original two-column, left-aligned layout untouched. */
.store-hero {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  padding: 56px 48px; background: linear-gradient(180deg, #fff5f5, #fff);
}
.store-hero--with-image {
  display: grid; grid-template-columns: 1.1fr .9fr; gap: 32px; text-align: left;
  background: linear-gradient(135deg, #fff5f5, #fff9f2);
}
.store-copy { display: flex; flex-direction: column; align-items: center; }
.store-hero--with-image .store-copy { align-items: flex-start; }
.store-copy .mini { font-size: 10px; font-weight: 800; color: var(--store-primary, #d32f2f); letter-spacing: .7px; text-transform: uppercase; }
.store-copy .mini--badge {
  display: inline-block; background: var(--store-badge-bg, #ffebee); color: var(--store-primary, #d32f2f);
  padding: 6px 14px; border-radius: 20px; font-size: 12px; font-weight: 700;
  letter-spacing: normal; text-transform: none;
}
.store-copy h1 { font-size: 34px; line-height: 1.15; margin: 12px 0 14px; color: var(--store-text, #1e1e1e); letter-spacing: -.02em; font-weight: 800; }
.store-copy h1 em { color: var(--store-primary, #d32f2f); font-style: normal; }
.store-copy p { font-size: 16px; line-height: 1.7; color: var(--store-muted, #666666); max-width: 820px; }
.store-hero--with-image .store-copy p { font-size: 12.5px; max-width: 480px; }
.store-price { font-size: 26px; font-weight: 800; color: var(--store-text, #1e1e1e); margin: 16px 0 4px; }
.store-price-suffix { font-size: 13px; font-weight: 600; color: var(--store-muted, #666666); }
.store-note { font-size: 10px; color: var(--store-muted, #666666); }
/* "Garansi 7 hari uang kembali" link, next to price in both Hero and
   Pricing (footer link placement audit, 24 Aug 2026, point b). Underlined
   like every other legal link in this codebase; brand-colored rather than
   muted so it doesn't read as fine print a buyer would skip past — it's
   meant to be noticed before checkout, not just legally present. */
.store-guarantee { display: inline-block; margin: 2px 0 4px; font-size: 12px; font-weight: 600; color: var(--store-primary, #d32f2f); text-decoration: underline; }
.store-guarantee:hover { color: var(--store-primary-dark, #a02222); }
.store-hero-ctas { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
.store-hero--with-image .store-hero-ctas { justify-content: flex-start; }
.store-hero-points {
  list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: row;
  flex-wrap: wrap; justify-content: center; gap: 6px 18px;
}
.store-hero--with-image .store-hero-points { flex-direction: column; justify-content: flex-start; }
.store-hero-points li { font-size: 12.5px; font-weight: 600; color: var(--store-text, #1e1e1e); }
.store-hero-points li::before { content: "✓ "; color: var(--store-check, #2e7d32); font-weight: 800; }
.store-cta {
  display: inline-flex; align-items: center; justify-content: center; margin-top: 16px;
  padding: 14px 22px; border-radius: 7px; background: var(--store-primary, #d32f2f); color: #fff;
  font-weight: 700; font-size: 16px; border: none; cursor: pointer;
  box-shadow: 0 8px 20px rgba(211, 47, 47, .25);
}
.store-cta:hover { background: var(--store-primary-dark, #9a0007); }
.store-cta:disabled { opacity: .6; cursor: wait; }
.store-cta--inline { text-decoration: none; }
.store-cta--disabled { opacity: .5; cursor: not-allowed; }
.store-cta--block { width: 100%; }
/* Small always-visible label on a disabled (preview-only) buy button,
   24 Aug 2026 — the pale `.store-cta--disabled` look plus a `title`
   tooltip weren't enough on their own: nothing hovers a button on a
   preview page, so the pale color alone read as a bug to the owner, not a
   deliberate "can't click this here" state. The wrapper is `inline-block`
   (or `--block` below, matching `.store-cta--block`'s own `width: 100%`)
   so it takes exactly the same footprint the bare button did before —
   the badge itself is `position: absolute`, floating in the button's own
   `margin-top: 16px` gap above it, out of flow, never shifting anything
   around it. Never rendered outside `preview_mode` (see the template),
   so a real visitor's page is untouched either way. */
.store-cta-disabled-wrap { position: relative; display: inline-block; }
.store-cta-disabled-wrap--block { display: block; width: 100%; }
.store-cta-disabled-badge {
  position: absolute; top: -2px; left: 50%; transform: translate(-50%, -100%);
  white-space: nowrap; background: #111827; color: #fff; font-size: 9px; font-weight: 700;
  letter-spacing: .2px; padding: 3px 8px; border-radius: 999px; pointer-events: none; z-index: 1;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .25);
}
/* Demo/"ghost" CTA — shared by the Hero's own demo button and the
   floating bar's demo pill (sales-page spec, 2026-08-23 visual revision):
   black fill, yellow outline, and a small yellow circle standing in for
   the leading "▶" so the glyph never has to be styled from inside plain
   admin-typed text. */
.store-cta--ghost, .store-floating-bar__btn--ghost {
  background: var(--store-dark, #111111); color: #fff; font-weight: 800;
  border: 2px solid var(--store-accent, #ffb300); box-shadow: 0 6px 16px rgba(0, 0, 0, .25);
}
.store-cta--ghost:hover, .store-floating-bar__btn--ghost:hover { background: #000; }
.store-cta--ghost::before, .store-floating-bar__btn--ghost::before {
  content: "▶"; display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; margin-right: 7px; border-radius: 50%; flex-shrink: 0;
  background: var(--store-accent, #ffb300); color: var(--store-dark, #111111); font-size: 9px;
}
.store-pricing { text-align: center; padding: 48px 24px; }
.store-pricing-card {
  max-width: 470px; margin: 0 auto; padding: 28px 24px; border-radius: 12px;
  border: 2px solid var(--store-primary, #d32f2f); background: #fff; box-shadow: 0 16px 40px rgba(16, 24, 40, .12);
}
.store-pricing-badge {
  display: inline-block; padding: 4px 12px; border-radius: 999px; background: #fff5f5;
  color: var(--store-primary, #d32f2f); font-size: 10px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase;
}
/* Deliberately plain small gray text, never struck through — a "no
   monthly fee" tagline isn't a superseded price, so a strikethrough here
   would read as a mistake, not emphasis. */
.store-pricing-tagline { font-size: 11.5px; color: var(--store-muted, #666666); margin: 10px 0 0; text-decoration: none; }
.store-pricing-items { list-style: none; margin: 16px 0 20px; padding: 0; text-align: left; display: flex; flex-direction: column; }
.store-pricing-items li { font-size: 11.5px; color: var(--store-text, #1e1e1e); line-height: 1.5; padding: 8px 0; border-bottom: 1px dashed var(--store-line, #e0e0e0); }
.store-pricing-items li:last-child { border-bottom: none; }
.store-price--lg { font-size: 36px; color: var(--store-primary, #d32f2f); }
.store-final-cta { text-align: center; padding: 48px 24px; background: #f9fafb; }
.store-final-cta h2 { font-size: 24px; margin: 0 0 8px; color: var(--store-text, #1e1e1e); }
.store-final-cta p { font-size: 12.5px; color: var(--store-muted, #666666); margin: 0 0 16px; }
.store-visual {
  background: var(--store-text, #1e1e1e); border-radius: 20px; overflow: hidden;
  aspect-ratio: 16 / 10; width: 100%; display: grid; place-items: center; color: #fff;
}
.store-visual--contain { background: #eef0f3; }
.store-visual__img { display: block; width: 100%; height: 100%; }
.store-visual__img--cover { object-fit: cover; }
.store-visual__img--contain { object-fit: contain; }
.store-section-heading { font-size: 22px; font-weight: 800; text-align: center; color: var(--store-text, #1e1e1e); margin: 0 0 24px; letter-spacing: -.01em; }
.store-sections { padding: 40px 48px 16px; }
.store-sections--problem, .store-sections--features, .store-sections--faq { background: var(--store-bg-alt, #f8f9fa); }
.store-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
.store-cards--faq { grid-template-columns: 1fr; max-width: 640px; margin: 0 auto; }
.public-card { border: 1px solid var(--store-line, #e0e0e0); border-radius: 14px; padding: 18px; background: #fff; }
.public-card b { font-size: 12px; color: var(--store-text, #1e1e1e); }
.public-card p { font-size: 10.5px; line-height: 1.6; color: var(--store-muted, #666666); margin: 8px 0 0; }
.public-card__icon { display: block; font-size: 20px; margin-bottom: 8px; }
/* Problem cards: the "❌" is part of the card title text itself (see
   template), not a separate large icon above it — only the left accent
   border is this variant's own styling. */
.public-card--problem { background: #fff; border-color: var(--store-line, #e0e0e0); border-left: 3px solid var(--store-primary, #d32f2f); }
/* Benefit cards: centered content, icon enlarged above the title. */
.public-card--benefit { text-align: center; }
.public-card--benefit .public-card__icon { font-size: 28px; margin: 0 auto 10px; }
/* Feature cards: soft shadow, title in the brand red (icon is already
   part of the title text, see template). */
.public-card--feature { box-shadow: var(--shadow); }
.public-card--feature b { color: var(--store-primary, #d32f2f); }
.store-footer { text-align: center; padding: 32px 24px; background: var(--store-dark, #111111); color: #fff; font-size: 11px; }
.store-footer a { color: inherit; text-decoration: underline; }
.store-footer a:hover { opacity: .8; }
/* Legal-links footer group (footer link placement audit, 24 Aug 2026) —
   Syarat & Ketentuan + Kebijakan Pengembalian Dana, same row, everywhere
   this include appears (`templates/legal/_footer_links.html`). Anchor
   color/underline comes from the containing footer (`.store-footer a` /
   `.checkout-footer a`) so this reads correctly on both the dark storefront
   footer and the light checkout footer without its own color rule. */
.legal-links { margin-top: 8px; display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }
/* Floating dual CTA bar (sales-page spec, 2026-08-23 visual revision) —
   always visible, fixed bottom-right, pill buttons. At <=600px it becomes
   a full-width strip anchored to the bottom edge instead (see media query
   below); `.storefront`'s padding-bottom above keeps it clear of content
   at every width. */
.store-floating-bar { display: flex; gap: 10px; position: fixed; right: 20px; bottom: 20px; z-index: 40; }
.store-floating-bar__btn {
  display: inline-flex; align-items: center; justify-content: center; white-space: nowrap;
  padding: 13px 20px; border-radius: 999px; font-weight: 800; font-size: 12.5px;
  text-decoration: none; background: var(--store-primary, #d32f2f); color: #fff;
  box-shadow: 0 8px 20px rgba(211, 47, 47, .3);
}
@media (max-width: 768px) {
  .store-hero--with-image { grid-template-columns: 1fr; }
  .store-hero { padding: 40px 24px; }
  .store-sections { padding: 32px 24px 16px; }
}
@media (max-width: 600px) {
  .store-copy h1 { font-size: 27px; }
  .store-cards { grid-template-columns: 1fr; }
  .storefront { padding-bottom: 58px; }
  .store-floating-bar { left: 8px; right: 8px; bottom: 7px; gap: 8px; }
  .store-floating-bar__btn { flex: 1; padding: 13px 8px; }
}
@media (max-width: 520px) {
  .store-nav { padding: 0 16px; }
  .store-hero { padding: 28px 16px; gap: 20px; }
  .store-sections { padding: 24px 16px 8px; }
  .store-price--lg { font-size: 32px; }
}
@media (max-width: 360px) {
  .store-copy h1 { font-size: 23px; }
  .store-price { font-size: 22px; }
  .store-hero-ctas { flex-direction: column; }
  .store-hero-ctas .store-cta { width: 100%; justify-content: center; margin-top: 0; }
  .store-floating-bar__btn { font-size: 11px; padding: 11px 6px; }
}

/* ---------------------------------------------------------------------- */
/* Purchase entry — customer-facing "Selesaikan Pembelian" page           */
/* Checkout redesign, 2026-08-24 — layout follows the owner's reference   */
/* design (moudipos.moudi.id); colors read `var(--store-*)` (never a bare */
/* hex) so a future per-product theme repaints this page for free, same   */
/* fallback-value rule as every other `.storefront` usage in this file.   */
/* ---------------------------------------------------------------------- */

.checkout-page {
  min-height: 100vh;
  background: linear-gradient(135deg, #f7f7ff, #f2fbff);
}
.checkout-topbar {
  position: sticky; top: 0; z-index: 60;
  height: 56px;
  background: #fff; border-bottom: 1px solid var(--store-line, #e0e0e0);
}
/* The bar background spans the full viewport (no max-width here), but its
   content is capped at the same gutter as `.checkout-shell` below and
   centered the same way, via `.checkout-topbar__inner` — otherwise the
   product name sits flush against the screen edge while the card/hero
   below it is centered in a narrower column, which is two layout systems
   on one page (desktop layout fix, 2026-08-24). Padding matches
   `.checkout-shell`'s 16px so the product name's left edge lines up
   exactly with the card's. */
.checkout-topbar__inner {
  max-width: 480px; height: 100%; margin: 0 auto; padding: 0 16px;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.checkout-topbar__product { font-weight: 800; font-size: 13.5px; color: var(--store-text, #1e1e1e); }
.checkout-topbar__status { font-size: 11.5px; font-weight: 650; color: var(--store-primary, #d32f2f); text-decoration: none; }
.checkout-topbar__status:hover { text-decoration: underline; }
/* Same 480px gutter as the topbar above — `.checkout-hero` (title +
   subtitle) and `.checkout-card` are both direct children of this shell,
   so they inherit it automatically and the whole page shares one
   consistent column from top to bottom (desktop layout fix, 2026-08-24;
   was 520px). */
.checkout-shell { max-width: 480px; margin: 0 auto; padding: 24px 16px 8px; }
.checkout-back { font-size: 11px; color: var(--store-muted, #666666); text-decoration: none; display: inline-block; margin-bottom: 14px; }
.checkout-back:hover { color: var(--store-primary, #d32f2f); }
.checkout-hero { text-align: center; padding: 8px 4px 22px; }
.checkout-hero__title { font-size: 24px; font-weight: 800; letter-spacing: -.02em; margin: 0 0 6px; color: var(--store-text, #1e1e1e); }
.checkout-hero__subtitle { font-size: 12.5px; font-weight: 650; color: var(--store-primary, #d32f2f); text-transform: uppercase; letter-spacing: .4px; margin: 0; }

.checkout-card {
  width: 100%; background: #fff; border: 1px solid var(--store-line, #e0e0e0);
  border-radius: 18px; box-shadow: var(--shadow); padding: 24px 20px 28px; margin-bottom: 24px;
}
.checkout-card--center { text-align: center; }

.checkout-section { margin-bottom: 24px; }
.checkout-section:last-child { margin-bottom: 0; }
.checkout-section__title {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .5px;
  color: var(--store-text, #1e1e1e); margin: 0 0 12px;
}
.checkout-section__title::before {
  content: ""; display: inline-block; width: 3px; height: 14px; border-radius: 2px;
  background: var(--store-primary, #d32f2f); flex-shrink: 0;
}

.checkout-product { display: flex; align-items: flex-start; gap: 14px; }
.checkout-product__thumb {
  width: 56px; height: 56px; border-radius: 12px; flex-shrink: 0;
  background: var(--store-primary, #d32f2f); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 17px; font-weight: 800; letter-spacing: .02em;
}
.checkout-product__body { min-width: 0; }
.checkout-product__name { font-weight: 700; font-size: 13.5px; color: var(--store-text, #1e1e1e); }
.checkout-product__sku { font-size: 10.5px; color: var(--store-muted, #666666); margin-top: 2px; }
.checkout-product__price { font-size: 21px; font-weight: 800; color: var(--store-primary, #d32f2f); margin-top: 4px; }
.checkout-product__tagline { font-size: 11px; color: var(--store-muted, #666666); margin-top: 4px; line-height: 1.5; }

.checkout-field-label { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.checkout-field-label label { margin: 0; }
.checkout-field-label__note { font-size: 10.5px; color: var(--store-muted, #666666); font-weight: 500; }

.checkout-summary {
  border-radius: 12px; padding: 14px 16px; background: var(--store-badge-bg, #ffebee);
}
.checkout-summary__row { display: flex; align-items: center; justify-content: space-between; padding: 5px 0; font-size: 12.5px; color: var(--store-text, #1e1e1e); }
.checkout-summary__divider { border-top: 1px dashed var(--store-primary, #d32f2f); opacity: .35; margin: 6px 0; }
.checkout-summary__row--total { font-weight: 800; font-size: 14.5px; padding-top: 2px; }

.checkout-submit { width: 100%; justify-content: center; margin-top: 6px; }
.checkout-submit:disabled { opacity: .6; cursor: wait; }
.checkout-note { font-size: 10.5px; color: var(--store-muted, #666666); text-align: center; margin-top: 12px; line-height: 1.6; }
.checkout-footer { text-align: center; padding: 24px 16px 32px; font-size: 10.5px; color: var(--store-muted, #666666); line-height: 1.6; }
/* Checkout help link (support_url audit, 24 Aug 2026) — same quiet-link
   idiom as .checkout-back: inherits the footer's muted color by default
   (no jarring bright blue on a copyright line), underlined so it still
   reads as clickable, only turns brand-colored on hover. */
.checkout-footer a { color: inherit; text-decoration: underline; }
.checkout-footer a:hover { color: var(--store-primary, #d32f2f); }

/* Public post-payment status page (templates/payments/order_status.html,
   2026-08-30) — plain status copy is the entire point of that page, so it
   needs its own readable body-text vocabulary rather than reusing
   `.checkout-note` (10.5px, muted — built for a footnote under a button,
   not for the message itself). */
.checkout-status__lead { font-size: 15px; font-weight: 800; color: var(--store-text, #1e1e1e); margin: 0 0 10px; line-height: 1.5; }
.checkout-status__body { font-size: 13.5px; color: var(--store-text, #1e1e1e); line-height: 1.65; margin: 0 0 10px; }
.checkout-status__body:last-child { margin-bottom: 0; }
.checkout-status__body a { color: var(--store-primary, #d32f2f); font-weight: 700; text-decoration: underline; }
.checkout-status__order {
  display: inline-block; margin-top: 14px; padding: 8px 14px; border-radius: 10px;
  background: var(--store-badge-bg, #ffebee); color: var(--store-text, #1e1e1e);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px; font-weight: 700; letter-spacing: .02em;
}

/* "Simpan sekarang" notice (24h reusable download link, 2 Sep 2026) —
   deliberately louder than `.alert--info`/`.alert--error` (built for a form
   validation message, not something a buyer must actually act on): amber,
   bordered, its own heading. Placed above the download button on
   `templates/fulfillment/apk_download.html`; the purchase-confirmation
   email uses its own inline-styled HTML twin
   (`apps.fulfillment.services._save_now_warning_section_html`) since email
   clients don't load this stylesheet. */
.save-now-notice {
  background: #fffbeb; border: 1.5px solid #f5a524; border-radius: 12px;
  padding: 16px 18px; margin-bottom: 22px; text-align: left;
}
.save-now-notice__title {
  margin: 0 0 8px; font-size: 14px; font-weight: 800; color: #92400e;
}
.save-now-notice__body { margin: 0 0 10px; font-size: 12.5px; line-height: 1.65; color: #78350f; }
.save-now-notice__body:last-child { margin-bottom: 0; }
.save-now-notice__body strong { font-weight: 800; }
.save-now-notice ol { margin: 0 0 10px 18px; padding: 0; font-size: 12.5px; line-height: 1.65; color: #78350f; }
.save-now-notice li { margin-bottom: 4px; }

@media (max-width: 360px) {
  .checkout-topbar { height: 52px; }
  .checkout-topbar__inner { padding: 0 14px; }
  .checkout-topbar__product { font-size: 12px; }
  .checkout-topbar__status { font-size: 10.5px; }
  .checkout-hero__title { font-size: 20px; }
  .checkout-card { padding: 18px 14px 22px; }
  .checkout-product__thumb { width: 48px; height: 48px; font-size: 15px; }
  .checkout-product__price { font-size: 18px; }
  .checkout-status__lead { font-size: 14px; }
  .checkout-status__order { font-size: 12px; padding: 7px 12px; }
}
.terms-field { max-width: none; }
.terms-checkbox { display: flex; align-items: flex-start; gap: 8px; font-size: 11px; line-height: 1.5; color: var(--text); font-weight: 500; cursor: pointer; }
.terms-checkbox input[type="checkbox"] { margin-top: 2px; flex-shrink: 0; width: 16px; height: 16px; }
.terms-checkbox a { color: var(--primary); font-weight: 650; }
.checkout-card .terms-body { font-size: 12px; line-height: 1.7; color: #344054; }
.checkout-card .terms-body p { margin: 0 0 12px; }

/* Terms pop-up (checkout terms spec round 2, 2026-08-23) — `hidden` is
   toggled by checkout_terms_modal.js; `.terms-modal { display: none }`
   below is the CSS-level default so the dialog never flashes visible
   before JS runs, and `:not([hidden])` is what actually shows it (a plain
   `.terms-modal { display: flex }` would otherwise fight the `[hidden]`
   attribute at equal specificity). */
.terms-modal { display: none; }
.terms-modal:not([hidden]) {
  display: flex; align-items: center; justify-content: center;
  position: fixed; inset: 0; z-index: 100; padding: 16px;
}
.terms-modal__backdrop { position: absolute; inset: 0; background: rgba(16, 24, 40, .55); }
.terms-modal__dialog {
  position: relative; background: #fff; border-radius: 14px; width: 100%; max-width: 560px;
  max-height: min(80vh, 640px); display: flex; flex-direction: column; overflow: hidden;
  box-shadow: 0 20px 60px rgba(16, 24, 40, .35);
}
.terms-modal__dialog:focus { outline: none; }
.terms-modal__header {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 16px 18px; border-bottom: 1px solid var(--line); flex-shrink: 0;
}
.terms-modal__header h2 { font-size: 14px; margin: 0; line-height: 1.4; color: var(--text); }
.terms-modal__close {
  border: 0; background: transparent; font-size: 22px; line-height: 1; color: var(--muted);
  cursor: pointer; padding: 4px 8px; border-radius: 8px; flex-shrink: 0;
}
.terms-modal__close:hover { background: var(--panel2); color: var(--text); }
.terms-modal__body { padding: 16px 18px; overflow-y: auto; font-size: 12px; line-height: 1.7; color: #344054; }
.terms-modal__body p { margin: 0 0 12px; }
.terms-modal__footer {
  padding: 12px 18px; border-top: 1px solid var(--line); flex-shrink: 0;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.terms-modal__link { font-size: 11px; color: var(--muted); text-decoration: underline; }
.terms-modal__link:hover { color: var(--text); }
.terms-modal__close-btn { width: 100%; justify-content: center; }
@media (max-width: 420px) {
  /* Owner's own acceptance width (360px): the dialog fills nearly the
     whole viewport and the close button is a full-width bar pinned to
     the bottom of the dialog — reachable with a thumb without having to
     scroll back up past a long terms body first. */
  .terms-modal { padding: 8px; }
  .terms-modal__dialog { max-height: 92vh; border-radius: 12px; }
  .terms-modal__header h2 { font-size: 13px; }
  .terms-modal__close { font-size: 26px; padding: 6px 10px; }
  .terms-modal__footer { padding: 10px 14px calc(10px + env(safe-area-inset-bottom, 0px)); }
}

/* ---------------------------------------------------------------------- */
/* Naming aliases — same rendered result, requested reusable class names   */
/* ---------------------------------------------------------------------- */

.app-shell { display: grid; grid-template-columns: 264px 1fr; min-height: 100vh; }
.nav-group { margin: 5px 0 14px; }
.nav-link { display: flex; align-items: center; gap: 10px; padding: 9px 11px; border-radius: var(--radius-sm); margin: 1px 0; font-size: 11px; font-weight: 600; color: #475467; }
.nav-link:hover { background: #f8fafc; color: var(--text); }
.nav-link.active, .nav-link--active { background: #eff6ff; color: #175cd3; box-shadow: inset 3px 0 0 var(--primary); }
.card-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 14px; }
.card-body { padding: 0; }
.data-table-wrapper { overflow-x: auto; }
.status-badge { display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 999px; font-size: 9.5px; font-weight: 750; white-space: nowrap; background: var(--panel2); color: var(--muted); }
.status-badge--success, .status--success { background: #ecfdf3; color: #067647; }
.status-badge--warning, .status--warning { background: #fffaeb; color: #93370d; }
.status-badge--danger, .status--danger { background: #fef3f2; color: #b42318; }
.status-badge--info, .status--info { background: #eff8ff; color: #175cd3; }
.status-badge--neutral, .status--neutral { background: var(--panel2); color: var(--muted); }
.status--lg { font-size: 11px; padding: 6px 12px; }
.warn-note { margin-top: 14px; padding: 12px 14px; border-radius: 10px; background: #fffaeb; border: 1px solid #fedf89; color: #93370d; }
.warn-note b { display: block; font-size: 10px; margin-bottom: 6px; }
.warn-note ul { margin: 0; padding-left: 16px; font-size: 10.5px; line-height: 1.7; }
.warn-note p { margin: 0; font-size: 10.5px; line-height: 1.7; }
.message-block p:not(.field__hint) { margin: 0 0 16px; color: #344054; font-size: 12.5px; white-space: pre-wrap; }
.message-block p:last-child { margin-bottom: 0; }
.form-shell { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 20px 22px; }
.detail-card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 20px 22px; margin-bottom: 18px; }

/* ---------------------------------------------------------------------- */
/* Hero — 2-column composition (Overview): hero-card + hero-side           */
/* ---------------------------------------------------------------------- */

.hero { display: grid; grid-template-columns: 1.45fr 0.8fr; gap: 16px; margin-bottom: 18px; align-items: stretch; }
.hero-card {
  background: linear-gradient(145deg, #ffffff 0%, #f8fbff 100%);
  border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 28px;
}
.hero-card .eyebrow {
  display: inline-flex; padding: 5px 9px; border: 1px solid #d1e9ff; background: #eff8ff;
  color: #175cd3; border-radius: 999px; font-size: 9px; font-weight: 800; letter-spacing: .7px;
}
.hero-card h2 { font-size: 26px; line-height: 1.15; letter-spacing: -.03em; color: var(--text); margin: 14px 0 10px; }
.hero-card h2 em { font-style: normal; color: var(--primary); }
.hero-card p { color: var(--muted); font-size: 12px; line-height: 1.65; max-width: 560px; margin: 0; }
.hero-metrics { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 22px; }
.hero-metrics .metric-card__value { font-size: 22px; }
.hero-side {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 22px;
}
.hero-side h3 { margin: 0 0 4px; font-size: 12.5px; color: var(--text); }
.hero-side > p { margin: 0 0 14px; color: var(--muted); font-size: 10.5px; }
.hero-side-box {
  padding: 12px 13px; margin-top: 9px; border: 1px solid #eaecf0; border-radius: 10px; background: #f9fafb;
}
.hero-side-box:first-of-type { margin-top: 0; }
a.hero-side-box { display: block; transition: .15s ease; }
a.hero-side-box:hover { border-color: #b2ccff; background: #f8fbff; }
.hero-side-box b { display: block; font-size: 10.5px; color: #344054; }
.hero-side-box span { display: block; color: var(--muted); font-size: 9.5px; margin-top: 4px; line-height: 1.5; }

/* ---------------------------------------------------------------------- */
/* Workflow — compact horizontal step-flow explainer (release lifecycle)   */
/* ---------------------------------------------------------------------- */

.workflow {
  display: flex; align-items: center; gap: 8px; overflow-x: auto;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: 14px; margin: 4px 0 18px;
}
.workflow-step {
  min-width: 132px; flex: 1; padding: 10px 12px; border-radius: 10px; background: #f9fafb;
}
.workflow-step span {
  display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: 50%;
  background: #eff6ff; color: #175cd3; font-size: 9px; font-weight: 800; margin-bottom: 8px;
}
.workflow-step b { display: block; font-size: 10px; color: #344054; }
.workflow-step small { display: block; font-size: 9px; color: var(--muted); margin-top: 3px; }
.workflow-arrow { color: #98a2b3; flex-shrink: 0; font-size: 12px; }

/* ---------------------------------------------------------------------- */
/* Wizard shell — one bordered card: sidebar + content + footer            */
/* ---------------------------------------------------------------------- */

.wizard-shell {
  display: grid; grid-template-columns: 224px 1fr; background: var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
}
.wizard-sidebar { background: #f9fafb; border-right: 1px solid var(--line); padding: 16px 12px; }
.wizard-content { padding: 24px 28px; min-width: 0; }
.wizard-content h2 { margin: 0 0 4px; font-size: 15px; color: var(--text); }
.wizard-footer {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  margin-top: 22px; padding-top: 16px; border-top: 1px solid #eaecf0;
}
.wizard-footer__side { display: flex; gap: 8px; align-items: center; }

@media (max-width: 1150px) {
  .hero { grid-template-columns: 1fr; }
  .hero-metrics { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 900px) {
  .app, .app-shell { grid-template-columns: 1fr; }
  .sidebar { position: relative; height: auto; border-right: 0; border-bottom: 1px solid var(--line); }
  .navgroup, .nav-group { display: grid; grid-template-columns: repeat(2, 1fr); gap: 3px; }
  .navlabel { grid-column: 1 / -1; }
  .topbar { position: relative; padding: 12px 16px; height: auto; flex-wrap: wrap; gap: 10px; }
  .content { padding: 16px; }
  dl.review { grid-template-columns: 1fr; }
  .verification__detail { grid-template-columns: 1fr; }
  .page-header { flex-direction: column; align-items: flex-start; }
  .metric-grid, .metric-grid--3, .metric-grid--2, .hero-metrics { grid-template-columns: 1fr 1fr; }
  .form-grid, .form-grid--3 { grid-template-columns: 1fr; }
  .wizard-layout { grid-template-columns: 1fr; }
  .wizard-nav { position: static; flex-direction: row; overflow-x: auto; }
  .wizard-nav__step { flex-shrink: 0; }
  .wizard-shell { grid-template-columns: 1fr; }
  .wizard-sidebar { border-right: 0; border-bottom: 1px solid var(--line); overflow-x: auto; }
  .wizard-sidebar .wizard-nav { flex-direction: row; }
  .wizard-footer { flex-direction: column; align-items: stretch; }
  .workflow-step { min-width: 118px; }
}

@media (max-width: 600px) {
  .content { padding: 0.75rem; }
  .metric-grid, .metric-grid--3, .metric-grid--2, .hero-metrics { grid-template-columns: 1fr; }
}
