/* ==========================================================================
   ITS — Rangamati Hill District Council Financial Management System
   Self-hosted design system. No CDN, no build step.

   This file replaces the Tailwind Play CDN the app used to load from
   cdn.tailwindcss.com. That CDN compiled utility classes in the browser on
   every page load, which meant: no styling at all without internet, a visible
   flash of unstyled content, and a dependency the vendor explicitly documents
   as unsuitable for production. Everything the templates need is defined here.

   Type is Nikosh — the Bangladesh government standard Unicode Bangla face —
   bundled under static/fonts/ so rendering does not depend on what happens to
   be installed on the client. Nikosh carries the `beng` GSUB/GPOS tables, so
   conjuncts (ক্ষ, ন্ত্র, স্ট্র) shape correctly rather than decomposing.

   Layout order:
     1. Fonts            5. Layout / shell        9. Reports
     2. Design tokens    6. Sidebar navigation   10. Login
     3. Reset            7. Dashboard            11. Utilities
     4. Typography       8. CRUD screens         12. Print
   ========================================================================== */

/* 1. Fonts ---------------------------------------------------------------- */

@font-face {
  font-family: "Nikosh";
  src: url("../fonts/Nikosh.ttf") format("truetype");
  font-weight: 400 700;   /* one weight; the range stops synthetic-bold gaps */
  font-style: normal;
  font-display: swap;
  unicode-range: U+0980-09FF, U+0964-0965, U+200C-200D, U+25CC;
}

/* NikoshBAN is the same design with Latin/Bengali metrics tuned for mixed
   text, which is what almost every screen here is (Bangla labels, Latin
   codes and amounts). Used as the general face.

   It is confined to the Bengali range for the same reason Nikosh is, and the
   reason is not cosmetic: NikoshBAN is the legacy Bangla-ANSI face, so its glyph
   at ASCII 0x30-0x39 is drawn as a BENGALI numeral. Without this unicode-range
   it matched Latin text too and every amount, code and count on an English
   screen rendered as "১,২৩৪.৫৬" while the underlying text was correct ASCII.
   Bounded here, Latin falls through to the system faces listed after it, and
   Bangla digits still come from the font - the app converts digits in Python
   (core.utils_bn.localize_digits), so nothing depended on the font doing it. */
@font-face {
  font-family: "NikoshBAN";
  src: url("../fonts/NikoshBAN.ttf") format("truetype");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0980-09FF, U+0964-0965, U+200C-200D, U+25CC;
}

/* 2. Design tokens -------------------------------------------------------- */

:root {
  /* Pine green and gold: the council's colours, and Bangladesh's. */
  --pine-50:  #eef6f3;
  --pine-100: #d7ebe4;
  --pine-200: #b0d7ca;
  --pine-400: #2b8b78;
  --pine-600: #0b6b5b;
  --pine-700: #075244;
  --pine-800: #053b31;
  --pine-900: #032a23;

  --gold-50:  #fbf3e2;
  --gold-100: #f6e6c4;
  --gold-400: #d9a531;
  --gold-500: #c48a1a;
  --gold-600: #a5720f;
  --gold-800: #6b4a08;

  --paper:    #f5f3ed;
  --surface:  #ffffff;
  --ink:      #1f2a28;
  --ink-70:   #55605d;
  --ink-50:   #7c8683;
  --ink-35:   #a3aba8;
  --line:     rgba(5, 59, 49, .12);
  --line-soft:rgba(5, 59, 49, .07);

  --danger:   #c0392b;
  --danger-bg:#fdecea;
  --ok:       #1e7d52;
  --ok-bg:    #e8f6ee;
  --warn:     #9a6a00;
  --warn-bg:  #fdf3dd;

  --radius:    .55rem;
  --radius-lg: .85rem;
  --radius-xl: 1.1rem;

  --shadow-sm: 0 1px 2px rgba(5, 40, 33, .06);
  --shadow:    0 2px 8px rgba(5, 40, 33, .07), 0 1px 2px rgba(5, 40, 33, .05);
  --shadow-md: 0 6px 20px rgba(5, 40, 33, .10), 0 2px 6px rgba(5, 40, 33, .06);
  --shadow-lg: 0 18px 45px rgba(5, 40, 33, .16);

  --header-h:  3.75rem;
  --sidebar-w: 17.5rem;

  --font-bn: "NikoshBAN", "Nikosh", "Nirmala UI", "Noto Sans Bengali", system-ui, sans-serif;
  --font-ui: "NikoshBAN", "Nikosh", "Nirmala UI", system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Amounts, codes and cheque numbers: tabular so columns align down the page. */
  --font-num: "NikoshBAN", "Nikosh", ui-monospace, "Cascadia Mono", Consolas, monospace;
}

/* 3. Reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font-ui);
  font-size: .95rem;
  line-height: 1.55;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Bangla conjuncts need these on; some engines disable ligatures by
     default when font-variant is otherwise touched. */
  font-variant-ligatures: common-ligatures contextual;
}

img, svg { max-width: 100%; display: block; }

button, input, select, textarea { font: inherit; color: inherit; }

button { cursor: pointer; }

a { color: var(--pine-600); text-decoration: none; }
a:hover { text-decoration: underline; }

/* A single visible focus treatment everywhere, keyboard-only. */
:focus-visible {
  outline: 2px solid var(--pine-400);
  outline-offset: 2px;
  border-radius: 4px;
}

/* 4. Typography ----------------------------------------------------------- */

h1, h2, h3, h4 { margin: 0; font-weight: 700; line-height: 1.25; }

.t-display { font-family: var(--font-bn); font-weight: 700; letter-spacing: -.01em; }
.t-num     { font-family: var(--font-num); font-variant-numeric: tabular-nums; }

.page-title    { font-family: var(--font-bn); font-size: 1.4rem; color: var(--pine-800); }
.page-subtitle { font-size: .875rem; color: var(--ink-50); margin: .15rem 0 0; }

.section-label {
  font-size: .72rem; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-50); margin: 0 0 .6rem;
}

/* 5. Layout / shell ------------------------------------------------------- */

.app-shell {
  display: grid;
  /* minmax(0, 1fr), not 1fr: a bare `1fr` track is minmax(auto, 1fr), and that
     `auto` minimum is min-content - so a wide table or a fixed-width grid
     inside the content area widens the track and the whole page scrolls
     sideways on a phone. The 0 minimum lets the track be narrower than its
     contents, which is what makes the inner scroll containers do their job. */
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  grid-template-rows: var(--header-h) 1fr;
  grid-template-areas: "header header" "sidebar content";
  height: 100vh;
  height: 100dvh;            /* mobile browser chrome shouldn't clip the app */
  max-width: 100%;
  overflow-x: hidden;
}

.app-header  { grid-area: header;  position: sticky; top: 0; z-index: 40; }
.app-sidebar { grid-area: sidebar; overflow-y: auto; overscroll-behavior: contain; }
.app-content {
  grid-area: content; overflow-y: auto; padding: 1.5rem;
  /* min-width:0 is what actually lets this grid cell be narrower than its
     content. Without it the content column takes its max-content width and the
     whole page scrolls sideways on a phone; wide tables are supposed to scroll
     inside their own .rpt-table-wrap, not drag the page with them. */
  min-width: 0;
}

@media (max-width: 640px) { .app-content { padding: 1rem; } }

/* --- header --- */
.app-header {
  display: flex; align-items: center; gap: .75rem;
  padding: 0 1rem;
  background: linear-gradient(100deg, var(--pine-800), var(--pine-600));
  color: #fff;
  box-shadow: var(--shadow);
}

.brand { display: flex; align-items: center; gap: .7rem; min-width: 0; color: #fff; }
.brand:hover { text-decoration: none; }
.brand-logo {
  height: 2.25rem; width: 2.25rem; flex: none;
  border-radius: 50%; background: #fff; object-fit: contain;
  box-shadow: 0 0 0 2px rgba(255,255,255,.25);
}
.brand-text { min-width: 0; }
.brand-name {
  display: block; font-family: var(--font-bn); font-weight: 700;
  font-size: 1rem; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.brand-sub {
  display: block; font-size: .7rem; color: rgba(255,255,255,.72);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 480px) { .brand-sub { display: none; } }

.header-right { margin-left: auto; display: flex; align-items: center; gap: .5rem; }

.icon-btn {
  display: grid; place-items: center;
  height: 2.25rem; width: 2.25rem; flex: none;
  background: transparent; border: 0; border-radius: var(--radius);
  color: inherit;
}
.icon-btn:hover { background: rgba(255,255,255,.14); }
.icon-btn span { display: block; width: 1.1rem; height: 2px; background: currentColor; border-radius: 2px; }
.icon-btn span + span { margin-top: .22rem; }
.menu-toggle { display: none; }
@media (max-width: 1023px) { .menu-toggle { display: grid; } }

/* --- account menu --- */
.acct { position: relative; }
.acct-btn {
  display: flex; align-items: center; gap: .5rem;
  padding: .35rem .6rem; border: 0; border-radius: var(--radius);
  background: transparent; color: inherit;
}
.acct-btn:hover { background: rgba(255,255,255,.14); }
.acct-avatar {
  display: grid; place-items: center;
  height: 1.85rem; width: 1.85rem; flex: none;
  border-radius: 50%; background: rgba(255,255,255,.22);
  font-size: .85rem; font-weight: 700;
}
.acct-name { font-size: .85rem; max-width: 10rem; overflow: hidden;
             text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 640px) { .acct-name { display: none; } }

.acct-menu {
  position: absolute; right: 0; top: calc(100% + .35rem);
  min-width: 13rem; padding: .3rem;
  background: var(--surface); color: var(--ink);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); border: 1px solid var(--line);
  z-index: 50;
}
.acct-menu[hidden] { display: none; }
.acct-menu a {
  display: block; padding: .5rem .7rem; border-radius: var(--radius);
  font-size: .875rem; color: var(--ink);
}
.acct-menu a:hover { background: var(--pine-50); color: var(--pine-800); text-decoration: none; }
.acct-menu a.danger { color: var(--danger); }
.acct-menu a.danger:hover { background: var(--danger-bg); color: var(--danger); }
.acct-menu hr { border: 0; border-top: 1px solid var(--line-soft); margin: .3rem .2rem; }

/* Report Header Setup: preview of the uploaded letterhead logo. */
.hdr-logo-preview {
  margin-top: .5rem; height: 4rem; width: 4rem; object-fit: contain;
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius);
  padding: .2rem;
}

