/* Invoicer — visual system.
   Everything is self-hosted: no CDN, no web font, no external asset. The CSP
   forbids them (style-src 'self'), which is also why nothing here relies on
   inline style attributes — dynamic styles go through the CSSOM. */

:root {
  --bg: #f3f5f8;
  --surface: #ffffff;
  --surface-2: #f8f9fb;
  --ink: #0f1623;
  --ink-2: #3f4b5f;
  --muted: #6f7b8d;
  --line: #e2e7ee;
  --line-strong: #c9d1dc;
  --accent: #1f5eff;
  --accent-soft: rgba(31, 94, 255, 0.1);
  --accent-ink: #ffffff;
  --danger: #c0392b;
  --danger-soft: rgba(192, 57, 43, 0.1);
  --ok: #0f7b4f;
  --ok-soft: rgba(15, 123, 79, 0.1);
  --warn: #9a6400;
  --warn-soft: rgba(196, 132, 0, 0.13);
  --info: #2f5f9e;
  --info-soft: rgba(47, 95, 158, 0.1);
  --radius: 12px;
  --radius-sm: 8px;
  --shadow: 0 1px 2px rgba(15, 22, 35, 0.06), 0 8px 24px rgba(15, 22, 35, 0.05);
  --shadow-lg: 0 24px 64px rgba(15, 22, 35, 0.22);
  --sidebar: 17.5rem;
  --nav-bg: #0e1a33;
  --nav-bg-2: #13234a;
  --nav-ink: #b9c4db;
  --nav-muted: #6f80a6;
  --brand-grad: linear-gradient(135deg, #2f6bff 0%, #5b4df5 60%, #7b3ff2 100%);
  --nav-current: rgba(91, 124, 255, 0.22);
  --nav-current-edge: #7d9bff;
  --logo-from: #2f6bff;
  --logo-to: #7b3ff2;
  --logo-lines: #5a55f5;
  --accent-ring: rgba(31, 94, 255, 0.16);
  --primary-shadow: rgba(47, 107, 255, 0.55);
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

/* Themes (Settings > Tema tampilan; migration 0032): a workspace's colours,
   set on <html data-theme> (state.js applyTheme). Only the app's own colours
   change; the documents keep theirs. "biru" repeats :root, so an element can
   carry it inside another theme (the Settings swatches). Accent and
   gradient keep white text at 4.5:1 or more; the logo's may be brighter. */
[data-theme="biru"] {
  --accent: #1f5eff;
  --accent-soft: rgba(31, 94, 255, 0.1);
  --nav-bg: #0e1a33;
  --nav-bg-2: #13234a;
  --nav-ink: #b9c4db;
  --nav-muted: #6f80a6;
  --brand-grad: linear-gradient(135deg, #2f6bff 0%, #5b4df5 60%, #7b3ff2 100%);
  --nav-current: rgba(91, 124, 255, 0.22);
  --nav-current-edge: #7d9bff;
  --logo-from: #2f6bff;
  --logo-to: #7b3ff2;
  --logo-lines: #5a55f5;
  --accent-ring: rgba(31, 94, 255, 0.16);
  --primary-shadow: rgba(47, 107, 255, 0.55);
}
[data-theme="kamaraya"] {
  --logo-lines: #047857;
  --accent-ring: rgba(4, 120, 87, 0.18);
  --primary-shadow: rgba(4, 120, 87, 0.5);
  --logo-from: #10b981;
  --logo-to: #047857;
  --accent: #047857;
  --accent-soft: rgba(4, 120, 87, 0.1);
  --nav-bg: #053d30;
  --nav-bg-2: #065f46;
  --nav-ink: #c3e8da;
  --nav-muted: #74b39b;
  --brand-grad: linear-gradient(135deg, #047857 0%, #065f46 55%, #064e3b 100%);
  --nav-current: rgba(52, 211, 153, 0.2);
  --nav-current-edge: #34d399;
}
[data-theme="ungu"] {
  --logo-lines: #6d28d9;
  --accent-ring: rgba(109, 40, 217, 0.16);
  --primary-shadow: rgba(109, 40, 217, 0.5);
  --logo-from: #8b5cf6;
  --logo-to: #5b21b6;
  --accent: #6d28d9;
  --accent-soft: rgba(109, 40, 217, 0.1);
  --nav-bg: #1d1035;
  --nav-bg-2: #2d1856;
  --nav-ink: #d3c6ee;
  --nav-muted: #8f7fb8;
  --brand-grad: linear-gradient(135deg, #7c3aed 0%, #6d28d9 55%, #5b21b6 100%);
  --nav-current: rgba(167, 139, 250, 0.22);
  --nav-current-edge: #a78bfa;
}
[data-theme="bata"] {
  --logo-lines: #c2410c;
  --accent-ring: rgba(194, 65, 12, 0.16);
  --primary-shadow: rgba(194, 65, 12, 0.5);
  --logo-from: #f97316;
  --logo-to: #c2410c;
  --accent: #c2410c;
  --accent-soft: rgba(194, 65, 12, 0.1);
  --nav-bg: #2a130b;
  --nav-bg-2: #401c0f;
  --nav-ink: #efd0c1;
  --nav-muted: #b5866f;
  --brand-grad: linear-gradient(135deg, #c2410c 0%, #b23a0b 55%, #9a3412 100%);
  --nav-current: rgba(251, 146, 60, 0.2);
  --nav-current-edge: #fb923c;
}
[data-theme="grafit"] {
  --logo-lines: #334155;
  --accent-ring: rgba(51, 65, 85, 0.18);
  --primary-shadow: rgba(51, 65, 85, 0.45);
  --logo-from: #64748b;
  --logo-to: #334155;
  --accent: #334155;
  --accent-soft: rgba(51, 65, 85, 0.1);
  --nav-bg: #111827;
  --nav-bg-2: #1f2937;
  --nav-ink: #cbd5e1;
  --nav-muted: #7d8899;
  --brand-grad: linear-gradient(135deg, #64748b 0%, #475569 55%, #334155 100%);
  --nav-current: rgba(148, 163, 184, 0.2);
  --nav-current-edge: #94a3b8;
}

* { box-sizing: border-box; }

/* Scrollbars in the app's colours: thin and quiet on the light page, light
   on the dark sidebar. scrollbar-color is the standard (Chrome 121+,
   Firefox); the ::-webkit- rules cover older Chromium and Safari. */
html { scrollbar-width: thin; scrollbar-color: #c3cbd8 transparent; }
.sidebar { scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.2) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #c3cbd8; border-radius: 999px; border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background-color: #9aa6b8; }
.sidebar::-webkit-scrollbar-thumb { background-color: rgba(255, 255, 255, 0.2); }

html, body, #root, .app { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  -webkit-font-smoothing: antialiased;
}

h1 { font-size: 1.45rem; letter-spacing: -0.015em; margin: 0; line-height: 1.25; }
h2 { font-size: 1.05rem; margin: 0; }
a { color: var(--accent); }

.muted { color: var(--muted); }
.small { font-size: 0.84rem; }
.error { color: var(--danger); }
.right { text-align: right; }
.pad { padding: 1.25rem; margin: 0; }
.pad-lg { padding: 1.5rem; }
.mono { font-family: var(--mono); font-size: 0.92em; }
.num { font-variant-numeric: tabular-nums; text-align: right; }
/* minmax(0, 1fr) as on .page: a wide table scrolls inside its wrapper instead of
   widening the stack, and with it the page, past a phone's screen. */
.stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; }
.stack > p { margin: 0; }
.row { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; }
.row.end { justify-content: flex-end; }
.row.nowrap { flex-wrap: nowrap; }
.row.between { justify-content: space-between; }

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

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.notice {
  margin: 0 0 1rem;
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius-sm);
  background: var(--info-soft);
  color: var(--info);
  font-size: 0.9rem;
}
.notice.warn { background: var(--warn-soft); color: var(--warn); }
.notice.danger { background: var(--danger-soft); color: var(--danger); }

.empty { padding: 2.25rem 1.25rem 2.5rem; text-align: center; color: var(--muted); }
.empty p { margin: 0 0 0.9rem; }
.empty-art { display: block; width: 9rem; height: auto; margin: 0 auto 0.75rem; }

/* --- icons ------------------------------------------------------------------- */

.icon {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.brand-logo { width: 2rem; height: 2rem; flex: none; display: block; }
/* A white-label logo (a company's, any colours): on a light tile, whole. */
img.brand-image { width: auto; max-width: 7.5rem; object-fit: contain; background: #fff; border-radius: 0.45rem; padding: 0.15rem; }
.brand span { min-width: 0; overflow-wrap: anywhere; }
/* A company's name is longer than "Invoicer": smaller, so it takes two lines, not three. */
.brand:has(img.brand-image) span { font-size: 0.92rem; line-height: 1.25; }

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

button { font: inherit; color: inherit; }

.btn {
  font: inherit;
  font-size: 0.93rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  padding: 0.5rem 0.95rem;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.15s, background 0.15s, color 0.15s, filter 0.15s;
}
.btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.btn:disabled, .btn.disabled { opacity: 0.5; cursor: not-allowed; }
/* Hidden from sight but not from the keyboard or a screen reader: the file
   input behind a styled label stays focusable. */
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
label.btn:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
.image-field { display: grid; gap: 0.4rem; margin-bottom: 1rem; }
.image-preview { max-height: 64px; max-width: 220px; object-fit: contain; padding: 0.4rem; border: 1px solid var(--line); border-radius: var(--radius-sm); background: #fff; }
.image-empty { padding: 0.6rem 0.8rem; border: 1px dashed var(--line); border-radius: var(--radius-sm); }
.btn.small { padding: 0.3rem 0.65rem; font-size: 0.85rem; }
a.btn, a.icon-btn { text-decoration: none; }
.btn.block { width: 100%; }

.btn .icon { width: 1.05rem; height: 1.05rem; }
.btn.primary {
  background: var(--brand-grad);
  border-color: transparent;
  color: var(--accent-ink);
  font-weight: 600;
  box-shadow: 0 6px 16px -6px var(--primary-shadow);
}
.btn.primary:hover:not(:disabled) { filter: brightness(1.07); color: var(--accent-ink); border-color: transparent; }

.btn.danger { background: var(--danger); border-color: var(--danger); color: #fff; font-weight: 560; }
.btn.danger:hover:not(:disabled) { filter: brightness(1.08); color: #fff; }

.btn.ghost { border-color: transparent; background: transparent; color: var(--ink-2); }
.btn.ghost:hover:not(:disabled) { background: rgba(15, 22, 35, 0.05); color: var(--ink); border-color: transparent; }

.btn.subtle { background: var(--surface-2); border-color: var(--line); color: var(--ink-2); }

.icon-btn {
  border: none;
  background: transparent;
  font-size: 1.35rem;
  line-height: 1;
  display: inline-grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--ink-2);
}
.icon-btn:hover { background: rgba(15, 22, 35, 0.06); }
.icon-btn .icon { width: 1.25rem; height: 1.25rem; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

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

.form { display: grid; gap: 1rem; }

/* A fieldset that only groups (a read-only form disables its controls). */
fieldset.bare { border: 0; margin: 0; padding: 0; min-width: 0; }
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: 0.9rem 1rem;
}

.field { display: grid; gap: 0.3rem; align-content: start; min-width: 0; }
.field.wide { grid-column: 1 / -1; }
.field > label, .field .label, .label {
  font-size: 0.83rem;
  color: var(--ink-2);
  font-weight: 560;
}
.req { color: var(--danger); }
.blank { color: var(--muted); }
.field-error { margin: 0; color: var(--danger); font-size: 0.82rem; }
.field-hint { margin: 0; color: var(--muted); font-size: 0.8rem; }

input[type="text"], input[type="password"], input[type="email"], input[type="tel"], input[type="url"],
input[type="search"], input[type="date"], input:not([type]), select, textarea {
  font: inherit;
  font-size: 0.94rem;
  width: 100%;
  min-width: 0;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
}
textarea { resize: vertical; }
/* One height for every single-line control, so a text box, a date and a
   select side by side line up, and so do the labels above them. */
input[type="text"], input[type="password"], input[type="email"], input[type="tel"], input[type="url"],
input[type="search"], input[type="date"], input:not([type]), select { min-height: 2.5rem; }
input:disabled, select:disabled, textarea:disabled { background: var(--surface-2); color: var(--ink-2); }
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-ring);
}
.field.invalid input, .field.invalid select, .field.invalid textarea { border-color: var(--danger); }
input.num { text-align: right; font-variant-numeric: tabular-nums; }

.check {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.92rem;
  cursor: pointer;
  user-select: none;
}
.check input { width: 1rem; height: 1rem; accent-color: var(--accent); }
.field > label.check { font-size: 0.92rem; font-weight: 400; color: var(--ink); }

.form-foot { display: grid; gap: 0.6rem; padding-top: 0.25rem; }
.form-foot .error { margin: 0; }

.section-title { margin: 0.4rem 0 0.1rem; font-size: 0.98rem; }
.section-title:first-child { margin-top: 0; }

.inline-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  gap: 0.8rem;
  align-items: start;
  margin-top: 1rem;
}

/* --- splash and auth ------------------------------------------------------------ */

.splash { min-height: 100%; display: grid; place-items: center; padding: 2rem; }

/* Sign-in: the form, and on a wide screen a panel beside it that says what
   the app is for. */
.auth { min-height: 100%; display: grid; grid-template-columns: minmax(0, 1fr); place-items: center; padding: 2rem 1rem; }
.auth-hero { display: none; }
.auth-card { width: min(25rem, 100%); padding: 2rem; display: grid; gap: 0.9rem; box-shadow: var(--shadow-lg); }
.auth-card > .muted { margin: -0.4rem 0 0.4rem; }
.brand-mark { display: flex; align-items: center; gap: 0.6rem; font-weight: 750; font-size: 1.4rem; letter-spacing: -0.03em; }
.brand-mark .brand-logo { width: 2.4rem; height: 2.4rem; }
.brand-logo .logo-from { stop-color: var(--logo-from); }
.brand-logo .logo-to { stop-color: var(--logo-to); }
.brand-logo .logo-lines { stroke: var(--logo-lines); }
@media (min-width: 900px) {
  .auth { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); padding: 0; place-items: stretch; }
  .auth-hero {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1rem;
    padding: 3.5rem clamp(2rem, 5vw, 5rem);
    color: #fff;
    background:
      radial-gradient(60rem 30rem at 110% -10%, rgba(255, 255, 255, 0.18), transparent 60%),
      radial-gradient(40rem 30rem at -20% 120%, rgba(25, 195, 125, 0.25), transparent 60%),
      var(--brand-grad);
  }
  .auth-hero-brand { display: flex; align-items: center; gap: 0.65rem; font-weight: 750; font-size: 1.35rem; letter-spacing: -0.02em; }
  .auth-hero-brand .brand-logo { width: 2.5rem; height: 2.5rem; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); border-radius: 0.6rem; }
  .auth-hero h2 { font-size: clamp(1.6rem, 2.4vw, 2.2rem); line-height: 1.2; letter-spacing: -0.02em; max-width: 30rem; margin-top: 1.5rem; }
  .auth-hero p { margin: 0; opacity: 0.85; max-width: 28rem; font-size: 1.02rem; }
  .auth-art { width: min(100%, 26rem); height: auto; margin-top: 1.5rem; }
  .auth-card { align-self: center; justify-self: center; }
  /* The panel already names the app. */
  .auth-card .brand-mark { display: none; }
}

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

.shell { display: grid; grid-template-columns: var(--sidebar) 1fr; min-height: 100%; }

.sidebar {
  background: linear-gradient(180deg, var(--nav-bg) 0%, var(--nav-bg-2) 100%);
  color: var(--nav-ink);
  border-right: 1px solid rgba(255, 255, 255, 0.04);
  padding: 1.1rem 0.9rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

.sidebar-top { display: flex; align-items: center; justify-content: space-between; padding: 0 0.35rem; }
.brand { display: flex; align-items: center; gap: 0.6rem; font-weight: 750; letter-spacing: -0.02em; font-size: 1.12rem; color: #fff; text-decoration: none; }
.sidebar .icon-btn { color: #dfe6f5; }
.sidebar .icon-btn:hover { background: rgba(255, 255, 255, 0.08); }
.menu-toggle { display: none; }

.switcher { display: grid; gap: 0.3rem; padding: 0.7rem; border-radius: var(--radius-sm); background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.08); }
.switcher .label { color: var(--nav-muted); }
.switcher.single strong { font-size: 0.92rem; color: #fff; }
.sidebar .switcher select { background-color: rgba(255, 255, 255, 0.07); color: #eef2fb; border-color: rgba(255, 255, 255, 0.14); text-overflow: ellipsis; }
.sidebar .switcher select option { color: var(--ink); background: #fff; }

nav { display: grid; gap: 0.1rem; }
.nav-section {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--nav-muted);
  font-weight: 650;
  padding: 0.9rem 0.6rem 0.3rem;
}
.nav-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.45rem 0.65rem;
  border-radius: var(--radius-sm);
  color: var(--nav-ink);
  text-decoration: none;
  font-size: 0.93rem;
  transition: background 0.15s, color 0.15s;
}
.nav-item .icon { width: 1.15rem; height: 1.15rem; opacity: 0.85; }
.nav-item:hover { background: rgba(255, 255, 255, 0.06); color: #fff; }
.nav-item.current { background: var(--nav-current); color: #fff; font-weight: 600; box-shadow: inset 3px 0 0 var(--nav-current-edge); }
.nav-item.current .icon { opacity: 1; }
/* Drafts waiting for approval: a warn badge at the end of "Persetujuan". */
.nav-count { margin-left: auto; min-width: 1.4rem; justify-content: center; font-variant-numeric: tabular-nums; }

.sidebar-foot {
  margin-top: auto;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  padding-top: 0.9rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.who-name { font-weight: 600; font-size: 0.9rem; color: #fff; text-decoration: none; display: block; }
.sidebar .role { color: var(--nav-muted); }
.sidebar .btn.ghost { color: var(--nav-ink); }
.sidebar .btn.ghost:hover:not(:disabled) { background: rgba(255, 255, 255, 0.08); color: #fff; }
.sidebar .error { color: #ffb4a8; }
/* The approval count, legible on the dark sidebar. */
.sidebar .badge.warn { background: #f6b93b; color: #2b1d00; }
a.who-name:hover { text-decoration: underline; }
.card.narrow { max-width: 32rem; }

.content { padding: 1.75rem 2rem 3rem; min-width: 0; }
/* minmax(0, 1fr): a grid item's min-width is otherwise its content, so one
   wide table would widen the whole page past a phone's screen instead of
   scrolling inside its own wrapper. */
.page { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.1rem; align-content: start; max-width: 80rem; }

.page-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 1rem; flex-wrap: wrap; }
.page-head p { margin: 0.25rem 0 0; }
/* A customer or company name with no spaces still wraps instead of widening the page. */
.page-head > div:first-child { min-width: 0; max-width: 100%; }
.page-head h1, .page-head p { overflow-wrap: anywhere; }
.page-actions { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }

.toolbar { display: flex; gap: 0.9rem; align-items: center; flex-wrap: wrap; }
.toolbar .search { max-width: 22rem; }

/* The base line is an inset shadow, so a tab's underline can sit on it
   without overflowing the strip (which scrolls sideways on a phone). */
.tabs { display: flex; gap: 0.25rem; box-shadow: inset 0 -1px 0 var(--line); overflow-x: auto; min-width: 0; max-width: 100%; }
.tab {
  padding: 0.55rem 0.85rem;
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  font-size: 0.93rem;
}
.tab:hover { color: var(--ink); }
.tab.current { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }

/* --- tables ------------------------------------------------------------------------ */

/* The wrapper scrolls in both directions, bounded to the viewport, so the
   header row can stay sticky while a long list scrolls — sticky only sticks
   within the nearest scrolling box, and an overflow-x wrapper is one. */
.table-wrap { overflow: auto; max-height: calc(100vh - 12rem); }

table.data { width: 100%; border-collapse: collapse; }
table.data th, table.data td { padding: 0.65rem 1rem; text-align: left; border-bottom: 1px solid var(--line); }
/* A long word (a name typed without spaces) wraps rather than widening its
   column off the screen; nowrap cells (numbers, dates, codes) keep theirs. */
table.data td { overflow-wrap: anywhere; }
table.data th {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  font-weight: 650;
  background: var(--surface-2);
  white-space: nowrap;
  position: sticky;
  top: 0;
  z-index: 1;
}
table.data th.right, table.data td.right { text-align: right; }
/* A sortable column's header is a button: the arrows show which column sorts
   the table and which way; faint on the others until hovered. */
.th-sort {
  display: inline-flex; align-items: center; gap: 0.3rem; padding: 0; border: 0; background: none;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit; cursor: pointer;
}
.th-sort .icon { width: 0.95rem; height: 0.95rem; opacity: 0.35; flex: none; }
.th-sort:hover, .th-sort.on { color: var(--ink); }
.th-sort:hover .icon, .th-sort.on .icon { opacity: 1; }
.th-sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
/* Search, filters and (on a phone) the sort menu, above a table. */
.table-toolbar { gap: 0.6rem; }
.table-toolbar .search { flex: 1 1 16rem; }
.table-toolbar select.filter { width: auto; max-width: 18rem; flex: 0 1 auto; }
.sort-menu { display: none; }
/* Words a list's card needs that its table's header says instead. */
.card-only { display: none; }
.load-more { display: flex; justify-content: center; padding: 0.75rem; border-top: 1px solid var(--line); }
.load-more p { margin: 0; text-align: center; }
@media (max-width: 560px) {
  .table-toolbar .search { flex-basis: 100%; max-width: none; }
  .table-toolbar select.filter { flex: 1 1 calc(50% - 0.3rem); max-width: none; }
  .sort-menu { display: block; }
}
table.data th.mono { font-family: inherit; font-size: 0.8rem; }
table.data tbody tr:last-child td { border-bottom: none; }
table.data tbody tr.clickable { cursor: pointer; }
table.data tbody tr.clickable:hover, table.data tbody tr.clickable:focus-visible { background: var(--surface-2); outline: none; }
table.data tbody tr.inactive td { color: var(--muted); }
table.data td.num, table.data th.num { text-align: right; white-space: nowrap; }
/* Codes and figures never break mid-token ("C-" / "1h31o"). */
table.data td.mono, table.data td.right, table.data td.date { white-space: nowrap; }

/* --- badges -------------------------------------------------------------------------- */

.badge {
  display: inline-block;
  padding: 0.08rem 0.5rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  background: rgba(15, 22, 35, 0.07);
  color: var(--ink-2);
}
.badge.ok { background: var(--ok-soft); color: var(--ok); }
.badge.warn { background: var(--warn-soft); color: var(--warn); }
.badge.danger { background: var(--danger-soft); color: var(--danger); }
.badge.info { background: var(--info-soft); color: var(--info); }

/* --- dialogs and toasts ------------------------------------------------------------------ */

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 22, 35, 0.4);
  display: grid;
  place-items: center;
  padding: 1rem;
  z-index: 50;
}
.modal {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  width: min(32rem, 100%);
  max-height: calc(100vh - 2rem);
  display: flex;
  flex-direction: column;
}
.modal.wide { width: min(52rem, 100%); }
.modal-head { display: flex; justify-content: space-between; align-items: center; padding: 1rem 1.25rem 0.5rem; }
.modal-body { padding: 0.5rem 1.25rem 1.25rem; overflow-y: auto; }
.modal-foot { padding: 0.75rem 1.25rem 1rem; border-top: 1px solid var(--line); }
.confirm-text { color: var(--ink-2); }

/* The photo viewer (lib/lightbox.js): the photo on a dark stage, its title
   above, its tools in a bar below. */
.lightbox-open, .lightbox-open body { overflow: hidden; }
.lightbox {
  position: fixed; inset: 0; z-index: 60; display: flex; flex-direction: column;
  background: rgba(8, 12, 20, 0.94); color: #eef2f8;
}
.lightbox .icon-btn { color: #eef2f8; width: 2.5rem; height: 2.5rem; }
.lightbox .icon-btn:hover:not(:disabled) { background: rgba(255, 255, 255, 0.12); }
.lightbox .icon-btn:disabled { opacity: 0.35; cursor: default; }
.lightbox-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 0.75rem max(1rem, env(safe-area-inset-right)) 0.75rem max(1rem, env(safe-area-inset-left));
}
.lightbox-title { display: flex; flex-direction: column; min-width: 0; }
.lightbox-title > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lightbox-title span { font-size: 0.85rem; color: #aab5c8; }
.lightbox-stage {
  position: relative; flex: 1; overflow: hidden;
  touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.lightbox-stage.ready { cursor: zoom-in; }
.lightbox-stage.pannable { cursor: grab; }
.lightbox-stage.pannable.dragging { cursor: grabbing; }
.lightbox-img {
  position: absolute; left: 50%; top: 50%; max-width: none; transform-origin: center;
  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.5); background: #fff;
}
.lightbox-stage:not(.ready) .lightbox-img { visibility: hidden; }
.lightbox-img.ease { transition: transform 0.18s ease-out; }
@media (prefers-reduced-motion: reduce) { .lightbox-img.ease { transition: none; } }
.lightbox-status { position: absolute; inset: 0; display: grid; place-items: center; margin: 0; color: #aab5c8; }
.lightbox-status.error { color: #ffb4a8; }
.lightbox-tools {
  align-self: center; display: flex; align-items: center; gap: 0.15rem;
  margin: 0.5rem 1rem max(0.75rem, env(safe-area-inset-bottom));
  padding: 0.3rem; border-radius: 999px; background: rgba(255, 255, 255, 0.1);
}
.lightbox-zoom {
  min-width: 3.75rem; height: 2.5rem; border: 0; border-radius: var(--radius-sm); background: transparent;
  color: inherit; font: inherit; font-size: 0.9rem; font-variant-numeric: tabular-nums; cursor: pointer;
}
.lightbox-zoom:hover { background: rgba(255, 255, 255, 0.12); }
.lightbox-sep { width: 1px; height: 1.5rem; margin: 0 0.25rem; background: rgba(255, 255, 255, 0.2); }
@media (max-width: 420px) {
  .lightbox .icon-btn { width: 2.35rem; }
  .lightbox-zoom { min-width: 3.25rem; }
  .lightbox-sep { margin: 0 0.1rem; }
}

.toasts {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  display: grid;
  gap: 0.5rem;
  z-index: 70; /* above the photo viewer */
  max-width: min(24rem, calc(100vw - 2rem));
}
.toast {
  padding: 0.7rem 1rem;
  border-radius: var(--radius-sm);
  background: var(--ink);
  color: #fff;
  box-shadow: var(--shadow-lg);
  font-size: 0.92rem;
}
.toast.error { background: var(--danger); }
.toast.info { background: var(--info); }

/* --- small screens ---------------------------------------------------------------------- */

@media (max-width: 860px) {
  /* One column: the header row takes its content height, the page the rest.
     With two auto rows, min-height: 100% would stretch the header too. */
  .shell { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  /* 100dvh where supported: the open menu's last row stays above a phone
     browser's toolbar. */
  .sidebar { position: sticky; top: 0; z-index: 20; height: auto; max-height: 100vh; max-height: 100dvh; border-right: none; gap: 0.6rem; padding: 0.75rem 0.9rem; }
  /* What takes focus is not left under the sticky header. */
  html { scroll-padding-top: 4.5rem; }
  .menu-toggle { display: inline-grid; place-items: center; }
  .sidebar nav, .sidebar .sidebar-foot, .sidebar .switcher { display: none; }
  .menu-open .sidebar nav, .menu-open .sidebar .sidebar-foot { display: grid; }
  .menu-open .sidebar .switcher { display: grid; }
  .menu-open .sidebar .sidebar-foot { display: flex; }
  .content { padding: 1.1rem 1rem 3rem; }
  .inline-form { grid-template-columns: 1fr; }
  .table-wrap { max-height: none; }
}

/* Touch screens: 16px inputs (iOS zooms the page into anything smaller) and
   44px tap targets. */
@media (max-width: 860px), (pointer: coarse) {
  /* Same selector list as the base rule: a bare `input` would lose to its
     attribute selectors on specificity and leave the 15px size in place. */
  input[type="text"], input[type="password"], input[type="email"], input[type="tel"], input[type="url"],
  input[type="search"], input[type="date"], input:not([type]), select, textarea, .btn { font-size: 16px; }
  input[type="text"], input[type="password"], input[type="email"], input[type="tel"], input[type="url"],
  input[type="search"], input[type="date"], input:not([type]), select { min-height: 2.75rem; }
  /* A link inside a table cell (an invoice number) is a tap target too. */
  table.data td a { display: inline-block; padding: 0.4rem 0; }
  table.data td a.icon-btn { display: inline-grid; padding: 0; }
  .icon-btn { width: 2.75rem; height: 2.75rem; }
  .btn, .btn.small { min-height: 2.75rem; }
  .check { min-height: 2.75rem; }
  .check input { width: 1.5rem; height: 1.5rem; }
  .brand { padding: 0.6rem 0; }
  .nav-item { padding: 0.7rem 0.75rem; }
}

/* --- invoice editor ----------------------------------------------------------- */

.editor-page { max-width: 110rem; }
.editor {
  display: grid;
  /* The preview never narrower than its document (30rem plus its padding);
     below 1260px the two stack instead (see the media query further down). */
  grid-template-columns: minmax(0, 1fr) minmax(32rem, 1fr);
  gap: 1.25rem;
  align-items: start;
}
.editor-form { display: grid; gap: 1rem; }
.editor-preview { position: sticky; top: 1rem; display: grid; gap: 0.5rem; max-height: calc(100vh - 2rem); overflow: auto; container-type: inline-size; }
/* The preview is the same document at a compact scale, so the whole width
   fits beside the form instead of hiding its last column. */
.editor-preview .invoice-doc { font-size: 0.74rem; padding: 1.4rem; min-width: 30rem; }
.editor-preview .doc-lines th, .editor-preview .doc-lines td { padding: 0.35rem 0.3rem; }
.editor-preview .doc-foot { grid-template-columns: minmax(0, 1fr) minmax(12rem, 1fr); gap: 1rem; }
.editor-preview .watermark { font-size: 3rem; }
.preview-bar { display: flex; justify-content: space-between; gap: 1rem; padding: 0 0.25rem; }

/* Account -> Passkey: one row per passkey, the remove button at the end. */
.key-list { list-style: none; margin: 0; padding: 0; border: 1px solid var(--line); border-radius: var(--radius-sm); }
.key-list li { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.7rem 0.9rem; }
.key-list li + li { border-top: 1px solid var(--line); }
.key-text { display: grid; gap: 0.15rem; min-width: 0; overflow-wrap: anywhere; }
.line-list { display: grid; gap: 0.6rem; }
.line-row {
  display: grid;
  grid-template-columns: 1.6rem minmax(0, 1fr) auto;
  gap: 0.6rem;
  padding: 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.line-no { font-weight: 650; color: var(--muted); padding-top: 0.55rem; font-variant-numeric: tabular-nums; }
.line-fields { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.5rem; min-width: 0; container-type: inline-size; }
.line-top { display: grid; grid-template-columns: minmax(8rem, 14rem) minmax(0, 1fr); gap: 0.5rem; }
.line-top.solo { grid-template-columns: minmax(0, 1fr); }
.line-detail { min-height: 2.6rem; resize: vertical; }
.line-period { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 0.5rem; max-width: 26rem; }
.line-nums { display: grid; grid-template-columns: minmax(0, 0.8fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.3fr) minmax(0, 1.5fr);
  gap: 0.5rem; align-items: end; }
.mini { display: grid; gap: 0.2rem; min-width: 0; }
.mini > span { font-size: 0.78rem; color: var(--muted); font-weight: 600; }
/* The figures of a line by its width: one row when wide; three to a row
   (the discount taking two) when not; on a narrow line (a phone, or the
   form beside the preview) two to a row, the discount across it, and the
   picker above the name. */
@container (width < 40rem) {
  .line-nums { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .mini.discount { grid-column: span 2; }
}
@container (width < 30rem) {
  .line-top { grid-template-columns: minmax(0, 1fr); }
  .line-nums { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mini.discount { grid-column: 1 / -1; }
}
/* The line's total: its own row, label and amount together at the right edge,
   under the price and discount it comes from. */
.mini.subtotal { grid-column: 1 / -1; display: flex; justify-content: flex-end; align-items: baseline; gap: 0.75rem;
  padding-top: 0.45rem; border-top: 1px dashed var(--line); }
.mini.subtotal strong { font-size: 1rem; }
.line-remove { align-self: start; }
.input-pair { display: grid; grid-template-columns: 4.2rem minmax(0, 1fr); gap: 0.35rem; }
.check-list { display: grid; gap: 0.25rem; }
/* An invoice's billing users: a long list scrolls in its box. */
.access-list { max-height: 18rem; overflow-y: auto; padding: 0.25rem 0; }
.wide-row { grid-column: 1 / -1; margin: 0; }

.payment-summary { display: flex; gap: 2rem; flex-wrap: wrap; }
.recurring-row { padding: 0.6rem 0; border-top: 1px solid var(--line); }
.recurring-row:first-child { border-top: 0; }
/* One date and one button: sized to their content, not stretched across the card. */
.inline-form.share-form { grid-template-columns: minmax(0, 16rem) minmax(0, 12rem) auto; justify-content: start; align-items: end; }
.inline-form.wa-test { grid-template-columns: minmax(0, 16rem) auto; justify-content: start; align-items: start; }
.inline-form.wa-test > .btn { margin-top: 1.6rem; }
/* An invoice's withholding slip: its number, date and buttons in a row. */
.inline-form.slip-form { grid-template-columns: minmax(0, 16rem) minmax(0, 11rem) auto; justify-content: start; align-items: end; }
@media (max-width: 860px) {
  .inline-form.share-form, .inline-form.wa-test, .inline-form.slip-form { grid-template-columns: 1fr; }
  .inline-form.wa-test > .btn { margin-top: 0; }
}
.share-wa { border-top: 1px solid var(--line); padding-top: 0.75rem; }
.share-fresh { padding: 0.75rem; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2); }
.share-fresh input { flex: 1 1 16rem; min-width: 0; }
/* A live link's open, copy and revoke. */
.share-actions { gap: 0.25rem; }
/* A link's "Saat dibuka", changed in its row. */
table.data select.compact { width: auto; min-height: 2rem; padding: 0.2rem 0.45rem; font-size: 0.85rem; }
.payment-summary strong { font-size: 1.1rem; }
/* Recording a payment: the proof chosen, before it is sent. */
.proof-chosen { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.25rem 0.25rem 0.25rem 0.6rem;
  border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); max-width: 100%; overflow-wrap: anywhere; }
.proof-link { font-weight: 600; }
/* A list on a phone: each row a small card rather than a table scrolled
   sideways. The row's subject (cell-main) and its figure (cell-figure) share
   the first line; the other cells follow below, small; cell-wide cells are
   left out. */
@media (max-width: 560px) {
  table.data.cards thead { display: none; }
  table.data.cards, table.data.cards tbody { display: block; }
  table.data.cards tr { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.3rem 0.75rem; padding: 0.75rem 1rem; border-top: 1px solid var(--line); }
  table.data.cards tbody tr:first-child { border-top: 0; }
  table.data.cards tr::after { content: ""; order: 2; flex-basis: 100%; height: 0; }
  table.data.cards td { order: 3; display: block; padding: 0; border: 0; text-align: left; font-size: 0.85rem; color: var(--muted); white-space: normal; }
  table.data.cards td.cell-main { order: 0; flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; font-size: 0.95rem; font-weight: 600; color: var(--ink); }
  table.data.cards td.cell-figure { order: 1; margin-left: auto; text-align: right; font-size: 0.95rem; font-weight: 650; color: var(--ink); white-space: nowrap; }
  table.data.cards td.cell-wide { display: none; }
  table.data.cards .card-only { display: inline; }
  table.data.cards td.cell-actions { flex-basis: 100%; }
  table.data.cards td.cell-actions:empty { display: none; }
  table.data.cards td.cell-actions .row { justify-content: flex-start; }
  /* A cell holding only the "—" of a missing value (Blank) says nothing on a card. */
  table.data.cards td:has(> .blank:only-child) { display: none; }
}

/* On a phone each receipt is a small card: date and amount, then where it
   went, then its proof and actions — not a table scrolled sideways. */
@media (max-width: 560px) {
  table.data.pay-table thead { display: none; }
  table.data.pay-table tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.2rem 0.75rem; padding: 0.7rem 0; border-top: 1px solid var(--line); }
  table.data.pay-table tbody tr:first-child { border-top: 0; }
  table.data.pay-table td { display: block; padding: 0; border: 0; text-align: left; }
  table.data.pay-table td.date { grid-column: 1; grid-row: 1; font-weight: 600; }
  table.data.pay-table td.num { grid-column: 2; grid-row: 1; text-align: right; font-weight: 600; }
  table.data.pay-table td.pay-bank, table.data.pay-table td.pay-ref { grid-column: 1 / -1; color: var(--muted); font-size: 0.85rem; }
  table.data.pay-table td.blank { display: none; }
  table.data.pay-table td.pay-proof { grid-column: 1; }
  table.data.pay-table td.pay-actions { grid-column: 2; align-self: center; }
}

@media (max-width: 1260px) {
  .editor { grid-template-columns: minmax(0, 1fr); }
  .editor-preview { position: static; max-height: none; }
}
@media (max-width: 560px) {
  /* For a browser without container queries; the @container rules above say
     the same for every narrow line. */
  .line-top { grid-template-columns: minmax(0, 1fr); }
  .line-nums { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .mini.discount { grid-column: 1 / -1; }
  /* The number and the remove button share a header row so the fields get the full width. */
  .line-row { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "no remove" "fields fields"; padding: 0.6rem; }
  .line-no { grid-area: no; align-self: center; padding-top: 0; }
  .line-fields { grid-area: fields; }
  .line-remove { grid-area: remove; }
}

/* Settings > Tema tampilan: one card per theme, its sidebar and accent as a
   swatch — read from the theme itself (the swatch carries data-theme). */
.theme-field { border: 0; padding: 0; margin: 0.25rem 0 0.5rem; min-width: 0; }
.theme-picks { display: grid; grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); gap: 0.6rem; margin-top: 0.35rem; }
.theme-pick { display: flex; align-items: center; gap: 0.55rem; padding: 0.55rem 0.7rem; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); cursor: pointer; font-size: 0.9rem; }
.theme-pick.on { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); font-weight: 600; }
.theme-pick input { position: absolute; opacity: 0; pointer-events: none; }
.theme-pick:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.theme-swatch { display: inline-flex; width: 2.1rem; height: 1.4rem; border-radius: 0.4rem; overflow: hidden; flex: none; border: 1px solid var(--line); }
.theme-swatch span { flex: 1; }
.theme-swatch span:first-child { background: var(--nav-bg); }
.theme-swatch span:last-child { background: var(--accent); }

/* Template Item (line-templates.js): each line a card of its fields; the
   editor's template picker beside "Tambah baris". */
.tpl-lines { display: grid; gap: 0.75rem; }
/* As the invoice editor's line rows: flat, bordered, numbered. */
.tpl-line { padding: 0.75rem; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2); }
.tpl-line-head { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.5rem; }
.tpl-line-head .line-no { padding-top: 0; }
.tpl-line-head .tpl-product { flex: 1; min-width: 0; }
/* Name and description across; the figures paired, two or more a row. */
.tpl-line-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr)); gap: 0.6rem 0.75rem; }
.tpl-line-grid .field.wide { grid-column: 1 / -1; }
.tpl-foot { flex-wrap: wrap; gap: 0.5rem; }
.btn.ghost.danger-text { color: var(--danger); }
.template-pick { width: auto; flex: 1 1 12rem; max-width: 22rem; min-width: 0; }

/* --- the invoice document ----------------------------------------------------------- */

.doc-frame { overflow-x: auto; container-type: inline-size; }
.invoice-doc {
  position: relative;
  background: #fff;
  color: #111827;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 2.25rem 2.25rem 2rem;
  font-size: 0.86rem;
  line-height: 1.45;
  /* Clipped, not scrollable: the rotated watermark is wider than the page,
     and a hidden overflow can still be scrolled sideways (a find in page,
     a scrollIntoView), sliding the document out of its box. */
  overflow: hidden;
  overflow: clip;
  min-width: 32rem;
}
.watermark {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 4.2rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: rgba(192, 57, 43, 0.12);
  transform: rotate(-24deg);
  pointer-events: none;
  white-space: nowrap;
  user-select: none;
}
/* A long watermark ("DRAF - BELUM DISETUJUI") is set smaller, as the PDF
   shrinks it, so it stays inside the page. */
.watermark.long { font-size: 2.5rem; }
.editor-preview .watermark.long { font-size: 1.8rem; }
.doc-head { display: flex; justify-content: space-between; gap: 2rem; align-items: flex-start; border-bottom: 2px solid #111827; padding-bottom: 1rem; }
.doc-company-name { font-size: 1.34em; font-weight: 750; letter-spacing: -0.01em; }
/* Same bounds as the PDF (60 x 18 mm): wide marks limited by width, tall ones
   by height, never stretched. */
.doc-logo { display: block; max-width: 60mm; max-height: 18mm; object-fit: contain; margin-bottom: 0.5rem; }
.doc-title { text-align: right; }
.doc-kind { font-size: 1.74em; font-weight: 800; letter-spacing: 0.08em; }
.doc-number { font-family: var(--mono); font-size: 1.16em; font-weight: 650; margin: 0.15rem 0 0.4rem; }
.doc-dates { display: grid; grid-template-columns: auto auto; gap: 0.1rem 0.75rem; margin: 0; justify-content: end; }
.doc-dates dt { color: #6b7280; }
.doc-dates dd { margin: 0; font-weight: 600; white-space: nowrap; }
.doc-replaces { margin: 0.75rem 0 0; font-weight: 650; }
.doc-billto { margin: 1rem 0; }
.doc-label { font-size: 0.84em; text-transform: uppercase; letter-spacing: 0.07em; color: #6b7280; font-weight: 700; }
.doc-strong { font-weight: 700; overflow-wrap: anywhere; }
.doc-small { font-size: 0.93em; color: #374151; }
.pre { white-space: pre-line; }
.doc-lines { width: 100%; border-collapse: collapse; margin: 0.5rem 0 1rem; }
.doc-lines th, .doc-lines td { padding: 0.45rem 0.5rem; border-bottom: 1px solid #e5e7eb; text-align: left; vertical-align: top; }
.doc-lines th { font-size: 0.84em; text-transform: uppercase; letter-spacing: 0.05em; color: #6b7280; border-bottom: 1.5px solid #111827; }
.doc-lines .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.doc-lines .no { width: 2rem; color: #6b7280; }
.doc-lines .disc-label { display: none; }
.doc-foot { display: grid; grid-template-columns: minmax(0, 1fr) minmax(16rem, 0.9fr); gap: 1.5rem; align-items: start; }
.doc-left { display: grid; gap: 0.8rem; }
.doc-terbilang { margin: 0; font-style: italic; }
.doc-banks { display: grid; gap: 0.6rem; }
/* "No Rekening : ..." and "Atas Nama : ..." with their colons lined up. */
.doc-bank-rows { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: 0.4em; margin: 0; }
.doc-bank-rows dt { margin: 0; }
.doc-bank-rows dd { margin: 0; overflow-wrap: anywhere; }
.doc-bank-rows dd::before { content: ": "; }
.doc-totals { width: 100%; border-collapse: collapse; }
.doc-totals th, .doc-totals td { padding: 0.25rem 0; text-align: left; font-weight: 500; }
.doc-totals td { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.doc-totals .muted-row th, .doc-totals .muted-row td { color: #6b7280; }
.doc-totals .strong-row th, .doc-totals .strong-row td { font-weight: 700; border-top: 1px solid #e5e7eb; padding-top: 0.4rem; }
.doc-totals .grand-row th, .doc-totals .grand-row td { font-weight: 800; font-size: 1.16em; border-top: 2px solid #111827; padding-top: 0.45rem; }
.doc-taxidr { margin-top: 0.75rem; padding: 0.6rem 0.75rem; background: #f9fafb; border-radius: 6px; }
.warn-text { color: var(--warn); }
.doc-sign { margin-top: 1.5rem; text-align: center; }
.doc-sign-space { height: 4.25rem; display: flex; align-items: flex-end; justify-content: center; }
/* Same bounds as the PDF (45 x 17 mm), never enlarged or stretched. */
.doc-signature { display: block; max-width: 45mm; max-height: 17mm; object-fit: contain; }
.doc-footer { margin-top: 1.5rem; padding-top: 0.75rem; border-top: 1px solid #e5e7eb; text-align: center; }

/* The forwarder layout (invoice-doc-forwarder.js; docs/spec.md §11): ruled
   boxes and a grid, as internal/pdf/forwarder.go prints it. Every size and
   gap is in em of the document's text (the PDF's millimetres, 1em ≈ 3.2mm),
   so the editor's smaller preview keeps the page's proportions. */
.fwd-doc { --fwd-rule: 1px solid #111827; line-height: 1.4; }
.fwd-head { display: flex; align-items: center; gap: 1.1em; }
.fwd-logo { display: block; width: auto; height: auto; max-width: 13em; max-height: 5.4em; object-fit: contain; flex: none; }
.fwd-company { font-size: 1.6em; font-weight: 800; line-height: 1.15; }
.fwd-tagline { font-size: 0.95em; font-weight: 600; font-style: italic; color: #1f2937; margin-top: 0.25em; }
.fwd-title { text-align: center; font-size: 1.75em; font-weight: 800; color: #1f3a68; letter-spacing: 0.06em; margin: 0.85em 0 0.55em; }
.fwd-replaces { text-align: center; font-weight: 700; margin: -0.2em 0 0.8em; }
.fwd-parties { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: 1.6em; align-items: start; }
.fwd-label { font-weight: 700; margin-bottom: 0.2em; }
.fwd-box { border: var(--fwd-rule); padding: 0.45em 0.65em; overflow-wrap: anywhere; }
.fwd-field { margin: 0.55em 0 0; }
.fwd-grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.fwd-grid th, .fwd-grid td { border: var(--fwd-rule); text-align: center; padding: 0.18em 0.35em; overflow-wrap: anywhere; }
.fwd-grid th { font-weight: 700; }
/* The 1px padding keeps the table's outer border inside the scroll box,
   which would otherwise clip its top. */
/* The 1px padding keeps the table's top border inside its scroll box; the
   margin puts its sides back in line with the boxes above and below. */
.fwd-lines-wrap { overflow-x: auto; margin: 1.4em -1px 0.9em; padding: 1px; }
.fwd-lines { width: 100%; border-collapse: collapse; border: var(--fwd-rule); }
.fwd-lines th { border: var(--fwd-rule); padding: 0.45em 0.4em; text-align: center; font-weight: 700; line-height: 1.25; }
.fwd-lines td { border-left: var(--fwd-rule); border-right: var(--fwd-rule); padding: 0.22em 0.5em; vertical-align: top; }
/* As the PDF's columns: 12, 79, 22, 34, 33 of 180mm. */
.fwd-lines th:nth-child(1) { width: 6.7%; }
.fwd-lines th:nth-child(3) { width: 12.2%; }
.fwd-lines th:nth-child(4) { width: 18.9%; }
.fwd-lines th:nth-child(5) { width: 18.3%; }
.fwd-lines .no { text-align: center; }
.fwd-lines td.qty { text-align: center; white-space: nowrap; }
.fwd-lines td.amount { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.fwd-price { display: flex; justify-content: space-between; gap: 0.5em; white-space: nowrap; font-variant-numeric: tabular-nums; }
.fwd-notes td { padding-top: 1em; }
.fwd-notes-head { font-weight: 700; text-decoration: underline; }
.fwd-notes .pre { font-style: italic; }
.fwd-fill td { height: 8em; }
.fwd-foot { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: 1.6em; align-items: start; }
.fwd-head-u { font-weight: 700; text-decoration: underline; margin: 0.5em 0 0.15em; }
.fwd-remarks > .fwd-head-u:first-child { margin-top: 0; }
.fwd-bank { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: 0.5em; margin: 0 0 0 1.2em; }
.fwd-bank dt, .fwd-bank dd { margin: 0; }
.fwd-bank dd { overflow-wrap: anywhere; }
.fwd-bank dd.strong { font-weight: 700; }
.fwd-bank .gap { grid-column: 1 / -1; height: 0.45em; }
.fwd-totals { width: 100%; border-collapse: collapse; }
.fwd-totals th, .fwd-totals td { border: var(--fwd-rule); padding: 0.22em 0.5em; text-align: left; font-weight: 500; }
.fwd-totals td { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.fwd-totals .strong-row th, .fwd-totals .strong-row td { font-weight: 700; }
.fwd-totals .muted-row th, .fwd-totals .muted-row td { color: #6b7280; }
.fwd-sign { margin-top: 1.4em; }
.fwd-sign .doc-sign-space { justify-content: flex-start; height: 5.6em; }
.fwd-sign-name { font-weight: 700; text-decoration: underline; }
.fwd-footer { margin-top: 2.2em; padding-top: 0.6em; border-top: 1px solid #4678be; text-align: center; font-size: 0.82em; line-height: 1.3; color: #374151; }
.fwd-footer strong, .doc-footer strong { font-weight: 700; }

/* The document on a phone: narrower than the page it prints on (32rem), it
   reflows rather than scrolling sideways. The header stacks; each line is a
   block, its description over "qty unit x price" and its amount; the totals
   come before the payment details, the signature last. */
@container (width < 32rem) {
  .invoice-doc, .editor-preview .invoice-doc { min-width: 0; padding: 1.1rem 1rem; font-size: 0.84rem; }
  /* Sized to the page, so the whole word stays on it. */
  .watermark, .editor-preview .watermark { font-size: 8cqi; }
  .watermark.long, .editor-preview .watermark.long { font-size: 5cqi; }
  .doc-head { flex-direction: column; gap: 0.75rem; }
  .doc-title { text-align: left; }
  .doc-dates { justify-content: start; }
  .doc-lines thead, .doc-lines td.no, .doc-lines td.none { display: none; }
  .doc-lines, .doc-lines tbody { display: block; }
  .doc-lines tr { display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto; column-gap: 0.35rem; padding: 0.55rem 0;
    border-bottom: 1px solid #e5e7eb; align-items: baseline; }
  .editor-preview .doc-lines td, .doc-lines td { padding: 0; border: 0; }
  .doc-lines td:nth-child(2) { grid-column: 1 / -1; margin-bottom: 0.2rem; }
  .doc-lines td.num { text-align: left; }
  .doc-lines td.price::before { content: "\00d7  "; color: #6b7280; }
  .doc-lines td.disc { grid-column: 1 / -1; grid-row: 3; color: #6b7280; }
  .doc-lines .disc-label { display: inline; }
  .doc-lines td.amount { text-align: right; font-weight: 650; }
  .doc-foot, .editor-preview .doc-foot { display: flex; flex-direction: column; gap: 0.8rem; }
  .doc-left, .doc-right { display: contents; }
  .doc-right > * { order: 1; }
  .doc-left > * { order: 2; }
  .doc-foot .doc-sign { order: 3; margin-top: 0.5rem; }
  /* The forwarder's boxes stack; its lines table keeps its columns, smaller. */
  .fwd-parties, .fwd-foot { grid-template-columns: minmax(0, 1fr); gap: 0.9rem; }
  .fwd-lines { font-size: 0.93em; }
  .fwd-lines .no { width: 2.2em; }
  /* Narrow columns wrap rather than overflow; a figure never splits. */
  .fwd-lines td:nth-child(2) { overflow-wrap: anywhere; }
  .fwd-lines td.qty { white-space: normal; }
  .fwd-price { flex-direction: column; align-items: flex-end; gap: 0; }
  .fwd-lines td, .fwd-lines th { padding: 0.18em 0.3em; }
  .fwd-fill td { height: 2.5em; }
  /* The logo gives the name and tagline room beside it. */
  .fwd-logo { max-width: 8em; max-height: 4em; }
}

/* --- printing: the document only --------------------------------------------------- */

@media print {
  @page { size: A4; margin: 14mm; }
  body { background: #fff; }
  .sidebar, .page-head, .notice, .payment-summary, .toasts, .preview-bar, .editor-form, .no-print { display: none !important; }
  .shell, .editor { display: block; }
  .content { padding: 0; }
  .invoice-doc { border: none; box-shadow: none; padding: 0; min-width: 0; }
  .doc-frame { overflow: visible; }
  /* An invoice not yet sent prints only its notice (docs/spec.md §10). */
  .unprintable > * { display: none !important; }
  .unprintable::before { content: attr(data-print-notice); display: block; font: 600 12pt/1.4 system-ui, sans-serif; }
  /* The forwarder's blocks stay whole across a page break, as its PDF keeps them. */
  .fwd-lines tr, .fwd-totals, .fwd-bank, .fwd-sign, .fwd-parties { break-inside: avoid; }
  .fwd-head-u { break-after: avoid; }
  .fwd-fill td { height: 2rem; }
  /* At the PDF's sizes (9pt body, 7pt footer), so lines break where it breaks them. */
  .fwd-doc { font-size: 9pt; }
  .fwd-doc .fwd-footer { font-size: 7pt; }
}

/* Dashboard */
/* Six figures: three by two, and two by three on a phone (below). */
.kpi-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem; }
.kpi-grid.four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) { .kpi-grid.four { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.page-head select.month-select { width: auto; min-width: 11rem; }
.kpi { display: grid; align-content: start; gap: 0.25rem; padding: 1rem 1.1rem; color: inherit; text-decoration: none; min-width: 0; }
a.kpi:hover { border-color: var(--line-strong); }
.kpi-label { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8rem; font-weight: 600; color: var(--muted); }
.kpi-value { font-size: 1.35rem; letter-spacing: -0.01em; overflow-wrap: anywhere; text-align: left; }
.kpi-note { font-size: 0.8rem; color: var(--muted); }
.dash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr)); gap: 0.75rem; align-items: start; }
.bars { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; align-content: start; }
.bar-row { display: grid; gap: 0.2rem; }
.bar-text { display: flex; justify-content: space-between; gap: 0.75rem; font-size: 0.9rem; }
.bar-label { min-width: 0; overflow-wrap: anywhere; }
.bar-value { white-space: nowrap; font-weight: 600; }
.bar-track { height: 0.5rem; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.bar-fill { height: 100%; border-radius: 999px; background: var(--accent); }
.bar-note { font-size: 0.78rem; color: var(--muted); }
.bars.aging .bar-1 .bar-fill { background: var(--warn); }
.bars.aging .bar-2 .bar-fill, .bars.aging .bar-3 .bar-fill, .bars.aging .bar-4 .bar-fill { background: var(--danger); }
.bars.aging .bar-0 .bar-fill { background: var(--ok); }
.trend-wrap { display: grid; gap: 0.5rem; }
svg.trend { width: 100%; height: auto; display: block; }
svg.trend .grid { stroke: var(--line); stroke-width: 1; }
svg.trend .axis { fill: var(--muted); font-size: 12px; }
/* The chart scales with its width, and its text with it: the text is set in
   chart units per width, so it reads at about 10-13 px from a phone to a wide
   screen. */
@media (max-width: 560px) { svg.trend .axis { font-size: 22px; } }
@media (min-width: 1100px) { svg.trend .axis { font-size: 9px; } }
@media (min-width: 1600px) { svg.trend .axis { font-size: 6.5px; } }
svg.trend .billed { fill: var(--accent); }
svg.trend .collected { fill: var(--ok); }
.legend { display: flex; gap: 1rem; flex-wrap: wrap; font-size: 0.82rem; color: var(--ink-2); }
.legend .key::before { content: ""; display: inline-block; width: 0.7rem; height: 0.7rem; border-radius: 2px; margin-right: 0.35rem; vertical-align: -0.05rem; }
.legend .key.billed::before { background: var(--accent); }
.legend .key.collected::before { background: var(--ok); }
.tax-list { display: grid; grid-template-columns: 1fr auto; gap: 0.4rem 1rem; margin: 0; }
.tax-list dt { color: var(--ink-2); }
.tax-list dd { margin: 0; font-weight: 600; text-align: right; }
@media (max-width: 560px) {
  .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem; }
  .kpi { padding: 0.75rem; }
  .kpi-value { font-size: 1.02rem; }
}

/* --- dashboard KPI icons ----------------------------------------------------------- */

.kpi-icon { display: inline-grid; place-items: center; width: 1.85rem; height: 1.85rem; border-radius: 0.55rem; flex: none; background: var(--accent-soft); color: var(--accent); }
.kpi-icon .icon { width: 1.05rem; height: 1.05rem; }
.kpi-icon.collected { background: var(--ok-soft); color: var(--ok); }
.kpi-icon.receivable { background: var(--warn-soft); color: var(--warn); }
.kpi-icon.overdue { background: var(--danger-soft); color: var(--danger); }
.kpi-icon.billed { background: var(--info-soft); color: var(--info); }
.kpi-icon.paid { background: rgba(123, 63, 242, 0.12); color: #6b37d8; }

/* An invoice's history: a collapsible list of who did what, and when. */
details.history > summary { cursor: pointer; list-style: revert; font-weight: 600; }
.history-list { list-style: none; margin: 0.75rem 0 0; padding: 0; }
.history-list li:last-child { border-bottom: 0; }
.history-list li { display: grid; grid-template-columns: 11rem 1fr auto; gap: 0.75rem; align-items: baseline; padding: 0.4rem 0; border-bottom: 1px solid var(--line); }
@media (max-width: 560px) { .history-list li { grid-template-columns: 1fr; gap: 0.15rem; } }

/* --- the customer portal (portal.html, docs/spec.md §12) ---------------------------- */
.portal-card { width: min(28rem, 100%); }
.portal-card h1 { overflow-wrap: anywhere; }
.portal-facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.35rem 1rem; margin: 0.25rem 0 0.5rem; }
.portal-facts dt { color: var(--muted); }
.portal-facts dd { margin: 0; overflow-wrap: anywhere; }
/* No staff hero beside it: the card alone, centred. */
.auth.portal-auth { grid-template-columns: minmax(0, 1fr); place-items: center; padding: 2rem 1rem; }
.portal-form { display: grid; gap: 0.75rem; }
.portal-offer, .portal-card .portal-offer + .portal-form, .portal-card > a + .portal-form { display: grid; gap: 0.75rem; border-top: 1px solid var(--line); padding-top: 1rem; }
.portal-offer > p, .portal-form > p { margin: 0; }
.portal { max-width: 64rem; margin: 0 auto; padding: 1.5rem 1rem 3rem; display: grid; gap: 1rem; }
.portal-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 0.75rem; }
.portal-head h1 { overflow-wrap: anywhere; }
.portal-body { display: grid; gap: 1rem; min-width: 0; }
.portal-card .strong, .portal .strong { font-weight: 700; }
.portal-facts dd.num { text-align: left; }
@media (min-width: 561px) {
  .portal table.data td, .portal table.data th { white-space: nowrap; }
}
/* Four tabs do not fit a phone's width: two rows of two. */
@media (max-width: 560px) {
  .portal-tabs { display: grid; grid-template-columns: 1fr 1fr; box-shadow: none; gap: 0.25rem; }
  .portal-tabs .tab { text-align: center; border: 1px solid var(--line); border-radius: 0.5rem; }
  .portal-tabs .tab.current { border-color: var(--accent); }
}
.field.portal-switch { border-top: 1px solid var(--line); padding-top: 0.9rem; }
