/* JobInvoicer -- app styles layered over Bootstrap 5 */

:root {
  /* ---------------------------------------------------------------- palette
     A warm, low-chrome scheme: paper-coloured canvas, clay accent, ink that is
     near-black but never pure black. Kept as the original --fi-* names so the
     forty-odd rules already pointing at them retune without being rewritten. */
  --fi-green:      #2b6cb0;   /* blue -- the single accent color            */
  --fi-green-dark: #1f5185;   /* pressed / hover                             */
  --fi-green-soft: #e9f0f8;   /* accent at 8%, for washes                    */

  --cl-canvas:     #f5f4ee;   /* the page                                    */
  --cl-surface:    #ffffff;   /* cards sit slightly above it                 */
  --cl-ink:        #141413;   /* headings                                    */
  --cl-ink-soft:   #6b6b66;   /* secondary text                              */
  --cl-line:       #e5e3da;   /* hairlines instead of shadows                */
  --cl-charcoal:   #262624;   /* navbar                                      */

  /* Semantic colours, warmed so they sit beside the clay rather than shout.
     Danger is pushed deeper than the accent so the two never read alike. */
  --cl-success:    #3f7a57;
  --cl-danger:     #9b2c2c;
  --cl-info:       #5b6b7c;   /* slate -- was blue, moved aside for the accent */

  --cl-serif: ui-serif, Georgia, "Iowan Old Style", "Times New Roman", serif;
}

body { -webkit-tap-highlight-color: transparent; }

/* ---------------------------------------------------------------- navbar */
.app-navbar {
  background: var(--fi-green);
  box-shadow: 0 1px 3px rgb(0 0 0 / 12%);
}
.app-navbar .nav-link            { color: rgb(255 255 255 / 78%); }
.app-navbar .nav-link:hover,
.app-navbar .nav-link.active     { color: #fff; }
.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  margin-right: .25rem;
  border-radius: .4rem;
  background: #fff;
  color: var(--fi-green);
  font-size: .8rem;
  font-weight: 700;
}