/* --- language switch ---
   One <form> per language (see partials/lang_switch.html), so the wrapper is
   the pill and each form is just a button holder. */
.lang-switch { display: inline-flex; align-items: center; gap: .15rem;
               background: rgba(255,255,255,.14); border-radius: 999px; padding: .15rem; }
.lang-switch form { display: contents; }
.lang-switch button {
  border: 0; background: transparent; color: rgba(255,255,255,.85);
  padding: .2rem .65rem; border-radius: 999px; font-size: .8rem; font-weight: 600;
}
.lang-switch button:hover { color: #fff; background: rgba(255,255,255,.15); }
.lang-switch button[aria-pressed="true"] { background: #fff; color: var(--pine-700); }
@media (max-width: 640px) { .lang-switch { display: none; } }

/* Login page renders the same partial on a light background. */
.lang-switch--light { background: rgba(5,59,49,.07); }
.lang-switch--light button { color: var(--pine-700); }
.lang-switch--light button:hover { background: rgba(5,59,49,.08); }
.lang-switch--light button[aria-pressed="true"] { background: var(--pine-700); color: #fff; }

/* --- sidebar as off-canvas drawer on small screens --- */
.app-sidebar {
  background: var(--surface);
  border-right: 1px solid var(--line);
}

@media (max-width: 1023px) {
  .app-shell {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "header" "content";
  }
  .app-sidebar {
    position: fixed; top: var(--header-h); bottom: 0; left: 0;
    width: min(var(--sidebar-w), 85vw); z-index: 35;
    transform: translateX(-100%);
    transition: transform .22s ease;
    box-shadow: var(--shadow-lg);
  }
  .app-sidebar.open { transform: translateX(0); }
}

.app-scrim {
  position: fixed; inset: 0; z-index: 30;
  background: rgba(3, 25, 20, .45);
  backdrop-filter: blur(1px);
}
.app-scrim[hidden] { display: none; }
@media (min-width: 1024px) { .app-scrim { display: none; } }

/* 6. Sidebar navigation --------------------------------------------------- */

.app-nav { padding: .75rem .6rem 3rem; font-size: .875rem; }

.menu-item {
  display: block; padding: .45rem .7rem; margin: .05rem 0;
  border-radius: var(--radius);
  color: var(--ink-70); line-height: 1.35;
  border-left: 3px solid transparent;
}
.menu-item:hover { background: var(--pine-50); color: var(--pine-800); text-decoration: none; }
.menu-item[aria-current="page"] {
  background: var(--pine-50); color: var(--pine-800);
  font-weight: 700; border-left-color: var(--pine-600);
}

.menu-item--home {
  font-weight: 700; color: var(--pine-800); background: var(--pine-50);
  margin-bottom: .6rem;
}

/* Not ported: visibly inert, so the menu never implies a working screen. */
.menu-item--todo { color: var(--ink-35); cursor: not-allowed; }
.menu-item--todo:hover { background: transparent; color: var(--ink-35); }
.menu-item--todo::after {
  content: "•"; margin-inline-start: .4rem; color: var(--gold-400); font-size: .9em;
}

.menu-group { border-radius: var(--radius); }
.menu-summary {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .5rem .7rem; border-radius: var(--radius);
  cursor: pointer; user-select: none; list-style: none;
  font-weight: 700; color: var(--pine-800);
}
.menu-summary::-webkit-details-marker { display: none; }
.menu-summary:hover { background: var(--pine-50); }
.menu-summary--sub { font-weight: 600; font-size: .84rem; color: var(--pine-600); }

.menu-caret { width: 1rem; height: 1rem; flex: none; transition: transform .18s ease; }
.menu-group[open] > .menu-summary .menu-caret { transform: rotate(180deg); }

.menu-children {
  padding-inline-start: .5rem; margin-inline-start: .45rem;
  border-inline-start: 1px solid var(--line-soft);
}
.menu-group--sub { margin: .1rem 0; }
.menu-empty { padding: .8rem; color: var(--ink-50); font-size: .82rem; }

/* 7. Dashboard ------------------------------------------------------------ */

.dash { max-width: 78rem; min-width: 0; }
.dash-head { margin-bottom: 1.5rem; flex-wrap: wrap; gap: .75rem; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.stat-grid {
  display: grid; gap: 1rem; margin-bottom: 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
}

.stat {
  padding: 1.1rem 1.2rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  border-top: 3px solid var(--pine-600);
}
.stat--gold { border-top-color: var(--gold-500); }
.stat-value {
  font-family: var(--font-bn); font-size: 1.9rem; font-weight: 700;
  line-height: 1.1; color: var(--pine-700);
  font-variant-numeric: tabular-nums;
}
.stat--gold .stat-value { color: var(--gold-600); }
.stat-label { font-size: .78rem; color: var(--ink-50); margin-top: .3rem; }

.money-grid {
  display: grid; gap: 1rem; margin-bottom: 1.75rem;
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}
.money-card {
  padding: 1.1rem 1.2rem; border-radius: var(--radius-lg);
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
}
.money-card--primary {
  background: linear-gradient(135deg, var(--pine-700), var(--pine-600));
  color: #fff; border-color: transparent;
}
.money-label { font-size: .76rem; color: var(--ink-50); }
.money-card--primary .money-label { color: rgba(255,255,255,.75); }
.money-value {
  font-family: var(--font-bn); font-size: 1.55rem; font-weight: 700;
  margin-top: .25rem; color: var(--pine-800);
  font-variant-numeric: tabular-nums; word-break: break-word;
}
.money-card--primary .money-value { color: #fff; }

.tile-grid {
  display: grid; gap: .85rem; margin-bottom: 1.75rem;
  grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
}
.tile {
  display: block; padding: .95rem 1.1rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  transition: box-shadow .16s ease, border-color .16s ease, transform .16s ease;
}
.tile:hover {
  text-decoration: none; box-shadow: var(--shadow-md);
  border-color: var(--pine-200); transform: translateY(-1px);
}
.tile-title { font-weight: 700; color: var(--pine-700); }
.tile:hover .tile-title { color: var(--pine-800); }
.tile-sub { font-size: .76rem; color: var(--ink-50); margin-top: .2rem; }

.tile--report:hover { border-color: var(--gold-400); }
.tile--report .tile-title {
  color: var(--gold-800); display: flex; align-items: center; gap: .5rem;
}
.tile--report .tile-title::before {
  content: ""; height: .4rem; width: .4rem; flex: none;
  border-radius: 50%; background: var(--gold-500);
}

/* --- charts ---
   Server-rendered SVG/CSS; no charting library, because the app loads no
   third-party JavaScript. Colour follows the data's job:

     * Magnitude and trend (fiscal years, months, suppliers, bill types) are
       ONE hue. Bar length already encodes the value, so colouring those bars
       by their value would spend the identity channel re-encoding what the
       geometry shows.
     * Only the deduction split is genuinely categorical - four things that
       differ in kind, not degree - so only it draws categorical hues.

   The four categorical slots come from a validated palette (worst adjacent
   CVD ΔE 16.3, normal-vision ΔE 19.6 against a white surface). Magenta and
   yellow fall below 3:1 there, which is legal only with a relief channel, so
   every segment ships a direct value label in .split-legend. Do not remove
   those labels without re-checking the palette. */

.chart-grid {
  display: grid; gap: 1rem; margin-bottom: 1.75rem;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
}
.chart-card {
  margin: 0; padding: 1.15rem 1.25rem 1.3rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  min-width: 0;                      /* let grid children actually shrink */
}
.chart-card--wide { grid-column: 1 / -1; }

.chart-head { margin-bottom: .9rem; }
.chart-title { font-family: var(--font-bn); font-size: 1rem; color: var(--pine-800); }
.chart-sub { margin: .15rem 0 0; font-size: .78rem; color: var(--ink-50); }
.chart-empty { padding: 2rem 0; text-align: center; color: var(--ink-50); font-size: .85rem; }

/* series colours */
.swatch {
  display: inline-block; height: .7rem; width: .7rem; flex: none;
  border-radius: 3px; vertical-align: -1px;
}
.swatch--gross    { background: var(--pine-200); }
.swatch--net      { background: var(--pine-600); }
.swatch--vat      { background: #2a78d6; }   /* categorical slot 1 */
.swatch--tax      { background: #008300; }   /* slot 2 */
.swatch--security { background: #e87ba4; }   /* slot 3 */
.swatch--others   { background: #eda100; }   /* slot 4 */

.legend { display: flex; flex-wrap: wrap; gap: .25rem 1rem; margin-bottom: .8rem; }
.legend-item {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .78rem; color: var(--ink-70);    /* text ink, never the series hue */
}

/* --- column chart (fiscal years) --- */
.col-chart {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: .5rem; align-items: end;
  min-width: 0;
}
/* Six fiscal years cannot be read at 55px each. Below the breakpoint the plot
   scrolls inside the card - the card, never the page. */
@media (max-width: 640px) {
  .col-chart {
    grid-auto-columns: minmax(3.6rem, 1fr);
    overflow-x: auto; padding-bottom: .35rem;
    -webkit-overflow-scrolling: touch;
  }
  .col-stack { height: 7.5rem; }
  .col-label { font-size: .68rem; }
}
.col-group { min-width: 0; text-align: center; }
.col-stack {
  display: flex; align-items: flex-end; justify-content: center; gap: 2px;
  height: 9.5rem;                    /* the plot area */
}
.col {
  position: relative; flex: 1 1 0; max-width: 1.6rem; min-height: 2px;
  border-radius: 4px 4px 0 0;        /* rounded data-end, square on the baseline */
  transition: filter .15s ease;
}
.col--gross { background: var(--pine-200); }
.col--net   { background: var(--pine-600); }
.col-group.is-current .col--net { background: var(--gold-500); }
.col-group.is-current .col--gross { background: var(--gold-100); }

.col-tip {
  position: absolute; bottom: 100%; left: 50%; transform: translate(-50%, -.35rem);
  padding: .2rem .45rem; border-radius: var(--radius);
  background: var(--pine-800); color: #fff;
  font-size: .7rem; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity .12s ease; z-index: 5;
}
.col:hover { filter: brightness(1.08); }
.col:hover .col-tip { opacity: 1; }

/* On a touch device there is no hover, and below 640px .col-chart becomes a
   scroll container - which clips an absolutely-positioned tooltip anyway. The
   figures stay reachable: each column carries an aria-label with both amounts
   and prints its bill count underneath. */
@media (hover: none) {
  .col-tip { display: none; }
}

.col-label {
  margin-top: .45rem; font-size: .74rem; color: var(--ink-70); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.col-meta { font-size: .68rem; color: var(--ink-50); }
.col-group.is-current .col-label { color: var(--gold-800); }

/* --- categorical slots (validated set; assigned in fixed order) --- */
.swatch--s1 { background: #2a78d6; }
.swatch--s2 { background: #008300; }
.swatch--s3 { background: #e87ba4; }
.swatch--s4 { background: #eda100; }
.swatch--s5 { background: #1baf7a; }
.swatch--s6 { background: #eb6834; }
.swatch--s7 { background: #4a3aa7; }
.swatch--s8 { background: #e34948; }

/* Doubled class for specificity: the base `.hbar-fill` rule sets the default
   pine fill and is declared later in this file, so a single-class slot rule
   loses the cascade and every bar came out green. */
.hbar-fill.hbar-fill--s1 { background: #2a78d6; }
.hbar-fill.hbar-fill--s2 { background: #008300; }
.hbar-fill.hbar-fill--s3 { background: #e87ba4; }
.hbar-fill.hbar-fill--s4 { background: #eda100; }
.hbar-fill.hbar-fill--s5 { background: #1baf7a; }
.hbar-fill.hbar-fill--s6 { background: #eb6834; }
.hbar-fill.hbar-fill--s7 { background: #4a3aa7; }
.hbar-fill.hbar-fill--s8 { background: #e34948; }

/* --- donut (deduction composition) --- */
.donut-wrap {
  position: relative; display: grid; place-items: center;
  padding: .35rem 0 .2rem;
}
.donut {
  width: 100%; max-width: 12.5rem; height: auto;
  transform: rotate(-90deg);          /* first arc starts at 12 o'clock */
}
.donut circle {
  fill: none; stroke-width: 5.2;
}
.donut-track { stroke: var(--line-soft); }
/* 2px surface ring between adjacent arcs, per the mark spec. */
.donut-seg { stroke-linecap: butt; paint-order: stroke; transition: stroke-width .15s ease; }
.donut-seg:hover { stroke-width: 6.4; }
.donut-seg--vat      { stroke: #2a78d6; }
.donut-seg--tax      { stroke: #008300; }
.donut-seg--security { stroke: #e87ba4; }
.donut-seg--others   { stroke: #eda100; }

.donut-center {
  position: absolute; inset: 0; display: grid; place-content: center;
  text-align: center; pointer-events: none; padding: 0 2.6rem;
}
.donut-center-val {
  font-family: var(--font-bn); font-weight: 700; font-size: .95rem;
  color: var(--pine-800); line-height: 1.15; word-break: break-word;
}
.donut-center-lbl { font-size: .68rem; color: var(--ink-50); margin-top: .1rem; }

.split-legend { list-style: none; margin: .9rem 0 0; padding: 0; }
.split-legend li {
  display: grid; align-items: baseline; gap: .5rem;
  grid-template-columns: auto 1fr auto auto;
  padding: .3rem 0; font-size: .8rem;
  border-top: 1px solid var(--line-soft);
}
.split-legend li:first-child { border-top: 0; }
.split-name { color: var(--ink-70); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.split-pct  { color: var(--ink-50); font-variant-numeric: tabular-nums; }
.split-amt  { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }

/* --- line/area chart (monthly) --- */
.line-chart {
  width: 100%; height: 9.5rem; display: block; overflow: visible;
}
/* preserveAspectRatio="none" scales stroke width with the box, so the stroke
   is declared in user units and un-scaled via vector-effect instead. */
.line-chart .line {
  fill: none; stroke: var(--pine-600); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.line-chart .area { fill: var(--pine-600); opacity: .12; stroke: none; }
.line-chart .grid { stroke: var(--line-soft); stroke-width: 1; vector-effect: non-scaling-stroke; }
.line-chart .axis { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }

.line-labels {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  margin-top: .4rem; font-size: .66rem; color: var(--ink-50); text-align: center;
}
.line-labels span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- horizontal bars (suppliers, bill types) --- */
.hbar-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.hbar-list li {
  display: grid; align-items: center; gap: .6rem;
  grid-template-columns: minmax(6rem, 9rem) 1fr auto;
  font-size: .8rem;
}
.hbar-name { color: var(--ink-70); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbar-track { height: .6rem; border-radius: 999px; background: var(--line-soft); overflow: hidden; }
.hbar-fill { display: block; height: 100%; border-radius: 999px; background: var(--pine-600); min-width: 2px; }
.hbar-val { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }

@media (max-width: 560px) {
  .hbar-list li { grid-template-columns: 1fr auto; }
  .hbar-track { grid-column: 1 / -1; }
}

/* --- fiscal-year picker in the dashboard header --- */
.fy-picker { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.fy-picker .input { width: auto; min-width: 9rem; }

/* Stat tiles are links on the dashboard; keep them looking like cards. */
a.stat { display: block; color: inherit; }
a.stat:hover {
  text-decoration: none; box-shadow: var(--shadow-md);
  border-color: var(--pine-200);
}

/* 8. Forms, buttons, CRUD screens ----------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  border: 1px solid transparent; border-radius: var(--radius);
  padding: .5rem 1.05rem;
  font-size: .875rem; font-weight: 600; line-height: 1.3;
  transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
  white-space: nowrap;
}
.btn:hover { text-decoration: none; }
.btn:disabled { opacity: .55; cursor: not-allowed; }

.btn-primary { background: var(--pine-700); color: #fff; }
.btn-primary:hover:not(:disabled) { background: var(--pine-800); }

.btn-gold { background: var(--gold-500); color: #fff; }
.btn-gold:hover:not(:disabled) { background: var(--gold-600); }

.btn-ghost { background: var(--pine-50); color: var(--pine-700); }
.btn-ghost:hover:not(:disabled) { background: var(--pine-100); }

.btn-subtle { background: rgba(5,59,49,.06); color: var(--ink-70); }
.btn-subtle:hover:not(:disabled) { background: rgba(5,59,49,.11); }

.btn-sm { padding: .35rem .75rem; font-size: .82rem; }

.btn-link { background: none; border: 0; padding: 0; font-weight: 600;
            font-size: .85rem; color: var(--pine-700); }
.btn-link:hover { text-decoration: underline; }
.btn-link.danger { color: var(--danger); }

/* --- inputs --- */
.input, .its-input, .rpt-filters select, .rpt-filters input {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .5rem .7rem;
  outline: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input:hover, .its-input:hover { border-color: rgba(5,59,49,.22); }
.input:focus, .its-input:focus,
.rpt-filters select:focus, .rpt-filters input:focus {
  border-color: var(--pine-600);
  box-shadow: 0 0 0 3px rgba(11,107,91,.16);
}
.input:disabled, .its-input:disabled { background: rgba(5,59,49,.04); color: var(--ink-50); }

select.input, select.its-input, .rpt-filters select {
  /* Native arrow is inconsistent across platforms; draw our own. */
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M6 8l4 4 4-4' fill='none' stroke='%2355605d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .55rem center;
  background-size: 1.05rem;
  padding-inline-end: 2rem;
}
html[dir="rtl"] select.input { background-position: left .55rem center; }

.field-label { font-size: .84rem; font-weight: 600; color: var(--ink-70); }
.req { color: var(--danger); }

/* --- CRUD screen --- */
.crud { max-width: 100%; }

.screen-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.1rem;
}

.entry-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  padding: 1.25rem; max-width: 52rem;
}

/* Label/field pairs. Collapses to stacked on narrow screens instead of
   squeezing a two-column grid into 360px, which is what made the entry form
   unusable on a phone. */
.entry-grid {
  display: grid; align-items: center;
  grid-template-columns: minmax(9rem, 14rem) minmax(0, 1fr);
  gap: .7rem 1.5rem;
}
/* `.row-pair` is display:contents, so its label is laid out in the grid but is
   not a DOM child of .entry-grid — it has to be named here too, or those rows
   lose their right-aligned label and break the column. */
.entry-grid > label,
.entry-grid > .row-pair > label { text-align: end; font-size: .875rem;
                                  font-weight: 600; color: var(--ink-70); }
.entry-grid .field { min-width: 0; max-width: 28rem; }

@media (max-width: 720px) {
  .entry-grid { grid-template-columns: minmax(0, 1fr); gap: .3rem; }
  .entry-grid > label,
  .entry-grid > .row-pair > label { text-align: start; margin-top: .5rem; }
  .entry-grid .field { max-width: none; }
}

.entry-actions {
  display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1.3rem;
  padding-inline-start: calc(min(14rem, 9rem) + 1.5rem);
}
@media (max-width: 720px) { .entry-actions { padding-inline-start: 0; } }

/* --- search results --- */
.search-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  margin-bottom: .85rem;
}
.its-term { max-width: 18rem; }
.result-count { font-size: .85rem; color: var(--ink-50); }
.its-count { font-weight: 700; color: var(--pine-800); }

.table-wrap {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  overflow: auto;                 /* wide grids scroll here, not the page */
  max-height: 68vh;
  -webkit-overflow-scrolling: touch;
}

.its-grid { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .875rem; }
.its-grid th, .its-grid td { padding: .55rem .8rem; white-space: nowrap; text-align: start; }
.its-grid thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--pine-50); color: var(--pine-800);
  font-weight: 700; border-bottom: 1px solid var(--line);
}
.its-grid tbody tr { border-top: 1px solid var(--line-soft); }
.its-grid tbody tr:nth-child(even of :not([hidden])) { background: rgba(5,59,49,.018); }
.its-grid tbody tr:hover { background: var(--pine-50); }
.its-grid td.row-actions { text-align: end; }
.row-actions-inner { display: inline-flex; gap: .75rem; justify-content: flex-end; }

.its-pager { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem;
             margin-top: .85rem; font-size: .85rem; }
.its-pager button {
  min-width: 2rem; padding: .3rem .55rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); font-size: .82rem; color: var(--ink-70);
}
.its-pager button:hover:not(:disabled) { background: var(--pine-50); color: var(--pine-800); }
.its-pager button[aria-current="true"] { background: var(--pine-700); border-color: var(--pine-700); color: #fff; font-weight: 700; }
.its-pager button:disabled { opacity: .45; cursor: not-allowed; }

/* 9. Reports --------------------------------------------------------------

   Screen and paper are two different documents, and they are styled as two.

   The screen used to render the report black-on-white with a hard black grid,
   on the theory that what you see should be what prints. In practice that gave
   up every affordance a screen has - no fills, no striping, no hierarchy - to
   imitate a photocopier, and the PDF is a separate, authoritative document
   anyway (reports/report_pdf.html + static/css/report_print.css), so nobody
   was ever reading the screen as a proof of the print.

   So the screen now uses the app's own pine-and-gold palette: a tinted, sticky
   column header, quiet zebra striping, a gold-footed totals row and rounded
   card surfaces. The printed form is untouched, and the @media print block in
   section 12 still flattens this screen back to black-on-white for anyone who
   hits Ctrl+P instead of the PDF button.

   All the report colours are declared once, on .report-screen, so the whole
   area can be re-tinted from five lines. */

.report-screen {
  max-width: 100%;
  --rpt-line:    var(--line);            /* cell divider */
  --rpt-line-in: var(--line-soft);       /* inner divider, quieter still */
  --rpt-head:    linear-gradient(180deg, #f3f9f7 0%, #e9f3f0 100%);
  --rpt-zebra:   #fafcfb;                /* must be a solid colour: the pinned
                                            first column matches it exactly */
  --rpt-hover:   var(--pine-50);
  --rpt-foot:    linear-gradient(180deg, #fdf8ec 0%, #fbf2df 100%);
  --rpt-foot-solid: #fcf5e6;
}

/* --- head ---------------------------------------------------------------- */

/* The report title sits on a card with a gold rule down its leading edge, so a
   report screen is recognisable at a glance from a CRUD screen using the same
   .screen-head markup. */
.report-screen > .screen-head {
  background: var(--surface);
  border: 1px solid var(--line);
  border-inline-start: 3px solid var(--gold-400);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: .9rem 1.1rem;
  align-items: center;
  margin-bottom: 1rem;
}

.rpt-actions { display: flex; gap: .5rem; flex-wrap: wrap; }
/* Icons are inline SVG in the template, sized here so they track the label. */
.rpt-actions .btn svg { width: 1rem; height: 1rem; flex: none; }
.rpt-actions .btn.is-busy { opacity: .7; pointer-events: none; }

/* --- filters ------------------------------------------------------------- */

.rpt-filters {
  display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-end;
  background: linear-gradient(180deg, var(--surface) 0%, #fbfdfc 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  padding: 1rem; margin-bottom: 1rem;
}
.fld { display: flex; flex-direction: column; gap: .3rem; min-width: 0; flex: 1 1 14rem; max-width: 22rem; }
.fld-label { font-size: .8rem; font-weight: 600; color: var(--ink-70); }
/* The filter row's action button should not stretch like the fields do. */
.rpt-filters .btn { flex: 0 0 auto; }
.rpt-filters .btn svg { width: 1rem; height: 1rem; flex: none; }

/* flex-wrap: nowrap matters here and is not tidying.
   .rpt-filters above is `display: flex; flex-wrap: wrap`, laid out as a single
   row of fields. Turning it into a column (to stack the filter row above the
   "more filters" panel) while leaving wrap on makes it a COLUMN-wrapping
   container, and a column-wrap container resolves its line height from the
   container rather than from the content - which left a 52px band of empty
   space across the top of the filter card, above the labels. */
.rpt-filters { flex-direction: column; flex-wrap: nowrap; align-items: stretch; }
.rpt-filter-row {
  display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-end;
}
.rpt-filter-actions { display: flex; gap: .5rem; flex: 0 0 auto; }

@media (max-width: 640px) {
  .fld { flex-basis: 100%; max-width: none; }
  .rpt-filters .btn { width: 100%; }
  .rpt-filter-actions { width: 100%; }
}

/* --- "more filters" ---
   Reports carry up to four filters. Showing all four in one row pushes the
   table below the fold on a laptop, and three of them are set once and left
   alone; folding everything past the second keeps the common case compact. The
   panel is forced open server-side whenever one of the hidden filters is
   actually set, so a bookmarked URL never hides the filter shaping its own
   figures. */
.rpt-more { margin-top: .85rem; border-top: 1px dashed var(--line); padding-top: .75rem; }
.rpt-more > summary {
  display: inline-flex; align-items: center; gap: .4rem;
  cursor: pointer; list-style: none;
  font-size: .82rem; font-weight: 700; color: var(--pine-700);
  padding: .25rem .55rem; border-radius: var(--radius);
  user-select: none;
}
.rpt-more > summary::-webkit-details-marker { display: none; }
.rpt-more > summary:hover { background: var(--pine-50); }
.rpt-more > summary svg { transition: transform .18s ease; }
.rpt-more[open] > summary svg { transform: rotate(180deg); }
.rpt-filter-row--more { margin-top: .75rem; }

/* --- type-to-search combobox ---
   Used for any filter with a long option list — 1,852 projects, 270 suppliers,
   71 employees. A native <select> there offers first-letter jumping and nothing
   else, and on Bangla labels not even that, so choosing a project meant
   scrolling a list a thousand rows deep. */
.combo { position: relative; }
.combo-input { width: 100%; padding-inline-end: 2rem; }
.combo-clear {
  position: absolute; inset-inline-end: .45rem; top: 50%;
  transform: translateY(-50%);
  border: 0; background: none; cursor: pointer;
  font-size: 1.15rem; line-height: 1; color: var(--ink-50);
  padding: 0 .2rem; border-radius: 999px;
}
.combo-clear:hover { color: var(--danger); background: var(--danger-bg); }

.combo-list {
  position: absolute; z-index: 30; inset-inline: 0; top: calc(100% + .25rem);
  margin: 0; padding: .25rem; list-style: none;
  background: var(--surface);
  border: 1px solid var(--pine-200);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  max-height: 17rem; overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--pine-200) transparent;
}
.combo-opt {
  padding: .4rem .6rem; border-radius: calc(var(--radius) - .15rem);
  font-size: .86rem; cursor: pointer; line-height: 1.35;
}
.combo-opt:hover, .combo-opt.is-active { background: var(--pine-50); color: var(--pine-800); }
.combo-opt[aria-selected="true"] {
  background: var(--pine-100); color: var(--pine-800); font-weight: 700;
}
.combo-empty, .combo-more {
  padding: .45rem .6rem; font-size: .8rem; color: var(--ink-50);
}
/* The "N more — keep typing" line is a promise that the list is incomplete, so
   it must not look like one of the options. */
.combo-more { border-top: 1px solid var(--line-soft); margin-top: .2rem; font-style: italic; }

/* --- quick find ---
   Filters the rows already on screen, in the browser. It searches every column
   at once, including ones the server offers no filter for, and it costs no
   request — which is what makes narrowing a 500-row register feel instant. */
.rpt-find {
  display: flex; align-items: center; gap: .5rem;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  padding: .45rem .75rem; margin-bottom: .85rem;
}
.rpt-find > svg { color: var(--ink-35); flex: none; }
.rpt-find input {
  flex: 1 1 auto; min-width: 0;
  border: 0; outline: none; background: none;
  font: inherit; font-size: .875rem; color: var(--ink);
  padding: .2rem 0;
}
.rpt-find input::placeholder { color: var(--ink-35); }
.rpt-find input::-webkit-search-cancel-button { display: none; }
.rpt-find:focus-within {
  border-color: var(--pine-600);
  box-shadow: 0 0 0 3px rgba(11, 107, 91, .16);
}
.rpt-find-count {
  flex: none; font-size: .76rem; font-weight: 700;
  color: var(--pine-800); background: var(--pine-50);
  border: 1px solid var(--pine-100);
  padding: .15rem .55rem; border-radius: 999px; white-space: nowrap;
}
.rpt-find-clear {
  flex: none; border: 0; background: none; cursor: pointer;
  font-size: 1.2rem; line-height: 1; color: var(--ink-50);
  padding: 0 .25rem; border-radius: 999px;
}
.rpt-find-clear:hover { color: var(--danger); background: var(--danger-bg); }

/* While a quick find is active the zebra stripe would be computed over rows
   that are hidden, giving two shaded rows in a row. Striping is dropped for the
   duration rather than left looking broken. */
.report-screen.is-finding .rpt-table tbody tr:nth-child(even) > td {
  background: var(--surface);
}
.report-screen.is-finding .rpt-table tbody tr:hover > td { background: var(--rpt-hover); }

/* --- meta strip ----------------------------------------------------------
   Row count, the truncation notice and the run time, as pills rather than a
   run-on line of grey text - the truncation notice in particular is the one
   thing on the screen that must not be skimmed past, because it is what says
   the figures above are a partial list. */

.rpt-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: .4rem .6rem;
  margin: 0 0 .7rem;
}
.rpt-subtitle {
  font-size: .95rem; font-weight: 700; color: var(--pine-800);
  margin: 0 .3rem 0 0;
}

.rpt-chip {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .2rem .6rem; border-radius: 999px;
  font-size: .76rem; font-weight: 600; line-height: 1.5;
  white-space: nowrap;
}
.rpt-chip svg { width: .85rem; height: .85rem; flex: none; }

.rpt-count {
  background: var(--pine-50); color: var(--pine-800);
  border: 1px solid var(--pine-100);
}
.rpt-truncated {
  background: var(--gold-50); color: var(--gold-800);
  border: 1px solid var(--gold-100);
}
.rpt-stamp {
  background: rgba(5, 59, 49, .04); color: var(--ink-50);
  border: 1px solid var(--line-soft); font-weight: 500;
}

/* --- table ---------------------------------------------------------------
   `border-collapse: separate` rather than `collapse`, which is what the sticky
   header needs: under a collapsed border model the shared cell borders belong
   to the table, not the cell, so they scroll out from under a sticky <th> and
   the pinned header ends up floating with no rule beneath it. Separate borders
   travel with the cell that owns them. */

.rpt-table-wrap {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: auto; max-height: 72vh;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--pine-200) transparent;
  animation: rptRise .18s ease both;
}
.rpt-table-wrap::-webkit-scrollbar { width: .6rem; height: .6rem; }
.rpt-table-wrap::-webkit-scrollbar-thumb {
  background: var(--pine-200); border-radius: 999px;
}
.rpt-table-wrap::-webkit-scrollbar-track { background: transparent; }

@keyframes rptRise {
  from { opacity: 0; transform: translateY(.25rem); }
  to   { opacity: 1; transform: none; }
}

.rpt-table {
  width: 100%; border-collapse: separate; border-spacing: 0;
  font-size: .86rem; background: var(--surface); color: var(--ink);
}
.rpt-table th, .rpt-table td {
  padding: .45rem .7rem; white-space: nowrap;
  border-bottom: 1px solid var(--rpt-line-in);
  border-right: 1px solid var(--rpt-line-in);
}
.rpt-table th:last-child, .rpt-table td:last-child { border-right: 0; }

.rpt-table thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--rpt-head); color: var(--pine-800);
  font-weight: 700; text-align: center;
  border-bottom: 1px solid var(--pine-200);
  /* The sticky row keeps its own rule while the body scrolls under it. */
  box-shadow: inset 0 -1px 0 var(--pine-200);
}

/* Quiet striping and a hover tint. The row under the pointer is the whole
   point of a 500-row register on a wide screen: it is what stops the eye
   sliding a line while it crosses ten columns. */
.rpt-table tbody tr:nth-child(even) > td { background: var(--rpt-zebra); }
.rpt-table tbody tr:hover > td {
  background: var(--rpt-hover);
  transition: background .12s ease;
}
/* The leading column carries the row's identity (project, bill, head), so it
   is weighted a little heavier than the figures beside it. */
.rpt-table tbody td:first-child { font-weight: 600; color: var(--ink); }

/* Totals pinned to the bottom of the scroll box: on an 8,000-row register the
   figure that matters is otherwise a very long scroll away. Gold, because it
   is the one row on the screen that is a conclusion rather than a record. */
.rpt-table tfoot td {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--rpt-foot); color: var(--pine-900);
  font-weight: 700;
  border-top: 2px solid var(--gold-400);
  border-bottom: 0;
  box-shadow: inset 0 1px 0 var(--gold-400);
}
.rpt-table tfoot td.total-label {
  text-align: start; letter-spacing: .01em; color: var(--gold-800);
}

.rpt-table .al-right  { text-align: end; }
.rpt-table .al-center { text-align: center; }
.rpt-table .al-left   { text-align: start; }
.rpt-table .mono, .its-grid .mono {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
}
/* Amounts read as figures, not as prose. */
.rpt-table td.mono { color: var(--pine-900); }
/* Long descriptive columns (project names run to 90+ characters) get to wrap
   instead of pushing the amount columns off-screen. */
.rpt-table td.wrap {
  white-space: normal; min-width: 16rem; max-width: 32rem;
  font-weight: 400; line-height: 1.4;
}

/* A caveat shown above a table that did return rows - e.g. a filter that
   could not be applied because the underlying column is empty. */
.rpt-note {
  margin: 0 0 .75rem; padding: .55rem .8rem;
  background: var(--gold-50);
  border: 1px solid var(--gold-100);
  border-inline-start: 3px solid var(--gold-400);
  border-radius: var(--radius);
  color: var(--gold-800);
  font-size: .82rem; line-height: 1.45;
}

.rpt-empty { padding: 2.75rem 1rem; text-align: center; color: var(--ink-70); }
.rpt-empty-icon { color: var(--pine-200); margin-bottom: .5rem; }
.rpt-empty-icon svg { width: 2.5rem; height: 2.5rem; }
.rpt-empty-title {
  font-weight: 700; color: var(--pine-800); margin-bottom: .3rem;
  font-size: 1rem;
}
.rpt-empty-hint { font-size: .82rem; color: var(--ink-50); }

/* --- narrow screens ---
   A 10-column register (project_history) cannot become readable on a 390px
   phone by reflowing - the columns are a fixed accounting layout and stacking
   them into cards loses the row-to-row comparison the register exists for. So
   the table keeps its shape and scrolls horizontally, and the work goes into
   making that scroll obvious and usable: tighter type, a pinned first column
   so you always know which row you are reading, and momentum scrolling. */
@media (max-width: 720px) {
  .report-screen > .screen-head { padding: .75rem .85rem; }
  .rpt-actions { width: 100%; }
  .rpt-actions .btn { flex: 1 1 0; }

  .rpt-table { font-size: .8rem; }
  .rpt-table th, .rpt-table td { padding: .4rem .55rem; }
  .rpt-table td.wrap { min-width: 11rem; max-width: 15rem; }

  /* First column pinned: on a wide register the row label scrolls away and
     every figure becomes anonymous.

     `background: inherit` rather than a fixed colour - the pinned cell has to
     be opaque, and inheriting the row's own background is what keeps it in
     step with the zebra stripe and the hover tint instead of punching a white
     hole through both. */
  .rpt-table tbody td:first-child {
    position: sticky; inset-inline-start: 0; z-index: 1;
    background: var(--surface);
    box-shadow: 1px 0 0 var(--rpt-line);
  }
  .rpt-table tbody tr:nth-child(even) > td:first-child { background: var(--rpt-zebra); }
  .rpt-table tbody tr:hover > td:first-child { background: var(--rpt-hover); }

  .rpt-table thead th:first-child {
    position: sticky; inset-inline-start: 0; z-index: 3;
    box-shadow: inset 0 -1px 0 var(--pine-200), 1px 0 0 var(--rpt-line);
  }
  .rpt-table tfoot td:first-child {
    position: sticky; inset-inline-start: 0; z-index: 3;
    background: var(--rpt-foot-solid);
    box-shadow: inset 0 1px 0 var(--gold-400), 1px 0 0 var(--rpt-line);
  }

  .rpt-table-wrap, .table-wrap { max-height: 60vh; }
}

/* Hint that a table scrolls sideways, shown only where it actually can. */
.rpt-scroll-hint {
  display: none;
  font-size: .76rem; color: var(--ink-50);
  margin: .5rem 0 0; text-align: center;
}
@media (max-width: 720px) { .rpt-scroll-hint.is-scrollable { display: block; } }

/* --- loading -------------------------------------------------------------
   A shimmering skeleton the shape of the table it is replacing, rather than an
   ellipsis. Changing a filter re-runs the query server-side; on the larger
   registers that is a second or more, and a bare "…" in an empty panel reads
   as "the screen has broken" rather than "the figures are coming".

   The global prefers-reduced-motion rule in section 11 stops the shimmer for
   anyone who has asked for that; the skeleton itself still shows. */
.rpt-skeleton { display: grid; gap: .45rem; padding: .2rem 0; }
.rpt-skeleton span {
  display: block; height: 2.1rem; border-radius: var(--radius);
  background: linear-gradient(90deg,
              rgba(5, 59, 49, .05) 25%,
              rgba(5, 59, 49, .11) 37%,
              rgba(5, 59, 49, .05) 63%);
  background-size: 400% 100%;
  animation: rptShimmer 1.2s ease-in-out infinite;
}
.rpt-skeleton span:first-child { height: 2.6rem; }
@keyframes rptShimmer {
  from { background-position: 100% 50%; }
  to   { background-position: 0 50%; }
}

.is-loading { padding: 2rem; text-align: center; color: var(--ink-50); }

/* 9b. Bill Entry / Approval / Delete --------------------------------------- */
/* The original laid these screens out as stacked <table class="basicentrytable">
   blocks: a right-aligned label in column 1, its control in column 2. That is
   exactly what .entry-grid above already does, so these screens reuse it and
   only add what is specific to them. */

.bill-screen { max-width: 100%; display: grid; gap: 1rem; justify-items: start; }
.bill-card { width: 100%; }
/* The line grid needs the full width; the label/control cards do not. */
.bill-card--wide { max-width: none; }

/* A conditional label+control pair. `display: contents` keeps both as direct
   children of .entry-grid, so they stay in their columns, while a single
   `hidden` on the wrapper removes the pair together. */
.row-pair { display: contents; }

.fld-hint { margin: .25rem 0 0; font-size: .76rem; color: var(--ink-50); }
.fld-hint:empty { display: none; }
.fld-hint.is-ok { color: var(--pine-600); font-weight: 600; }
.fld-hint.is-warning { color: var(--danger); }

/* Short controls — dates, amounts, codes — should not stretch to 28rem. */
.field--short { max-width: 14rem; }

.radio-row { display: flex; gap: 1.25rem; align-items: center; }
.radio { display: inline-flex; align-items: center; gap: .4rem;
         font-size: .875rem; font-weight: 400; cursor: pointer; }
.radio input { accent-color: var(--pine-600); }

.cheque-row { display: flex; gap: .5rem; max-width: 20rem; }
.cheque-series { max-width: 6rem; flex: none; }

/* Read-only computed totals: styled as inputs so the column lines up, but
   clearly not editable. */
.its-input--output { display: block; background: rgba(5,59,49,.04); font-weight: 700; }
.its-input--net { color: var(--pine-800); }

/* Below ৳1,000 of budget left the field goes red, as it did in the original —
   the cue that the project can no longer carry another bill. */
#RemainingBudget.is-low {
  background: var(--danger-bg); color: var(--danger);
  border-color: var(--danger); font-weight: 700;
}

.bill-status {
  align-self: center; padding: .3rem .75rem; border-radius: 999px;
  font-size: .78rem; font-weight: 700;
  background: var(--gold-50); color: var(--gold-800);
}
.bill-status.is-approved { background: var(--pine-50); color: var(--pine-700); }

/* The add row sits inside the table, as the original's entry-template row did. */
.line-entry-row td { background: rgba(5,59,49,.03); vertical-align: middle; }
.line-entry-row .its-input { min-width: 9rem; }
.bill-lines td { white-space: normal; }
.bill-lines tbody tr.is-editing { background: var(--gold-50); }
.bill-lines-empty { text-align: center; color: var(--ink-50); padding: 1.5rem !important; }
.bill-lines tfoot td {
  border-top: 2px solid var(--line);
  font-weight: 700; background: var(--pine-50); color: var(--pine-800);
}

@media (max-width: 720px) {
  .cheque-row { flex-wrap: wrap; }
  .cheque-series { max-width: none; }
  .field--short { max-width: none; }
}

/* 10. Login --------------------------------------------------------------- */

.login-page {
  min-height: 100%;
  display: grid; place-items: center;
  padding: 2.5rem 1rem;
  background:
    radial-gradient(1100px 520px at 50% -8%, var(--pine-50), transparent 62%),
    linear-gradient(160deg, #f7f5ef, #e9e3d6);
}
.login-box { width: 100%; max-width: 26rem; }
.login-head { text-align: center; margin-bottom: 1.4rem; }
.login-logo {
  height: 4.75rem; width: 4.75rem; margin: 0 auto .75rem;
  border-radius: 50%; background: #fff; object-fit: contain;
  box-shadow: 0 0 0 3px rgba(11,107,91,.14), var(--shadow);
}
.login-title { font-family: var(--font-bn); font-size: 1.35rem; color: var(--pine-800); line-height: 1.3; }
.login-sub { font-size: .85rem; color: var(--ink-50); margin-top: .15rem; }

.login-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.login-card-head {
  background: linear-gradient(100deg, var(--pine-800), var(--pine-600));
  color: #fff; padding: .8rem 1.4rem; font-weight: 700; letter-spacing: .02em;
}
.login-form { padding: 1.4rem; display: grid; gap: .95rem; }
.login-hint { font-size: .85rem; color: var(--ink-50); margin: 0; }
.login-field { display: grid; gap: .3rem; }
.login-msg { min-height: 1.25rem; margin: 0; font-size: .85rem; color: var(--danger); }
.login-foot { margin-top: 1rem; display: flex; justify-content: center; }

/* --- message box (ITS.mb in its.core.js) --- */

.mb-overlay {
  /* Above every other overlay, the PDF progress ring (90) included: a message
     box is what reports a failure, and one that opened behind the thing that
     failed would be invisible. */
  position: fixed; inset: 0; z-index: 100;
  display: grid; place-items: center; padding: 1rem;
  background: rgba(3, 25, 20, .5);
}
.mb-box {
  width: 100%; max-width: 24rem;
  background: var(--surface); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); overflow: hidden;
}
.mb-head {
  padding: .75rem 1.25rem; font-weight: 700;
  background: var(--pine-700); color: #fff;
}
.mb-head--error { background: var(--danger); }
.mb-body { padding: 1.1rem 1.25rem; font-size: .9rem; color: var(--ink); }
.mb-foot {
  display: flex; justify-content: flex-end; gap: .5rem;
  padding: .75rem 1.25rem; background: rgba(5,59,49,.04);
  border-top: 1px solid var(--line-soft);
}

/* 11. Utilities ----------------------------------------------------------- */

.stack   { display: grid; gap: .75rem; }
.row     { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.spread  { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.muted   { color: var(--ink-50); }
.hidden  { display: none !important; }

/* The `hidden` ATTRIBUTE only carries `display: none` from the UA sheet, so any
   `display:` in this file beats it — .fld, .btn, .entry-grid and .card all did,
   which left fields that JavaScript had hidden (the Edit-mode bill number, the
   Security-refund project start year, the Get-Last-Bill button) on screen the
   whole time. Attribute and class must behave the same. */
[hidden] { display: none !important; }
.bn-num  { font-variant-numeric: tabular-nums; }

.alert { padding: .7rem .9rem; border-radius: var(--radius); font-size: .875rem; }
.alert-error { background: var(--danger-bg); color: var(--danger); }
.alert-ok    { background: var(--ok-bg);     color: var(--ok); }

/* Screen-reader-only, for labels that must exist but not be seen. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
}

/* 12. Print --------------------------------------------------------------- */
/* Ctrl+P on a report screen should produce the table, not the chrome. The
   PDF export (reports/report_pdf.html) is the authoritative printed form,
   but browser print should at least be sane. */

/* The printed letterhead. display:none until @media print takes over, so it
   costs the screen nothing but a few hundred bytes of markup. Same eight fields
   and same layout as the PDF (templates/reports/report_pdf.html), from the same
   source (reports/header.py), so a printed screen and a printed PDF carry the
   same header rather than two versions of it. */
.print-head { display: none; }

@page { size: A4 portrait; margin: 13mm 10mm 14mm; }

@media print {
  /* ---- what goes away ----
     Chrome, and every control that does something a sheet of paper cannot. */
  .app-header, .app-sidebar, .app-scrim,
  .rpt-actions, .rpt-filters, .rpt-find, .its-pager, .search-bar,
  .rpt-scroll-hint, .rpt-skeleton, .rpt-stamp,
  .rpt-empty-icon { display: none !important; }

  .app-shell { display: block; height: auto; }
  .app-content { overflow: visible; padding: 0; }
  html, body { background: #fff; color: #000; }

  /* ---- the letterhead ----
     A printed report used to be a bare grid of figures with nothing on it
     naming the council it came from, which is not a document anybody can file. */
  /* Same arrangement as the PDF (static/css/report_print.css) and as the
     office's own vouchers: logo at the left with the two title lines beside it
     and left-aligned, the contact block hard right as an aligned label/value
     list, closed by a heavy rule. */
  .print-head { display: block; margin-bottom: 8pt; }
  .print-head-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12pt;
    border-bottom: 1.5pt solid #000; padding-bottom: 6pt;
  }
  .print-head-brand { display: flex; align-items: center; gap: 9pt; min-width: 0; }
  .print-head-logo { width: 46pt; height: 46pt; object-fit: contain; flex: none; }
  .print-head-org { min-width: 0; }
  .print-head-org .line1 { font-size: 14pt; font-weight: 700; line-height: 1.25; }
  .print-head-org .line2 { font-size: 12pt; font-weight: 700; line-height: 1.25; }
  .print-head-meta {
    flex: none; border-collapse: collapse; max-width: 190pt;
    font-size: 7.5pt; line-height: 1.35;
  }
  .print-head-meta td {
    border: 0 !important; padding: 0 !important;
    background: none !important; vertical-align: top;
  }
  .print-head-meta .cm-label { white-space: nowrap; padding-inline-end: 6pt !important; }
  .print-head-meta .cm-sep { width: 6pt; text-align: center; }
  .print-head-meta .cm-value { word-wrap: break-word; }
  .print-title {
    text-align: center; font-size: 12pt; font-weight: 700;
    margin: 10pt 0 2pt;
  }
  /* The screen's own title block would repeat what .print-head just said. */
  .report-screen > .screen-head { display: none !important; }

  /* ---- fitting A4 ----
     This is the change that makes a browser print usable. On screen every cell
     is `white-space: nowrap` so the money columns stay aligned and the table
     scrolls sideways inside its own box. Sent to a printer that way, a
     ten-column register simply runs off the right edge of the sheet and the
     last three columns are never seen. On paper the cells wrap instead, and the
     table - which is width:100% - is then laid out to the page.

     Amounts are the exception: an amount broken across two lines is a misread
     waiting to happen, and they are short enough to always fit. */
  .rpt-table, .rpt-table th, .rpt-table td {
    white-space: normal !important;
    overflow-wrap: anywhere;
  }
  .rpt-table td.mono, .rpt-table th.al-right {
    white-space: nowrap !important; overflow-wrap: normal;
  }
  /* The screen reserves 16-32rem for a description column so it does not crush
     the amounts. On paper that is a demand for more width than the sheet has. */
  .rpt-table td.wrap { min-width: 0 !important; max-width: none !important; }

  .rpt-table-wrap, .table-wrap {
    max-height: none !important; overflow: visible !important;
    border: 0; box-shadow: none; border-radius: 0;
    animation: none !important;
  }
  .rpt-table { font-size: 8pt !important; width: 100% !important; }
  .rpt-table th, .rpt-table td { padding: 2pt 4pt !important; }

  /* Sticky is a scrolling behaviour; on paper it either does nothing or pins a
     row to the wrong place. */
  .rpt-table thead th, .rpt-table tfoot td,
  .rpt-table tbody td:first-child,
  .rpt-table thead th:first-child,
  .rpt-table tfoot td:first-child {
    position: static !important; box-shadow: none !important;
  }

  thead { display: table-header-group; }   /* repeat headers across pages */
  tfoot { display: table-row-group; }      /* the grand total prints once, at the end */
  tr, img { break-inside: avoid; }

  /* ---- flatten the palette ----
     The screen is deliberately coloured (section 9) while a filed register is
     black on white. Browsers drop background colours by default but keep
     borders, so stating the border explicitly - rather than relying on a
     background - is what makes the printed grid survive. */
  .rpt-table { border-collapse: collapse !important; background: #fff !important; }
  .rpt-table th, .rpt-table td,
  .rpt-table tbody tr:nth-child(even) > td,
  .rpt-table tbody tr:hover > td,
  .rpt-table tfoot td {
    background: #fff !important; color: #000 !important;
    border: 1px solid #000 !important;
  }
  .rpt-table thead th, .rpt-table tfoot td { font-weight: 700 !important; }

  /* The row count and - far more importantly - the truncation notice stay.
     The screen shows at most 500 rows, so a printed screen is frequently a
     PARTIAL register; the line saying so is the difference between a short
     printout and a wrong one. It prints as plain text, not a tinted pill. */
  .rpt-meta { display: block !important; margin-bottom: 6pt !important; }
  .rpt-chip, .rpt-count, .rpt-truncated, .rpt-note {
    display: inline !important;
    background: none !important; color: #000 !important;
    border: 0 !important; border-radius: 0 !important;
    padding: 0 !important; font-size: 7.5pt !important;
  }
  .rpt-chip svg { display: none !important; }
  .rpt-truncated::before { content: "— "; }
  .rpt-note { display: block !important; font-style: italic; margin: 0 0 5pt !important; }
  .rpt-subtitle {
    display: block !important; color: #000 !important;
    font-size: 8pt !important; text-align: center; margin: 0 0 4pt !important;
  }

  /* A quick find hides rows with the `hidden` attribute. Printing while one is
     active should print what is on screen — the filtered rows — which is what
     [hidden] already does, so nothing here undoes it. */
}

/* 13. Rich-text editor and image field ------------------------------------
   Used by the Signatory & Seal setup screen (crud/entity.html renders the
   wrappers; static/js/editors.js builds the toolbar and the editable area).

   No CDN editor, so no vendor stylesheet either - what follows is the whole
   look of the control. */

/* A seal editor is a block, not a value in a label/value grid. Both the label
   and the control span the full width, so the label is not left sitting alone
   in the first column with an empty cell beside it. */
.field--wide { grid-column: 1 / -1; max-width: none !important; }
.field-label--wide {
  grid-column: 1 / -1;
  text-align: start !important;
  margin-top: .6rem;
}

.rte {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}
.rte:focus-within { border-color: var(--pine-600); box-shadow: 0 0 0 3px rgba(11,107,91,.16); }

.rte-bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: .2rem;
  padding: .35rem .45rem;
  background: var(--pine-50);
  border-bottom: 1px solid var(--line-soft);
}
.rte-sep { width: 1px; height: 1.2rem; background: var(--line); margin: 0 .25rem; }

.rte-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.9rem; height: 1.9rem; padding: 0 .35rem;
  border: 1px solid transparent; border-radius: .35rem;
  background: transparent; color: var(--pine-800);
  font-size: .85rem; line-height: 1;
}
.rte-btn:hover { background: var(--pine-100); }
.rte-btn.is-on { background: var(--pine-700); color: #fff; }

.rte-select {
  height: 1.9rem; border: 1px solid var(--line); border-radius: .35rem;
  background: var(--surface); color: var(--ink); font-size: .8rem;
  padding: 0 .35rem;
}
.rte-color {
  width: 1.9rem; height: 1.9rem; padding: 0;
  border: 1px solid var(--line); border-radius: .35rem; background: none;
}

/* The editable area. min-height rather than a row count: a seal is usually four
   or five lines, and a box that grows with its content beats one that scrolls. */
.rte-area {
  min-height: 8.5rem; max-height: 26rem; overflow: auto;
  padding: .7rem .8rem;
  outline: none;
  line-height: 1.5;
}
.rte-area:empty::before {
  content: attr(data-placeholder);
  color: var(--ink-35);
  pointer-events: none;
}
/* A pasted design brings its own sizing; only the outer bounds are imposed, so
   an oversized stamp cannot push the form sideways. */
.rte-area img { max-width: 100%; height: auto; }
.rte-area table { border-collapse: collapse; }

.rte-source {
  display: block; width: 100%; min-height: 10rem;
  border: 0; outline: none; resize: vertical;
  padding: .7rem .8rem;
  font-family: var(--font-num); font-size: .8rem; line-height: 1.5;
  background: #fbfaf7; color: var(--ink);
}

/* --- image field (a data: URI in a hidden input) --- */
.imgfield { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.imgfield-preview {
  max-height: 4.5rem; max-width: 14rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: #fff; padding: .25rem;
}
.imgfield-pick { font-size: .82rem; }

/* 14. Voucher generate screen ---------------------------------------------
   Form on the left, the actual document on the right. The document is rendered
   by the server from templates/reports/_voucher_doc.html - the same partial the
   PDF uses - so what follows styles that markup for a screen, and
   static/css/voucher_print.css styles the identical markup for paper. */

.vch-workspace {
  display: grid;
  grid-template-columns: minmax(0, 26rem) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}
@media (max-width: 1180px) {
  .vch-workspace { grid-template-columns: minmax(0, 1fr); }
}

.vch-form { display: grid; gap: 1rem; min-width: 0; }

.vch-fs {
  margin: 0; border: 1px solid var(--line); border-radius: var(--radius-lg);
  background: var(--surface); box-shadow: var(--shadow-sm);
  padding: 1rem 1.1rem 1.15rem;
}
.vch-fs > legend {
  display: flex; align-items: center; gap: .5rem;
  padding: 0 .4rem; margin-inline-start: -.4rem;
  font-size: .9rem; font-weight: 700; color: var(--pine-800);
}
.vch-step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.4rem; height: 1.4rem; border-radius: 999px;
  background: var(--pine-700); color: #fff;
  font-size: .75rem; font-weight: 700;
}

.vch-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .75rem .9rem; margin-top: .8rem;
}
@media (max-width: 560px) { .vch-grid { grid-template-columns: minmax(0, 1fr); } }
/* .fld is sized for the report filter bar (flex, capped at 22rem); inside this
   grid the cell decides the width instead. */
.vch-grid .fld, .vch-form .fld { max-width: none; flex: none; }
.fld--full { grid-column: 1 / -1; }

.vch-note { margin: .55rem 0 0; font-size: .78rem; color: var(--ink-50); }
.vch-note--empty { font-style: italic; }

/* --- signatory tick list --- */
.vch-sigs { list-style: none; margin: .75rem 0 0; padding: 0; display: grid; gap: .35rem; }
.vch-sig > label {
  display: grid;
  grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: center; gap: .55rem;
  padding: .5rem .6rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  cursor: pointer;
}
.vch-sig > label:hover { border-color: var(--pine-200); background: var(--pine-50); }
.vch-sig.is-on > label { border-color: var(--pine-600); background: var(--pine-50); }

/* The position badge. The ticked ORDER is the printed order, so it has to be
   visible - without it, three ticked names give no clue which seal prints first. */
.vch-sig-order {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.35rem; height: 1.35rem; border-radius: 999px;
  background: var(--gold-500); color: #fff; font-size: .72rem; font-weight: 700;
}
.vch-sig-order:empty { background: transparent; }

.vch-sig-body { display: grid; min-width: 0; }
.vch-sig-name  { font-weight: 600; }
.vch-sig-desig { font-size: .78rem; color: var(--ink-70); }
.vch-sig-org   { font-size: .74rem; color: var(--ink-50); }
.vch-sig-tags  { display: flex; gap: .25rem; flex-wrap: wrap; justify-content: end; }

.tag {
  font-size: .68rem; font-weight: 600; padding: .1rem .4rem;
  border-radius: 999px; background: var(--pine-50); color: var(--pine-700);
  white-space: nowrap;
}
.tag--ok    { background: var(--ok-bg); color: var(--ok); }
.tag--muted { background: rgba(5,59,49,.06); color: var(--ink-50); }

.alert-warn { background: var(--warn-bg); color: var(--warn); }
.vch-warn { margin-bottom: .75rem; }
.vch-warn ul { margin: .35rem 0 0; padding-inline-start: 1.1rem; }

/* --- the result pane --- */
.vch-result { min-width: 0; }
.vch-result.is-loading { opacity: .55; }

.vch-blank {
  display: grid; justify-items: center; gap: .6rem;
  padding: 4rem 1rem; text-align: center;
  color: var(--ink-50);
  border: 1px dashed var(--line); border-radius: var(--radius-lg);
  background: var(--surface);
}
.vch-blank p { margin: 0; font-size: .875rem; }

/* The sheet. Set on white with A4-ish proportions so what is on screen reads as
   the page that will come out of the printer, not as one more web panel. */
.vch-paper {
  background: #fff; color: #000;
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: 2.2rem 2rem;
  max-width: 52rem;
}

.vch { font-size: .95rem; line-height: 1.55; }

/* Letterhead - the same markup the PDF uses (.head / .head-brand / .head-meta,
   styled for paper in static/css/report_print.css), styled here for a screen. */
.vch .head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
  border-bottom: 2px solid #000; padding-bottom: .5rem;
}
.vch .head-brand { display: flex; align-items: center; gap: .7rem; min-width: 0; }
.vch .head-logo { width: 3.2rem; height: 3.2rem; object-fit: contain; flex: none; }
.vch .head-org .line1 { font-size: 1.2rem; font-weight: 700; line-height: 1.25; white-space: nowrap; }
.vch .head-org .line2 { font-size: 1.05rem; font-weight: 700; line-height: 1.25; white-space: nowrap; }
.vch .head-meta {
  flex: none; border-collapse: collapse; font-size: .72rem; line-height: 1.4;
  max-width: 15rem;
}
.vch .head-meta td { border: 0; padding: 0; background: none; vertical-align: top; }
.vch .head-meta .cm-label { white-space: nowrap; padding-inline-end: .4rem; }
.vch .head-meta .cm-sep { width: .5rem; text-align: center; }
.vch .head-meta .cm-value { word-wrap: break-word; }

.vch-caption { text-align: center; font-size: 1.05rem; font-weight: 400; margin: 1rem 0 1.1rem; }

.vch-block { margin: 0 0 .9rem; }
.vch-row { display: flex; align-items: baseline; gap: .15rem; margin: 0 0 .2rem; }
.vch-row--split { justify-content: space-between; gap: 1.5rem; }
.vch-line { display: flex; align-items: baseline; gap: .15rem; min-width: 0; }
.vch-line--right { flex: none; }
.vch-lbl, .vch-sep { white-space: nowrap; }
.vch-val { min-width: 0; }
.vch-val--strong { font-weight: 700; }
.vch-val--num { white-space: nowrap; font-variant-numeric: tabular-nums; }
.vch-row--wrap { display: block; margin-bottom: .3rem; }
.vch-row--wrap .vch-lbl, .vch-row--wrap .vch-sep, .vch-row--wrap .vch-val { display: inline; }

/* Unruled columnar list, as on the office's own vouchers - see the note in
   static/css/voucher_print.css for why no grid is drawn. */
.vch-lines { width: 100%; table-layout: fixed; border-collapse: collapse; margin: 1.1rem 0 1rem; }
.vch-lines th, .vch-lines td {
  border: 0; background: none; padding: .3rem .35rem; vertical-align: top;
  overflow-wrap: anywhere;
}
.vch-lines thead th { font-weight: 700; padding-bottom: .45rem; border-bottom: 1px solid #000; }
.vch-lines tbody td { padding-top: .45rem; padding-bottom: .45rem; }
.vch-lines .c-sl   { width: 11%; text-align: center; }
.vch-lines .c-code { width: 17%; text-align: center; white-space: nowrap; }
.vch-lines .c-desc { width: 46%; }
.vch-lines .c-amt  { width: 26%; text-align: right; white-space: nowrap;
                     font-variant-numeric: tabular-nums; }
.vch-empty { text-align: center; padding: 1.2rem 0; color: var(--ink-50); }

/* One after another with a standard gap, wrapping to a further row once they no
   longer fit. `flex: 0 0 auto` — no minimum width — is what makes them sit as a
   row of stamps rather than as evenly padded columns; see the fuller note in
   static/css/voucher_print.css, which sets the same layout for paper. */
.vch-seals {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: 1.1rem 1.9rem; margin-top: 2.6rem;
}
.vch-seal { flex: 0 0 auto; max-width: 13rem; break-inside: avoid; }
.vch-seal img { max-width: 100%; height: auto; }
.vch-sign { display: block; max-height: 3.4rem; margin-bottom: .15rem; }
/* Tight, like the composed block: a seal is one impression, so its lines belong
   against each other. */
.vch-seal-body { font-size: .85rem; line-height: 1.15; }
.vch-seal-body--plain { padding-top: 1.8rem; line-height: 1.25; }
.vch-seal-rule { border-top: 1px solid #000; width: 11rem; margin-bottom: .2rem; }
.vch-seal-name  { font-weight: 700; }
.vch-seal-desig { font-size: .8rem; }
.vch-seal-org   { font-size: .76rem; }

/* --- printing the voucher screen ---
   Ctrl+P gives the sheet and nothing else. The PDF button is the authoritative
   printed form (it applies static/css/voucher_print.css through WeasyPrint), but
   a browser print must at least produce a filable voucher rather than the app
   around one. */
@media print {
  .voucher-screen > .screen-head,
  .voucher-screen .vch-form,
  .voucher-screen .vch-warn,
  .voucher-screen .vch-blank { display: none !important; }

  .vch-workspace { display: block; }
  .vch-paper {
    border: 0; box-shadow: none; border-radius: 0; padding: 0; max-width: none;
  }
  .vch-result { opacity: 1 !important; }
  .vch { font-size: 11pt; color: #000; }
  .vch .head-org .line1 { font-size: 14pt; }
  .vch .head-org .line2 { font-size: 12pt; }
  .vch .head-meta { font-size: 8.5pt; max-width: 210pt; }
  .vch-caption { font-size: 12.5pt; }
  .vch-lines th, .vch-lines td { color: #000 !important; background: none !important; }
  .vch-seal, .vch-lines tr { break-inside: avoid; }
}

/* 15. PDF download progress ---------------------------------------------
   Shown by static/js/pdf_progress.js while a PDF is produced and fetched.
   Every PDF button in the app goes through it.

   The dial is split and each half is labelled, because only half of the wait
   can be measured: the server typesetting the document reports nothing, and the
   bytes arriving report exactly. See the note at the top of pdf_progress.js. */

.pdfp-overlay {
  position: fixed; inset: 0; z-index: 90;
  display: grid; place-items: center;
  background: rgba(3, 26, 21, .45);
  padding: 1rem;
  animation: pdfp-fade .12s ease-out;
}
@keyframes pdfp-fade { from { opacity: 0; } to { opacity: 1; } }

.pdfp-box {
  background: var(--surface);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  padding: 1.8rem 2rem 1.4rem;
  width: min(22rem, 100%);
  text-align: center;
}

.pdfp-ring-wrap { position: relative; width: 8.5rem; margin: 0 auto .9rem; }
.pdfp-ring { display: block; width: 100%; height: auto; transform: rotate(-90deg); }

.pdfp-track {
  fill: none; stroke: var(--line-soft); stroke-width: 9;
}
.pdfp-bar {
  fill: none; stroke: var(--pine-600); stroke-width: 9; stroke-linecap: round;
  /* The eased estimate steps every 120ms and the byte counter arrives in
     bursts; without this the ring would tick rather than sweep. */
  transition: stroke-dashoffset .18s linear;
}
.pdfp-box.is-error .pdfp-bar { stroke: var(--danger); }
.pdfp-box.is-done  .pdfp-bar { stroke: var(--ok); }

/* The figure sits over the ring rather than inside the SVG, so it renders in
   the page's own Bangla face - an SVG <text> would not pick up --font-num, and
   this number is written in Bengali digits when the app is. */
.pdfp-pct {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-size: 1.5rem; font-weight: 700; color: var(--pine-800);
  font-variant-numeric: tabular-nums;
}
.pdfp-box.is-error .pdfp-pct { color: var(--danger); }

.pdfp-title {
  font-weight: 700; font-size: .95rem; color: var(--ink);
  margin-bottom: .2rem;
}
.pdfp-title:empty { display: none; }

.pdfp-phase { font-size: .85rem; color: var(--ink-70); }
.pdfp-note {
  margin-top: .5rem; font-size: .78rem; color: var(--ink-50);
  line-height: 1.4;
}

.pdfp-actions {
  display: flex; justify-content: center; gap: .5rem; flex-wrap: wrap;
  margin-top: 1.1rem;
}
.pdfp-actions:empty { display: none; }

/* A busy PDF button is disabled while the ring is up; say so visually rather
   than leaving it looking clickable. */
.btn.is-busy { opacity: .7; }

@media (prefers-reduced-motion: reduce) {
  .pdfp-overlay { animation: none; }
  .pdfp-bar { transition: none; }
}

@media print { .pdfp-overlay { display: none !important; } }

/* --- extra accounting lines on the voucher screen ---
   Rows the office types onto a voucher for a code the bill does not carry.
   A grid rather than a table so the row collapses to a stack on a narrow
   screen instead of scrolling sideways inside a form. */
.vch-xl { display: grid; gap: .35rem; margin-top: .8rem; }

.vch-xl-head,
.vch-xl-row {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr) 7rem 3rem 1.9rem;
  gap: .4rem;
  align-items: center;
}
.vch-xl-head {
  font-size: .72rem; font-weight: 600; color: var(--ink-50);
  text-transform: uppercase; letter-spacing: .02em;
}
.vch-xl-head span:nth-child(3) { text-align: end; }
.vch-xl-head span:nth-child(4) { text-align: center; }

.vch-xl-row .input { padding: .35rem .5rem; font-size: .85rem; }
.vch-xl-row [data-xl-amt] { text-align: end; font-variant-numeric: tabular-nums; }
.vch-xl-ded { display: grid; place-items: center; }

.vch-xl-del {
  display: grid; place-items: center;
  width: 1.7rem; height: 1.7rem; border-radius: var(--radius);
  border: 1px solid transparent; background: transparent;
  color: var(--ink-50); font-size: 1.1rem; line-height: 1;
}
.vch-xl-del:hover { background: var(--danger-bg); color: var(--danger); }

.vch-xl:empty + [data-xl-add] { margin-top: .4rem; }

@media (max-width: 620px) {
  .vch-xl-head { display: none; }
  .vch-xl-row {
    grid-template-columns: minmax(0, 1fr) 4.5rem 1.9rem;
    padding: .45rem; border: 1px solid var(--line); border-radius: var(--radius);
  }
  .vch-xl-row [data-xl-code] { grid-column: 1 / -1; }
  .vch-xl-row [data-xl-desc] { grid-column: 1 / -1; }
}

/* An extra line on the document itself. Marked, never hidden: it changes the
   net payable, so a reader checking the sheet against the approved bill has to
   see which figures came from where. Italic and a small tag rather than a tint,
   because a tint is the first thing a photocopier loses. */
.vch-extra-mark {
  margin-inline-start: .35rem;
  font-size: .78em;
  font-style: italic;
}

.vch-doc-note {
  margin: .6rem 0 0;
  font-size: .82rem;
  font-style: italic;
}

/* The compose button carries a word, not just a glyph, so it is wider than the
   icon buttons beside it. */
.rte-btn--compose { gap: .3rem; padding: 0 .55rem; font-weight: 600; font-size: .78rem; }
.rte-btn--compose svg { flex: none; }

/* 15. Voucher register ----------------------------------------------------
   The list of vouchers that have actually been issued. A plain grid: this is a
   register, and the thing being scanned for is a row, not a chart. */

.vreg-flagged {
  display: inline-flex; align-items: center; gap: .45rem;
  margin-top: .7rem; font-size: .85rem; color: var(--ink-70);
  cursor: pointer;
}

.tag--warn { background: var(--warn-bg); color: var(--warn); }

.vreg-table td { vertical-align: top; }
.vreg-no { font-weight: 700; color: var(--pine-700); }
.vreg-no:hover { text-decoration: underline; }
.vreg-no.is-busy { opacity: .55; }
.vreg-payee { max-width: 20rem; }
.vreg-when { white-space: nowrap; font-size: .82rem; }
.vreg-by {
  display: block; font-size: .74rem; color: var(--ink-50);
}

/* A flagged row is marked on its leading edge rather than tinted across: the
   status column already says what is wrong, and a full-width tint on every
   unreconciled voucher turns the register into a wall of colour. */
.vreg-table tr.is-flagged > td:first-child {
  box-shadow: inset 3px 0 0 var(--warn);
}

.vreg-pager {
  display: flex; align-items: center; justify-content: center; gap: .8rem;
  margin-top: 1rem; font-size: .85rem; color: var(--ink-70);
}