/* ------------------------------------------------------- auth (login/register) */
.auth-body {
  min-height: 100vh;
  background: linear-gradient(160deg, #f5f4ee 0%, #ece9df 55%, #e3ded1 100%);
}
.auth-main {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 2rem 1rem;
  flex-direction: column;
}
.auth-card {
  width: 100%;
  max-width: 27rem;
  border: 0;
  border-radius: 1rem;
  box-shadow: 0 1.5rem 3rem rgb(0 0 0 / 25%);
}
.auth-main .alert { width: 100%; max-width: 27rem; }

/* ------------------------------------------------------------ stat tiles */
.stat-card {
  border: 0;
  border-radius: .85rem;
  box-shadow: 0 1px 2px rgb(16 24 40 / 6%), 0 1px 3px rgb(16 24 40 / 10%);
}
.stat-card .stat-label {
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #667085;
}
.stat-card .stat-value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.2;
}
.stat-card .stat-meta { font-size: .8125rem; color: #667085; }
.stat-accent { height: .25rem; border-radius: .85rem .85rem 0 0; }

.panel {
  border: 0;
  border-radius: .85rem;
  box-shadow: 0 1px 2px rgb(16 24 40 / 6%), 0 1px 3px rgb(16 24 40 / 10%);
}

/* Comfortable touch targets -- this gets used one-handed in a truck. */
@media (max-width: 576px) {
  .btn, .form-control, .form-select { min-height: 2.75rem; }
  .stat-card .stat-value { font-size: 1.5rem; }
}

/* -------------------------------------------------- compact invoice screen
   The invoice page is create + edit + payments on one surface, so it earns a
   tighter density than the rest of the app: the goal is the whole thing on a
   laptop screen without scrolling. Scoped to .inv-compact so nothing else
   inherits it. */
main:has(.inv-compact) { padding-top: .75rem; padding-bottom: .75rem; }

.inv-compact .card-body            { padding: .7rem .8rem; }
.inv-compact .card-header          { padding: .4rem .8rem; }
.inv-compact .card                 { margin-bottom: .6rem !important; }
.inv-compact .form-label           { margin-bottom: .1rem; font-size: .75rem; font-weight: 600; color: #475467; }
.inv-compact .form-text            { margin-top: .1rem; font-size: .72rem; }
.inv-compact .table > :not(caption) > * > *  { padding: .28rem .45rem; }
.inv-compact .table thead th       { font-size: .72rem; text-transform: uppercase; letter-spacing: .03em; }
.inv-compact .invalid-feedback     { font-size: .72rem; }
.inv-compact .alert                { padding: .4rem .6rem; margin-bottom: .5rem; }
.inv-compact hr                    { margin: .5rem 0; }

/* The summary column is the one place a big number still earns its size. */
.inv-compact .stat-value           { font-size: 1.35rem; }

/* Touch targets stay generous on a phone -- compact is a desktop concern. */
@media (min-width: 992px) {
  .inv-compact .form-control-sm,
  .inv-compact .form-select-sm     { min-height: 0; padding: .2rem .5rem; font-size: .82rem; }
}

/* ------------------------------------------- purpose colours on the invoice
   Each card on invoice.php carries an accent that says what it is for, so the
   eye can jump straight to the money without reading headings:
     details  blue    who and when
     services indigo  the work being billed
     payments green   money in
     notes    slate   free text, no money
     summary  amber   what is still owed -- turns green once settled
   Left border rather than a top bar: it costs no vertical space, which the
   compact layout cannot spare. */
.inv-compact .card            { border-left: 3px solid transparent; }

.inv-compact .card-details    { border-left-color: #2563eb; }
.inv-compact .card-services   { border-left-color: #4f46e5; }
.inv-compact .card-payments   { border-left-color: var(--fi-green); }
.inv-compact .card-notes      { border-left-color: #94a3b8; }
.inv-compact .card-summary    { border-left-color: #b45309; }
.inv-compact .card-summary.is-settled { border-left-color: var(--fi-green); }

/* A wash of the same hue behind the heading ties the header to the border. */
.inv-compact .card-services  .card-header { background: rgb(79  70 229 / 6%); }
.inv-compact .card-payments  .card-header { background: rgb(31 122  61 / 7%); }

.inv-compact .card-services  .card-header .fw-semibold { color: #4338ca; }
.inv-compact .card-payments  .card-header .fw-semibold { color: var(--fi-green-dark); }

/* The summary heading follows the same rule as its border. */
.inv-compact .card-summary   .summary-heading           { color: #b45309; }
.inv-compact .card-summary.is-settled .summary-heading  { color: var(--fi-green-dark); }

/* ------------------------------------------------------------------ icons
   Bootstrap Icons ship at a single light-ish weight. Painting a stroke in the
   current color thickens every glyph so an icon-only button still reads as a
   control at a glance -- which matters more here than usual, because the row
   actions carry no text label. */
.bi {
  /* No text-stroke. Stroking an icon font thickens the glyph into its own
     counters -- on the -fill variants the detail closes up and it reads as a
     blob. Weight comes from size and color instead, which stays crisp. */
  vertical-align: -.125em;
  line-height: 1;
}

/* Square icon-only buttons: keep them tappable without a text label to pad
   them out. The truck rule still applies -- 2.75rem on a phone. */
.btn-icon {
  --bs-btn-padding-x: .5rem;
  --bs-btn-padding-y: .3rem;
  line-height: 1;
}
.btn-icon .bi { font-size: 1.1rem; }

@media (max-width: 576px) {
  .btn-icon { min-width: 2.75rem; min-height: 2.75rem; }
}

/* Inside the compact invoice screen they can be a touch tighter on desktop. */
@media (min-width: 992px) {
  .inv-compact .btn-icon { --bs-btn-padding-x: .4rem; --bs-btn-padding-y: .2rem; }
}

/* No box around an icon: the glyph itself is the control. Border, background
   and shadow are stripped, but the intent color from btn-outline-* survives,
   and the keyboard focus ring is deliberately kept -- removing that would
   leave tab users with no idea where they are. */
.btn-icon {
  --bs-btn-bg: transparent;
  --bs-btn-border-color: transparent;
  --bs-btn-hover-bg: transparent;
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-bg: transparent;
  --bs-btn-active-border-color: transparent;
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: transparent;
  /* Outline variants flip the label to white on hover, which would vanish
     against no background. Hold the resting color instead. */
  --bs-btn-hover-color: var(--bs-btn-color);
  --bs-btn-active-color: var(--bs-btn-color);
  box-shadow: none;
}
.btn-icon:hover  { opacity: .65; }
.btn-icon:active { opacity: .9; }
.btn-icon:focus-visible { box-shadow: 0 0 0 .2rem rgb(43 108 176 / 35%); }

/* Without a box to sit in, the glyph carries the weight on its own. */
.btn-icon .bi { font-size: 1.15rem; }

/* Solid variants set their label to white, which was correct when they had a
   coloured background to sit on. Stripping the background left white glyphs on
   a white card -- invisible. Repaint them in their own hue. */
.btn-icon.btn-success {
  --bs-btn-color: var(--fi-green);
  --bs-btn-hover-color: var(--fi-green-dark);
  --bs-btn-active-color: var(--fi-green-dark);
}
.btn-icon.btn-danger {
  --bs-btn-color: #dc3545; --bs-btn-hover-color: #a71d2a; --bs-btn-active-color: #a71d2a;
}
.btn-icon.btn-primary {
  --bs-btn-color: #2563eb; --bs-btn-hover-color: #1d4ed8; --bs-btn-active-color: #1d4ed8;
}
.btn-icon.btn-secondary {
  --bs-btn-color: #6b7280; --bs-btn-hover-color: #4b5563; --bs-btn-active-color: #4b5563;
}

/* Save is the one action worth making unmissable. */
.btn-icon.btn-success .bi { font-size: 1.25rem; }

/* ==========================================================================
   Warm theme
   Paper canvas, ink text, one clay accent, hairline borders instead of
   shadows, and a serif for anything that acts as a title. Bootstrap is steered
   through its own custom properties rather than overridden rule by rule.
   ========================================================================== */

:root {
  --bs-body-bg:          var(--cl-canvas);
  --bs-body-color:       #2b2b28;
  --bs-emphasis-color:   var(--cl-ink);
  --bs-secondary-color:  var(--cl-ink-soft);
  --bs-border-color:     var(--cl-line);
  --bs-link-color:       var(--fi-green-dark);
  --bs-link-hover-color: var(--fi-green);
  --bs-primary:          #2b6cb0;
  --bs-success:          #3f7a57;
  --bs-danger:           #9b2c2c;
}

body,
body.bg-body-tertiary { background: var(--cl-canvas); }

/* .navbar-brand takes the serif face but NOT the ink color: it sits on the
   charcoal bar, where near-black is invisible. Its color is set below. */
h1, h2, h3, .h1, .h2, .h3 {
  font-family: var(--cl-serif);
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--cl-ink);
}
.navbar-brand {
  font-family: var(--cl-serif);
  font-weight: 600;
  letter-spacing: -.01em;
}

/* ------------------------------------------------------------- navbar */
.app-navbar {
  background: var(--cl-charcoal);
  box-shadow: none;
  border-bottom: 1px solid #34342f;
}
.app-navbar .navbar-brand,
.app-navbar .navbar-brand:hover,
.app-navbar .navbar-brand:focus { color: #f5f4ee; }
.app-navbar .nav-link          { color: rgb(245 244 238 / 62%); }
.app-navbar .nav-link:hover,
.app-navbar .nav-link.active   { color: #f5f4ee; }
.brand-mark {
  background: var(--fi-green);
  color: #fff;
  border-radius: .5rem;
  font-weight: 600;
}

/* -------------------------------------------------------------- cards */
.card, .panel, .stat-card {
  background: var(--cl-surface);
  border: 1px solid var(--cl-line);
  border-radius: .75rem;
  box-shadow: none;
}
.card-header { border-bottom: 1px solid var(--cl-line); }

.stat-card .stat-label { color: var(--cl-ink-soft); letter-spacing: .06em; }
.stat-card .stat-value { font-family: var(--cl-serif); color: var(--cl-ink); }
.stat-card .stat-meta  { color: var(--cl-ink-soft); }
.stat-accent           { height: .2rem; }

/* ------------------------------------------------------------ controls */
/* The blue clears 5.4:1 against white in both directions, so unlike the clay
   it can serve as a button background directly. */
.btn-success {
  --bs-btn-bg: var(--fi-green);            --bs-btn-border-color: var(--fi-green);
  --bs-btn-hover-bg: var(--fi-green-dark); --bs-btn-hover-border-color: var(--fi-green-dark);
  --bs-btn-active-bg: var(--fi-green-dark);
}
.btn-outline-success { --bs-btn-color: var(--fi-green-dark); --bs-btn-border-color: var(--fi-green); }
.btn-icon.btn-success { --bs-btn-color: var(--fi-green); --bs-btn-hover-color: var(--fi-green-dark); }
.btn-icon.btn-danger  { --bs-btn-color: var(--cl-danger); --bs-btn-hover-color: #7a2222; }

.form-control, .form-select {
  background: var(--cl-surface);
  border-color: var(--cl-line);
  color: var(--cl-ink);
}
.form-control:focus, .form-select:focus {
  border-color: var(--fi-green);
  box-shadow: 0 0 0 .18rem rgb(43 108 176 / 20%);
}
.table > :not(caption) > * > * { border-color: var(--cl-line); }
.table thead th, .table-light  { background: #faf9f5; color: var(--cl-ink-soft); }
.table-hover > tbody > tr:hover > * { background: #faf8f3; }

.nav-pills .nav-link.active { background: var(--fi-green); }
.badge.text-bg-success { background: var(--cl-success) !important; }
.badge.text-bg-danger  { background: var(--cl-danger)  !important; }
.badge.text-bg-primary { background: var(--cl-info)    !important; }

/* ------------------------------------------------------- sign-in screen */
.auth-body {
  background: linear-gradient(165deg, #f5f4ee 0%, #ece9df 55%, #e3ded1 100%);
}
.auth-card {
  border: 1px solid var(--cl-line);
  box-shadow: 0 1.5rem 3rem rgb(20 20 19 / 8%);
}

/* --------------------------------------- invoice card accents, rewarmed */
.inv-compact .card-details    { border-left-color: #5b6b7c; }
.inv-compact .card-services   { border-left-color: #8a7a5c; }
.inv-compact .card-payments   { border-left-color: var(--cl-success); }
.inv-compact .card-notes      { border-left-color: #c9c5b6; }
.inv-compact .card-summary    { border-left-color: var(--fi-green); }
.inv-compact .card-summary.is-settled { border-left-color: var(--cl-success); }

.inv-compact .card-services .card-header { background: rgb(138 122 92 / 6%); }
.inv-compact .card-payments .card-header { background: rgb(63 122 87 / 6%); }
.inv-compact .card-services .card-header .fw-semibold { color: #6f6247; }
.inv-compact .card-payments .card-header .fw-semibold { color: #2f5c42; }
.inv-compact .card-summary  .summary-heading          { color: var(--fi-green-dark); }
.inv-compact .card-summary.is-settled .summary-heading { color: #2f5c42; }
.inv-compact .form-label { color: var(--cl-ink-soft); }

/* ==========================================================================
   Statement of account
   Modelled on a landscaper's monthly statement: business block top-left,
   account meta top-right, service address beside it, then a ledger that runs
   charge/payment down the page carrying a balance, and a tear-off stub.
   Sized in points so the printed sheet is predictable.
   ========================================================================== */

.stmt {
  background: var(--cl-surface);
  border: 1px solid var(--cl-line);
  border-radius: .75rem;
  padding: 1.5rem;
  font-size: .8125rem;
}

.stmt-head {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  padding-bottom: 1rem;
  border-bottom: 2px solid var(--cl-ink);
}
.stmt-biz      { flex: 1 1 14rem; }
.stmt-biz-name { font-family: var(--cl-serif); font-size: 1.15rem; font-weight: 600; color: var(--cl-ink); }
.stmt-meta     { flex: 0 0 auto; }
.stmt-meta td  { padding: .05rem .35rem; }
.stmt-meta .k,
.stmt-stub .k  { color: var(--cl-ink-soft); text-align: right; }
.stmt-meta .v,
.stmt-stub .v  { font-weight: 600; text-align: right; white-space: nowrap; }
.stmt-to       { flex: 1 1 12rem; }
.stmt-to-label { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; color: var(--cl-ink-soft); }
.stmt-to-name  { font-weight: 600; }
.stmt-to-addr  { color: var(--cl-ink-soft); }

.stmt-table       { width: 100%; border-collapse: collapse; margin-top: .75rem; }
.stmt-table th    { font-size: .7rem; text-transform: uppercase; letter-spacing: .04em;
                    color: var(--cl-ink-soft); border-bottom: 1px solid var(--cl-line);
                    padding: .3rem .4rem; text-align: left; }
.stmt-table td    { padding: .3rem .4rem; border-bottom: 1px solid #f0efe8; }
.stmt-table .c-num  { text-align: right; white-space: nowrap; }
.stmt-table .c-date { white-space: nowrap; }
.stmt-table .c-ref  { color: var(--cl-ink-soft); }

/* Payments read as relief, so they get the positive color, not the alarm one. */
.stmt-credit td      { color: var(--cl-success); }
.stmt-opening td     { color: var(--cl-ink-soft); font-style: italic; }
.stmt-total td       { border-top: 2px solid var(--cl-ink); border-bottom: none;
                       font-weight: 700; padding-top: .5rem; }
.stmt-remit          { font-size: .7rem; letter-spacing: .08em; color: var(--cl-ink-soft); }
.stmt-terms          { margin: .75rem 0 0; text-align: center; font-size: .75rem; color: var(--cl-ink-soft); }

.stmt-stub        { margin-top: 1.5rem; border-top: 1px dashed #b9b5a6; padding-top: 1rem; }
.stmt-stub-note   { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em;
                    color: var(--cl-ink-soft); margin-bottom: .5rem; }
.stmt-stub-body   { display: flex; flex-wrap: wrap; gap: 1.5rem; justify-content: space-between; }
.stmt-stub-left   { flex: 1 1 16rem; }
.stmt-stub-right  { flex: 0 0 auto; }
.stmt-stub td     { padding: .1rem .35rem; }
.stmt-stub .strong { font-size: 1.05rem; }
.stmt-remit-to    { margin-top: .75rem; }

/* ------------------------------------------------------------------ print
   Everything that is chrome disappears; the statement fills the sheet. The
   ledger must not break a row across pages, and the stub stays with its own
   heading. */
@media print {
  @page { size: letter portrait; margin: .6in; }

  body, body.bg-body-tertiary { background: #fff !important; }
  /* Hide chrome only. An earlier version also hid `.card.panel:not(.stmt)`,
     which was scoped to nothing and so blanked the invoice-list table -- that
     table lives inside a card. Pages mark their own chrome with .d-print-none
     instead, which is the only rule that can tell content from furniture. */
  .app-navbar, nav, .alert, .d-print-none, footer { display: none !important; }

  /* On paper a card is furniture, not content: drop the border and padding but
     keep whatever is inside it. */
  .card.panel:not(.stmt) {
    border: 0 !important; border-radius: 0 !important;
    background: transparent !important; box-shadow: none !important;
  }
  .card.panel:not(.stmt) > .card-body { padding: 0 !important; }

  /* overflow:auto clips instead of scrolling on paper -- a wide table would
     lose its right-hand columns silently. */
  .table-responsive { overflow: visible !important; }
  main.container                        { max-width: none; padding: 0 !important; }

  .stmt {
    border: 0; border-radius: 0; padding: 0;
    font-size: 10pt; color: #000; background: #fff;
  }
  .stmt-biz-name                        { font-size: 15pt; }
  .stmt-head                            { border-bottom-color: #000; }
  .stmt-table td, .stmt-table th        { padding: 3pt 4pt; }
  .stmt-table tr                        { page-break-inside: avoid; }
  .stmt-credit td                       { color: #000; }
  .stmt-total td                        { border-top-color: #000; }
  .stmt-stub                            { page-break-inside: avoid; border-top: 1px dashed #000; }
  a                                     { color: #000; text-decoration: none; }
}

/* ==========================================================================
   Printable invoice — the document the customer actually receives.
   Hidden on screen (.d-none), shown only when printing, so the operator's
   editing UI and the customer's document never have to be the same markup.
   ========================================================================== */

.doc { font-size: 10.5pt; color: #000; }

.doc-head        { display: flex; justify-content: space-between; gap: 2rem;
                   padding-bottom: .75rem; border-bottom: 2px solid #000; }
.doc-biz-name    { font-family: var(--cl-serif); font-size: 16pt; font-weight: 600; }
.doc-title       { text-align: right; }
.doc-title-word  { font-family: var(--cl-serif); font-size: 20pt; font-weight: 600;
                   letter-spacing: .02em; margin-bottom: .25rem; }
.doc-title td    { padding: .05rem .3rem; }
.doc-title .k,
.doc-totals .k   { color: #555; text-align: right; }
.doc-title .v    { font-weight: 600; text-align: right; white-space: nowrap; }

.doc-to          { margin: 1rem 0; }
.doc-to-label    { font-size: 8pt; text-transform: uppercase; letter-spacing: .06em; color: #555; }
.doc-to-name     { font-weight: 600; font-size: 11pt; }
.doc-to-addr     { color: #333; }

.doc-table       { width: 100%; border-collapse: collapse; margin-top: .5rem; }
.doc-table th    { font-size: 8pt; text-transform: uppercase; letter-spacing: .04em; color: #555;
                   border-bottom: 1px solid #000; padding: 4pt; text-align: left; }
.doc-table td    { padding: 4pt; border-bottom: 1px solid #ddd; }
.doc-table .c-num { text-align: right; white-space: nowrap; }
.doc-table tr    { page-break-inside: avoid; }

.doc-totals      { margin-left: auto; margin-top: .5rem; }
.doc-totals td   { padding: 2pt 4pt; }
.doc-totals .v   { text-align: right; white-space: nowrap; min-width: 5rem; }
.doc-total-line td { border-top: 1px solid #000; font-weight: 700; }

.doc-block       { margin-top: 1rem; page-break-inside: avoid; }
.doc-block-title { font-size: 8pt; text-transform: uppercase; letter-spacing: .06em;
                   color: #555; margin-bottom: .2rem; }
.doc-pay         { display: flex; justify-content: space-between; border-bottom: 1px solid #eee;
                   padding: 2pt 0; }
.doc-paid        { margin-top: 1rem; padding: 6pt; border: 1px solid #000; text-align: center;
                   font-weight: 600; }
.doc-terms       { margin-top: 1.25rem; text-align: center; font-size: 9pt; color: #555; }
.doc-footnote    { margin: .35rem 0 0; font-size: 8.5pt; color: #666; }

@media print {
  @page { size: letter portrait; margin: .6in; }
  .doc { page-break-inside: auto; }

  /* The list summary prints as a plain report: no cards, no pills, no search. */
  .table > :not(caption) > * > * { border-color: #ccc !important; }
  #invoicesTable thead th        { background: #f2f2f2 !important; color: #000 !important; }
  /* DataTables 2.x renamed every wrapper class (dataTables_length -> dt-length
     and so on). Both sets are listed so the widgets stay hidden whichever
     version is loaded -- with only the 1.x names, "entries per page",
     "Search invoices…" and "Showing 1 to 7 of 7" all printed. */
  .dt-length, .dt-search, .dt-info, .dt-paging, .dt-layout-start, .dt-layout-end,
  .dataTables_wrapper .dataTables_filter,
  .dataTables_wrapper .dataTables_length,
  .dataTables_wrapper .dataTables_paginate,
  .dataTables_wrapper .dataTables_info   { display: none !important; }

  /* An empty layout row still reserves space; collapse it. */
  .dt-layout-row:not(:has(.dt-layout-table)) { display: none !important; }

  /* The list prints on the same template as an invoice: same type sizes, same
     rules, same uppercase column heads. One document language, three pages. */
  #invoicesTable {
    width: 100% !important;
    border-collapse: collapse;
    font-size: 10pt;
    margin-top: .5rem;
  }
  #invoicesTable thead            { display: table-header-group; }
  #invoicesTable tfoot            { display: table-footer-group; }
  #invoicesTable tr               { page-break-inside: avoid; }
  #invoicesTable thead th {
    font-size: 8pt; text-transform: uppercase; letter-spacing: .04em;
    color: #555 !important; background: transparent !important;
    border-bottom: 1px solid #000 !important; padding: 4pt !important;
    text-align: left;
  }
  #invoicesTable tbody td         { padding: 4pt !important; border-bottom: 1px solid #ddd !important; }
  #invoicesTable tfoot td {
    padding: 4pt !important; font-weight: 700;
    border-top: 1px solid #000 !important; border-bottom: none !important;
  }
  #invoicesTable .text-end        { text-align: right; }
  #invoicesTable tbody tr:hover > * { background: transparent !important; }

  /* An invoice number is one token -- letting it wrap turned 20260826-0002
     into two lines and made the column unreadable. */
  #invoicesTable td:first-child,
  #invoicesTable td:first-child a { white-space: nowrap; }
  #invoicesTable td               { vertical-align: top; }

  /* Badges print as ink-heavy blocks; render the status as plain text. */
  #invoicesTable .badge {
    background: transparent !important; color: #000 !important;
    padding: 0 !important; font-weight: 400; font-size: inherit;
  }
}


/* --------------------------------------------- printed list, as a statement */
.doc-masthead  { margin-bottom: .5rem; }
.doc-summary   { margin-left: auto; margin-top: .75rem; width: max-content; }
.doc-summary td { padding: 2pt 6pt; }
.doc-summary .k { color: #555; text-align: right; }
.doc-summary .v { text-align: right; white-space: nowrap; min-width: 5.5rem; }

@media print {
  /* Match .doc: same base size and color, so a printed list and a printed
     invoice are visibly the same stationery. */
  body                    { font-size: 10.5pt; color: #000; }
  .doc-masthead .doc-head { border-bottom: 2px solid #000; padding-bottom: .5rem; }
  .doc-summary            { page-break-inside: avoid; }
  h1, .h3                 { font-size: 14pt; }
}

/* ==========================================================================
   Icon-only buttons — cascade correction

   These rules were losing to the theme's `.btn-success`, which is declared
   later in this file and is equally specific, so it won `--bs-btn-bg` and put
   a solid accent fill back behind the glyph. `.btn-icon.btn-success` then won
   `--bs-btn-color` on specificity, painting the glyph the same accent color.
   The result was an accent-coloured glyph on an accent-coloured fill: a solid
   blue slab with no icon in it.

   Declared last and at `.btn.btn-icon` (two classes) so nothing above can
   outrank them.
   ========================================================================== */

.btn.btn-icon {
  --bs-btn-bg: transparent;
  --bs-btn-border-color: transparent;
  --bs-btn-hover-bg: transparent;
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-bg: transparent;
  --bs-btn-active-border-color: transparent;
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: transparent;
  box-shadow: none;
}

/* Each variant paints its glyph in its own intent color, never white --
   white was only ever readable because there used to be a fill behind it. */
.btn.btn-icon.btn-success,
.btn.btn-icon.btn-outline-success {
  --bs-btn-color: var(--fi-green-dark);
  --bs-btn-hover-color: var(--fi-green);
  --bs-btn-active-color: var(--fi-green);
}
.btn.btn-icon.btn-danger,
.btn.btn-icon.btn-outline-danger {
  --bs-btn-color: var(--cl-danger);
  --bs-btn-hover-color: #7a2222;
  --bs-btn-active-color: #7a2222;
}
.btn.btn-icon.btn-primary,
.btn.btn-icon.btn-outline-primary {
  --bs-btn-color: var(--fi-green-dark);
  --bs-btn-hover-color: var(--fi-green);
  --bs-btn-active-color: var(--fi-green);
}
.btn.btn-icon.btn-secondary,
.btn.btn-icon.btn-outline-secondary {
  --bs-btn-color: var(--cl-ink-soft);
  --bs-btn-hover-color: var(--cl-ink);
  --bs-btn-active-color: var(--cl-ink);
}

.btn.btn-icon:hover  { opacity: .7; }
.btn.btn-icon:active { opacity: .9; }
.btn.btn-icon:disabled,
.btn.btn-icon.disabled { opacity: .35; }
.btn.btn-icon:focus-visible { box-shadow: 0 0 0 .2rem rgb(43 108 176 / 35%); }

.btn.btn-icon .bi { font-size: 1.15rem; }

/* ------------------------------------------------------------ job photos */
.inv-compact .card-photos { border-left-color: #7c6f9a; }
.inv-compact .card-photos .card-header { background: rgb(124 111 154 / 6%); }
.inv-compact .card-photos .card-header .fw-semibold { color: #5f5479; }

.photo-thumb        { width: 9rem; }
.photo-thumb img    { width: 100%; height: 6.5rem; object-fit: cover;
                      border-radius: .5rem; border: 1px solid var(--cl-line); display: block; }
.photo-thumb figcaption { font-size: .72rem; line-height: 1.3; margin-top: .2rem; }

@media print {
  /* Photos are evidence -- they belong on the printed copy, laid out to fit. */
  .photo-thumb        { width: 30%; }
  .photo-thumb img    { height: auto; }
  .js-delete-photo    { display: none !important; }
}

/* ------------------------------------------------------------ placeholders
   Bootstrap's default placeholder gray sits too close to the real text color
   against this warm background -- a hint could be mistaken for a value that
   was already entered, which on a money field is a genuine hazard. Lighter,
   and italic so the difference survives even if the color does not. */
.form-control::placeholder,
.form-select::placeholder,
textarea::placeholder {
  color: #a9a69a !important;
  font-style: italic;
  opacity: 1;              /* Firefox dims placeholders again without this */
}
.form-control:focus::placeholder { color: #c4c1b5 !important; }

/* ==========================================================================
   Landing page
   Same palette as the app so the jump from pitch to product is not jarring.
   Type is deliberately large: this is read on a phone, outdoors, in sunlight.
   ========================================================================== */

body.lp { background: var(--cl-canvas); color: #2b2b28; }

.lp-nav        { border-bottom: 1px solid var(--cl-line); background: var(--cl-surface); }
.lp-brand      { font-family: var(--cl-serif); font-size: 1.15rem; font-weight: 600; color: var(--cl-ink); }
.lp-brand .brand-mark { background: var(--fi-green); color: #fff; }

.lp-hero       { padding: 3.5rem 0 3rem; }
.lp-h1 {
  font-family: var(--cl-serif);
  font-size: clamp(2rem, 5.5vw, 3.1rem);
  font-weight: 600; line-height: 1.12; letter-spacing: -.02em;
  color: var(--cl-ink); margin-bottom: 1rem;
}
.lp-h2 {
  font-family: var(--cl-serif);
  font-size: clamp(1.5rem, 3.4vw, 2rem);
  font-weight: 600; color: var(--cl-ink); margin-bottom: .5rem;
}
.lp-h3         { font-size: 1.02rem; font-weight: 700; color: var(--cl-ink); margin: .6rem 0 .3rem; }
.lp-lead       { font-size: 1.13rem; line-height: 1.6; color: #4a4a45; margin-bottom: 1.5rem; }
.lp-note       { font-size: .9rem; color: var(--cl-ink-soft); }

/* The sample invoice: a drawing of the product, not a screenshot, so it
   cannot go stale the next time the app changes. */
.lp-card {
  background: var(--cl-surface); border: 1px solid var(--cl-line);
  border-left: 3px solid var(--fi-green);
  border-radius: .75rem; padding: 1.1rem 1.25rem;
  box-shadow: 0 1.5rem 3rem rgb(20 20 19 / 7%);
  max-width: 24rem; margin-inline: auto;
}
.lp-card-head  { display: flex; justify-content: space-between; align-items: center;
                 padding-bottom: .6rem; margin-bottom: .4rem; border-bottom: 1px solid var(--cl-line); }
.lp-row        { display: flex; justify-content: space-between; padding: .35rem 0; font-size: .95rem; }
.lp-row-total  { border-top: 1px solid var(--cl-line); margin-top: .3rem; padding-top: .5rem; font-weight: 700; }
.lp-row-paid   { color: var(--cl-success); }
.lp-stamp      { margin-top: .6rem; text-align: center; padding: .4rem;
                 border: 1px solid var(--cl-success); border-radius: .5rem;
                 color: var(--cl-success); font-weight: 600; font-size: .9rem; }

.lp-band       { background: var(--cl-surface); border-block: 1px solid var(--cl-line); padding: 3rem 0; }
.lp-section    { padding: 3.25rem 0; }
.lp-close      { padding: 3.5rem 0; background: var(--cl-surface); border-top: 1px solid var(--cl-line); }

.lp-step p, .lp-feature p { color: #55554e; margin-bottom: 0; line-height: 1.55; }
.lp-num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; border-radius: 50%;
  background: var(--fi-green); color: #fff;
  font-weight: 700; font-size: .95rem;
}
.lp-feature i  { font-size: 1.5rem; color: var(--fi-green); }

.lp-foot       { border-top: 1px solid var(--cl-line); color: var(--cl-ink-soft); font-size: .9rem; }
.lp-foot a     { color: var(--fi-green-dark); }

@media (max-width: 576px) {
  .lp-hero    { padding: 2.25rem 0 2rem; }
  .lp-lead    { font-size: 1.05rem; }
  .lp-hero .btn-lg { width: 100%; }   /* thumb-sized targets on a phone */
}

/* landing: trade line, and the price panel ------------------------------- */
.lp-eyebrow {
  font-size: .8rem; text-transform: uppercase; letter-spacing: .14em;
  color: var(--fi-green-dark); font-weight: 700; margin-bottom: .5rem;
}
.lp-price {
  background: var(--cl-canvas); border: 1px solid var(--cl-line);
  border-radius: .75rem; padding: 1.4rem 1.5rem; height: 100%;
}
.lp-price-soft   { background: transparent; border-style: dashed; }
.lp-price-tag {
  font-family: var(--cl-serif); font-size: 1.9rem; font-weight: 600;
  color: var(--cl-ink); line-height: 1; margin-bottom: .5rem;
}
.lp-price-soft .lp-price-tag { color: var(--cl-ink-soft); font-size: 1.4rem; }
.lp-list         { margin: 0; padding-left: 1.1rem; color: #55554e; }
.lp-list li      { margin-bottom: .25rem; }

/* ------------------------------------------------------------------ to do */
.todo-item.todo-done .todo-title { text-decoration: line-through; color: var(--cl-ink-soft); }
.todo-item.todo-done             { opacity: .72; }

/* Platform tools: right-aligned, set apart from the day-to-day links. */
.app-navbar .nav-admin { --bs-nav-link-padding-x: .6rem; }
@media (min-width: 992px) {
  .app-navbar .nav-admin { padding-left: .75rem; border-left: 1px solid rgba(255, 255, 255, .18); }
  .app-navbar .nav-admin .nav-link { opacity: .82; font-size: .9375rem; }
  .app-navbar .nav-admin .nav-link:hover,
  .app-navbar .nav-admin .nav-link.active { opacity: 1; }
}

/* A quick-add button's taxable marker -- small enough not to compete with the name. */
.svc-tax-dot { opacity: .65; font-size: .75em; vertical-align: .15em; }

/* Discussion thread on a to-do item. */
.todo-thread .msg        { margin-bottom: .6rem; font-size: .9375rem; }
.todo-thread .msg-who    { font-size: .75rem; font-weight: 600; letter-spacing: .02em; margin-bottom: .15rem; }
.todo-thread .msg-body   { padding: .5rem .7rem; border-radius: .5rem; border: 1px solid var(--cl-line);
                           background: var(--cl-surface); white-space: normal; overflow-wrap: anywhere; }
.todo-thread .msg-user .msg-body      { background: var(--fi-green-soft); border-color: transparent; }
.todo-thread .msg-assistant .msg-who  { color: var(--fi-green-dark); }
.todo-thread textarea    { resize: vertical; }

/* --------------------------------------------------------- help bubble */
.help-dock {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 1045;          /* above content, below Bootstrap's modal backdrop */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .5rem;
}

.help-bubble {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--fi-green);
  color: #fff;
  font-size: 1.35rem;
  text-decoration: none;
  box-shadow: 0 .35rem 1rem rgba(20, 20, 19, .28);
  transition: transform .12s ease, background .12s ease;
}
.help-bubble:hover,
.help-bubble:focus-visible { background: var(--fi-green-dark); color: #fff; transform: translateY(-1px); }
.help-bubble i { font-size: 1.25rem; line-height: 1; }

/* Clear of the thumb on a phone, and of the home indicator on iOS. */
@media (max-width: 576px) {
  .help-dock   { right: 1rem; bottom: calc(1rem + env(safe-area-inset-bottom, 0px)); }
  .help-bubble { width: 2.75rem; height: 2.75rem; }
}

.help-panel {
  width: min(23rem, calc(100vw - 2rem));
  max-height: min(30rem, calc(100vh - 6rem));
  display: flex;
  flex-direction: column;
  background: var(--cl-surface);
  border: 1px solid var(--cl-line);
  border-radius: .85rem;
  box-shadow: 0 .75rem 2rem rgba(20, 20, 19, .22);
  overflow: hidden;
}
.help-panel[hidden] { display: none; }

.help-panel-head {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .5rem .75rem;
  border-bottom: 1px solid var(--cl-line);
  background: var(--cl-canvas);
}

.help-panel-body { padding: .75rem; overflow-y: auto; flex: 1 1 auto; }

.help-panel-foot {
  display: flex;
  gap: .4rem;
  align-items: flex-end;
  padding: .6rem .75rem;
  border-top: 1px solid var(--cl-line);
}
.help-panel-foot textarea { resize: none; }

/* Shared with the to-do thread. */
.help-thread .msg       { margin-bottom: .6rem; font-size: .9375rem; }
.help-thread .msg:last-child { margin-bottom: 0; }
.help-thread .msg-who   { font-size: .75rem; font-weight: 600; margin-bottom: .15rem; }
.help-thread .msg-body  { padding: .45rem .65rem; border-radius: .5rem; border: 1px solid var(--cl-line);
                          background: var(--cl-surface); overflow-wrap: anywhere; }
.help-thread .msg-user .msg-body     { background: var(--fi-green-soft); border-color: transparent; }
.help-thread .msg-assistant .msg-who { color: var(--fi-green-dark); }

@media print { .help-dock { display: none !important; } }

/* ------------------------------------------------------------- schedule */
.sched            { table-layout: fixed; min-width: 60rem; }
.sched th,
.sched td         { vertical-align: top; border-color: var(--cl-line); }
.sched .sched-who { width: 10rem; position: sticky; left: 0; background: var(--cl-surface); z-index: 1; }
.sched thead .sched-who { background: var(--bs-table-bg, #f8f9fa); }
.sched-day        { width: 12%; text-align: center; }
.sched-day.is-today,
.sched-cell.is-today { background: var(--fi-green-soft); }
.sched-cell       { padding: .35rem; }
.sched-unassigned .sched-who { color: var(--cl-danger); }

.jobcard {
  border: 1px solid var(--cl-line);
  border-left: 3px solid var(--fi-green);
  border-radius: .4rem;
  padding: .35rem .45rem;
  margin-bottom: .35rem;
  background: var(--cl-surface);
  font-size: .8125rem;
  line-height: 1.35;
}
.jobcard:last-child { margin-bottom: 0; }
.jobcard.is-done    { border-left-color: var(--cl-success); opacity: .7; }
.jobcard.is-done .jobcard-service { text-decoration: line-through; }

.jobcard-top     { display: flex; gap: .35rem; align-items: baseline; }
.jobcard-service { font-weight: 600; overflow-wrap: anywhere; }
.jobcard-price   { margin-left: auto; white-space: nowrap; color: var(--cl-ink-soft); }
.jobcard-cust    { overflow-wrap: anywhere; }
.jobcard-addr    { color: var(--cl-ink-soft); font-size: .75rem; overflow-wrap: anywhere; }
.jobcard-acts    { display: flex; flex-wrap: wrap; gap: .2rem; margin-top: .3rem; align-items: center; }
.jobcard-acts .form-select,
.jobcard-acts .form-control { font-size: .7rem; padding: .1rem .3rem; height: auto; }

@media print {
  .sched .jobcard-acts { display: none; }
  .sched               { min-width: 0; font-size: 8pt; }
}

/* The "+" that schedules work into one person's day. Quiet until hovered. */
.jobcard-add {
  display: block;
  width: 100%;
  padding: .1rem;
  border: 1px dashed var(--cl-line);
  border-radius: .3rem;
  background: transparent;
  color: var(--cl-ink-soft);
  font-size: .75rem;
  line-height: 1.2;
  opacity: 0;
  transition: opacity .12s ease;
}
.sched-cell:hover .jobcard-add,
.jobcard-add:focus-visible { opacity: 1; }
.jobcard-add:hover { border-color: var(--fi-green); color: var(--fi-green-dark); }

/* Drag a job to another day or another person. */
.jobcard.is-draggable { cursor: grab; }
.jobcard.is-draggable:active { cursor: grabbing; }
.jobcard.is-dragging  { opacity: .4; }
.jobcard.is-saving    { border-left-style: dashed; }
.sched-cell.is-dropzone {
  background: var(--fi-green-soft);
  outline: 2px dashed var(--fi-green);
  outline-offset: -3px;
}
