:root {
  --paper: #F6F4ED; --card: #FFFFFF; --ink: #26241F; --muted: #6B6656;
  --band: #FFEFA8; --band-soft: #FFF8D6; --rule: #C00000;
  --accent: #E0622B; --accent-deep: #B44814; --blue: #1F3864;
  --grid: #DAD4C4; --grid-soft: #EFEBDD;
  --tint: #FFF6EF;  /* soft highlight fill (current month, active drop zone) */
  --display: 'Literata', Georgia, serif;
  --ui: 'IBM Plex Sans', system-ui, sans-serif;
}
/* dark palette: same warm scheme, inverted. Applies when the OS asks for dark (unless the
   user pinned light) OR when the user pinned dark — the switch just sets data-theme. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #191713; --card: #211E19; --ink: #EAE6DB; --muted: #A69F8D;
    --band: #2E2919; --band-soft: #282419; --rule: #E0685C;
    --accent: #E0622B; --accent-deep: #B44814; --blue: #8FAADC;
    --grid: #3B372E; --grid-soft: #2C2921; --tint: #3A3226;
  }
  /* --rule doubles as a fill: light text fails on the lighter dark-mode red, so flip it */
  :root:not([data-theme="light"]) .badge,
  :root:not([data-theme="light"]) button.confirming { color: #1A1512; }
  :root:not([data-theme="light"]) .card { box-shadow: 0 1px 2px rgba(0, 0, 0, .4); }
  /* accent-deep on a dark card fails AA for the export warning; band-soft vanishes
     against the card, hiding the pending-row cue at night (UI audit 2026-08-09) */
  :root:not([data-theme="light"]) .scope-line.warn { color: var(--accent); }
  :root:not([data-theme="light"]) tr.pending-row { background: var(--tint); }
  /* multiply ink vanishes on dark photos/cards — the stamp prints plainly at night */
  :root:not([data-theme="light"]) .stamp { mix-blend-mode: normal; opacity: .95; }
}
:root[data-theme="dark"] {
  --paper: #191713; --card: #211E19; --ink: #EAE6DB; --muted: #A69F8D;
  --band: #2E2919; --band-soft: #282419; --rule: #E0685C;
  --accent: #E0622B; --accent-deep: #B44814; --blue: #8FAADC;
  --grid: #3B372E; --grid-soft: #2C2921; --tint: #3A3226;
}
:root[data-theme="dark"] .badge,
:root[data-theme="dark"] button.confirming { color: #1A1512; }
:root[data-theme="dark"] .card { box-shadow: 0 1px 2px rgba(0, 0, 0, .4); }
:root[data-theme="dark"] .scope-line.warn { color: var(--accent); }
:root[data-theme="dark"] tr.pending-row { background: var(--tint); }
:root[data-theme="dark"] .stamp { mix-blend-mode: normal; opacity: .95; }
* { box-sizing: border-box; }
/* the hidden attribute must ALWAYS win: an author display on a class (button.secondary,
   .scan-ctl…) silently defeated [hidden] — the scanner's retry button haunted every state
   and the torch showed with no camera (live-scan review 2026-08-10) */
[hidden] { display: none !important; }
body { margin: 0; background: var(--paper); color: var(--ink);
       font-family: var(--ui); font-size: 1rem; line-height: 1.5; }

/* masthead — a fragment of the printed form: red rule over the yellow title band,
   with the classic thin companion hairline of printed stationery */
.masthead { border-top: 4px solid var(--rule); background: var(--band);
            border-bottom: 1px solid var(--grid); position: relative;
            padding-top: env(safe-area-inset-top); }  /* installed-PWA notch */
.masthead::after { content: ""; position: absolute; top: 2px; left: 0; right: 0;
                   height: 1px; background: var(--rule); opacity: .45; }
.band { max-width: 60rem; margin: 0 auto; padding: .65rem 1.25rem;
        display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.brand { font-family: var(--display); font-weight: 700; font-size: 1.15rem;
         letter-spacing: .42em; text-decoration: none; color: var(--ink); white-space: nowrap; }
.who { display: flex; align-items: center; gap: .9rem; font-size: .85rem; flex-wrap: wrap; }
.who-name { font-weight: 600; }
.company-switch { margin: 0; }
.company-switch select { font-size: .85rem; padding: .2rem .4rem; border: 1px solid var(--ink);
                         border-radius: 6px; background: var(--card); font-weight: 600; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.who a { color: var(--blue); }
.who form { margin: 0; }
.ghost { background: none; border: 1px solid var(--ink); color: var(--ink);
         border-radius: 6px; padding: .25rem .7rem; font: inherit; font-size: .85rem; cursor: pointer; }
.ghost:hover { background: var(--ink); color: var(--band); }
.account { position: relative; }
.account summary { list-style: none; cursor: pointer; font-weight: 600; font-size: .85rem;
                   padding: .3rem .6rem; border: 1px solid var(--ink); border-radius: 6px;
                   max-width: 11rem; overflow: hidden; text-overflow: ellipsis;
                   white-space: nowrap; }
.account summary::-webkit-details-marker { display: none; }
.account summary::after { content: " ▾"; }
.account[open] summary { background: var(--ink); color: var(--band); }
.account-menu { position: absolute; right: 0; top: calc(100% + .4rem); z-index: 50;
                background: var(--card); border: 1px solid var(--grid); border-radius: 6px;
                box-shadow: 0 4px 12px rgba(20,18,14,.2); padding: .6rem; min-width: 12rem;
                display: flex; flex-direction: column; gap: .55rem; align-items: stretch; }
.account-menu .ghost { width: 100%; }
.account-menu form { margin: 0; }

/* bottom navigation: phones only — the top tabs hide and this takes the thumb zone */
.bottomnav { display: none; }
@media (max-width: 540px) {
  .navwrap { display: none; }
  main { padding-bottom: 5.5rem; }
  .site-footer { padding-bottom: calc(4.6rem + env(safe-area-inset-bottom)); }
  .bottomnav { display: flex; position: fixed; bottom: 0; left: 0; right: 0; z-index: 40;
               background: var(--card); border-top: 1px solid var(--grid);
               padding: .3rem .25rem calc(.3rem + env(safe-area-inset-bottom)); }
  .bottomnav a { flex: 1; display: flex; flex-direction: column; align-items: center;
                 gap: .1rem; color: var(--muted); text-decoration: none; font-size: .72rem;
                 padding: .3rem 0 .2rem; position: relative;
                 border-top: 2px solid transparent; }
  .bottomnav a.active { color: var(--ink); font-weight: 600;
                        border-top-color: var(--accent);
                        background: var(--tint); border-radius: 0 0 8px 8px; }
  .nav-count { position: absolute; top: .05rem; left: calc(50% + .5rem);
               background: #C9A227; color: #26241F; border-radius: 999px;
               font-size: .65rem; font-weight: 700; line-height: 1.2;
               padding: .05rem .3rem; min-width: 1rem; text-align: center; }
}

.navwrap nav { max-width: 60rem; margin: 0 auto; padding: 0 1.25rem; display: flex; gap: .25rem; }
.navwrap nav a { color: var(--muted); text-decoration: none; padding: .55rem .8rem; font-size: .95rem;
        border-bottom: 3px solid transparent; margin-bottom: -1px; }
.navwrap nav a:hover { color: var(--ink); }
.navwrap nav a.active { color: var(--ink); font-weight: 600; border-bottom-color: var(--accent); }
.navwrap { background: var(--card); border-bottom: 1px solid var(--grid); }
.subnav { display: flex; flex-wrap: wrap; gap: .25rem; margin: 0 0 1rem; border-bottom: 1px solid var(--grid); }
.subnav a { color: var(--muted); text-decoration: none; padding: .4rem .7rem; font-size: .9rem;
            border-bottom: 3px solid transparent; margin-bottom: -1px; }
.subnav a:hover { color: var(--ink); }
.subnav a.active { color: var(--ink); font-weight: 600; border-bottom-color: var(--accent); }
.admin-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: 1rem; }
.kpi-label { margin: 0; color: var(--muted); font-size: .85rem; }
.kpi { margin: .1rem 0 0; font-size: 1.7rem; font-weight: 700; font-family: var(--display); }
.kpi small { font-size: .8rem; font-weight: 400; color: var(--muted); }
.bar { background: var(--grid-soft); border-radius: 3px; height: .55rem; overflow: hidden; }
.bar span { display: block; height: 100%; }
.modal { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center;
         justify-content: center; padding: 1rem; background: rgba(20, 18, 14, .5); }
.modal[hidden] { display: none; }   /* author display:flex would else defeat the hidden attr */
.modal-card { width: 100%; max-width: 26rem; }

main { max-width: 60rem; margin: 0 auto; padding: 1.5rem 1.25rem 3rem; }
.site-footer { max-width: 60rem; margin: 0 auto; padding: 1rem 1.25rem 1.6rem;
               color: var(--muted); font-size: .8rem; text-align: center;
               border-top: 1px solid var(--grid); }
@media (prefers-reduced-motion: no-preference) {
  main { animation: rise .18s ease-out; }
  @keyframes rise { from { opacity: 0; transform: translateY(5px); } }
  button:not(:disabled):active { transform: scale(.985); }   /* tactile press */
  .nav-count { animation: pop .35s ease-out; }
  @keyframes pop { 0% { transform: scale(.4); } 65% { transform: scale(1.18); }
                   100% { transform: scale(1); } }
}
/* the card stack settles in with a short stagger — phones only, capped at nine rows */
@media (prefers-reduced-motion: no-preference) and (max-width: 540px) {
  table.receipts tr:not(.totals) { animation: cardrise .26s ease-out backwards; }
  table.receipts tr:nth-child(3) { animation-delay: .04s; }
  table.receipts tr:nth-child(4) { animation-delay: .08s; }
  table.receipts tr:nth-child(5) { animation-delay: .12s; }
  table.receipts tr:nth-child(6) { animation-delay: .16s; }
  table.receipts tr:nth-child(7) { animation-delay: .20s; }
  table.receipts tr:nth-child(8) { animation-delay: .24s; }
  table.receipts tr:nth-child(n+9) { animation-delay: .27s; }
  @keyframes cardrise { from { opacity: 0; transform: translateY(8px); } }
}

h2 { font-family: var(--display); font-weight: 600; font-size: 1.45rem; margin: .2rem 0 1rem; }
h2 small { font-family: var(--ui); font-size: .9rem; color: var(--muted); font-weight: 400; }
a { color: var(--blue); }

.card { background: var(--card); border: 1px solid var(--grid); border-radius: 6px;
        padding: 1.25rem; box-shadow: 0 1px 2px rgba(38,36,31,.05); }
.empty-state { text-align: center; color: var(--muted); padding: 2.5rem 1.5rem; }
.empty-state svg { display: block; margin: 0 auto .8rem; color: var(--grid); }
/* the stamp — a confirmed receipt is stamped like every Greek document: composed within
   the photo, the faint tilt of a careful hand rather than a flourish */
.photo-wrap { position: relative; display: inline-block; max-width: 100%; }
.stamp { position: absolute; top: .9rem; right: .8rem; transform: rotate(-3deg);
         display: inline-flex; flex-direction: column; align-items: center; gap: .05rem;
         color: var(--rule); border: 3px double var(--rule); border-radius: 6px;
         padding: .28rem .75rem; font-weight: 700; font-size: .78rem;
         letter-spacing: .18em; text-transform: uppercase; line-height: 1.25;
         pointer-events: none; opacity: .85; mix-blend-mode: multiply;
         background: rgba(255,255,255,.06); }
.stamp small { font-size: .66rem; font-weight: 600; letter-spacing: .12em; }
.card.narrow { max-width: 26rem; }
.split { display: grid; gap: 1.25rem; align-items: start; }
@media (min-width: 760px) { .split { grid-template-columns: 1fr 1fr; } }

.error { color: var(--rule); font-weight: 600; }
/* one composed voice for every field: same border, radius, padding, focus everywhere */
input:not([type="radio"]):not([type="checkbox"]):not([type="file"]),
select, textarea {
  font: inherit; color: var(--ink); background: var(--card);
  border: 1px solid var(--grid); border-radius: 6px; padding: .5rem .65rem; }
svg.icon { vertical-align: -0.125em; flex-shrink: 0; }
/* what an export will actually contain — quiet until it has a warning to make */
.scope-line { font-size: .88rem; color: var(--muted); margin: .5rem 0 0; }
.scope-line.warn { color: var(--accent-deep); font-weight: 600; }
/* the QR's own address: present but quiet, and it must never widen the page on a phone */
.qr-src { font-size: .8rem; color: var(--muted); margin: .4rem 0 0; line-height: 1.45; }
.qr-src a { overflow-wrap: anywhere; }
.notice { background: var(--band-soft); border: 1px solid var(--grid); border-left: 4px solid var(--accent);
          padding: .6rem .8rem; border-radius: 6px; }
.notice-dup { border-left-color: #C9A227; }
.notice-tip { border-left-color: var(--muted); font-size: .9rem; }
.notice-qr { border-left-color: #2E7D32; }        /* auto-read from QR — verified */
.notice-manual { border-left-color: var(--blue); } /* hand-typed — no QR, no AI */
.dup-list { margin: .4rem 0 .3rem; padding-left: 1.1rem; }
.dup-list li { margin: .15rem 0; }
.dup-why { color: var(--muted); font-size: .85rem; }

input, select { font: inherit; padding: .5rem .6rem; border: 1px solid var(--grid);
                border-radius: 6px; background: var(--card); color: var(--ink); max-width: 100%; }
/* Fields whose stored value is genuinely long. Only max-width was set, so these kept the
   browser's ~20-character default box and truncated the very value the user is asked to
   proofread — «ΕΛΛΗΝΙΚΕΣ ΥΠΕΡΑΓΟΡΕΣ ΣΚΛΑΒΕΝΙΤΗΣ Α.Ε.Ε., Αστρος Κυνουρίας» showing as
   «ΕΛΛΗΝΙΚΕΣ ΥΠΕΡΑΓΟΡΕΣ» — inside a card with room to spare (UX audit 2026-07-19). The
   short ones (date, amount, ΑΦΜ, the admin range filters) keep their own widths. */
#merchant, #recipient, .card input[name="name"], .card input[name="display_name"],
.card input[name="address"], .card input[name="iban"], .card input[name="email"],
.card input[name="doy"], .card input[name="username"],
.card input[type="password"] { width: 100%; }
input:focus-visible, select:focus-visible, button:focus-visible, a:focus-visible {
  outline: 3px solid var(--accent); outline-offset: 2px; }
form p { margin: .65rem 0; }
label { font-size: .92rem; }

/* --accent-deep (not --accent) as the resting fill: white text on it clears WCAG AA (~5.4:1) */
button { font: inherit; font-weight: 600; background: var(--accent-deep); color: #fff; border: 0;
         border-radius: 6px; padding: .55rem 1.15rem; cursor: pointer; }
button:not(:disabled):hover { background: #96380F; }
button:disabled { opacity: .55; cursor: default; }
button.secondary, a.secondary { background: var(--card); color: var(--ink);
         border: 1px solid var(--grid); font-weight: 400; display: inline-block;
         text-decoration: none; border-radius: 6px; padding: .55rem 1.15rem; }
button.secondary:not(:disabled):hover, a.secondary:hover { background: var(--grid-soft); }
button.confirming { background: var(--rule); color: #fff; border-color: var(--rule); font-weight: 600; }
input[type="radio"], input[type="checkbox"] { accent-color: var(--accent-deep); }
hr { border: 0; border-top: 1px solid var(--grid); }

/* ledger table — ruled like the form grid, amounts in tabular figures */
/* the ledger scrolls inside itself on a phone instead of pushing the page sideways */
.table-wrap { overflow-x: auto; }
tr[data-href] { cursor: pointer; }
.phone-only { display: none; }
.drop-actions { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; }
.filter-bar { display: flex; flex-wrap: wrap; gap: .6rem; align-items: end; }
.ts { white-space: nowrap; }
/* phones: lead with the scanner — the whole app's front door. Full-width, thumb-sized,
   scan first, camera second, the desktop file-picker demoted (owner request 2026-07-17) */
@media (hover: none) and (pointer: coarse) {
  .desktop-only { display: none; }
  .phone-only { display: block; }
  .drop { padding: 1.1rem .9rem; }
  .drop > svg { display: none; }
  .drop-actions { flex-direction: column; align-items: stretch; }
  .drop-actions button { width: 100%; padding: .8rem 1rem; font-size: 1.05rem; }
  .drop-actions .pick-btn { background: none; border: 1px solid var(--grid);
                            color: var(--ink); font-size: .95rem; padding: .6rem 1rem; }
}
/* compact chrome on narrow screens: the header+nav used to eat ~40% of the viewport */
@media (max-width: 540px) {
  .band { padding: .45rem .9rem; gap: .5rem .8rem; }
  .brand { font-size: .95rem; letter-spacing: .28em; }
  /* one-row masthead: brand | company | account — the chrome that ate a third of the
     first screen (phone-first shell 2026-08-09) */
  .band { flex-wrap: nowrap; gap: .5rem; padding: .5rem .8rem; }
  .brand { font-size: .95rem; letter-spacing: .28em; }
  .who { font-size: .8rem; gap: .4rem; flex: 1; min-width: 0;
         flex-wrap: nowrap; justify-content: flex-end; }
  .company-switch { flex: 1; min-width: 0; }
  .company-switch select { width: 100%; }
  .account summary { max-width: 6.5rem; }
  .navwrap nav { padding: 0 .5rem; gap: 0; overflow-x: auto; flex-wrap: nowrap; }
  .navwrap nav a { white-space: nowrap; padding: .5rem .6rem; font-size: .9rem; }
  main { padding: 1rem .8rem 2.5rem; }
  h2 { font-size: 1.25rem; margin: .1rem 0 .7rem; }
  .monthbar { gap: .4rem; }
  /* the ledger becomes CARDS on the phone (phone-first shell 2026-08-09, stage 2): the
     SAME table DOM re-laid by grid — merchant+amount lead, date/category/status/delete
     sit in a second row — so the row-tap, badge, delete and search selectors all keep
     working and nothing can clip off-screen */
  table.receipts { display: block; border: 0; background: none; }
  table.receipts tbody { display: block; }
  table.receipts tr:not(.totals) { display: grid;
    grid-template-columns: 1fr auto auto auto; align-items: center;
    column-gap: .55rem; row-gap: .2rem;
    background: var(--card); border: 1px solid var(--grid); border-radius: 6px;
    padding: .6rem .75rem; margin-bottom: .5rem; }
  table.receipts tr:first-child { display: none; }        /* the header row (after the
                                                             grid rule, same specificity) */
  table.receipts tr[hidden] { display: none; }            /* the search filter must win */
  table.receipts td { display: block; border: 0; padding: 0; }
  table.receipts td:nth-child(1) { grid-column: 1; grid-row: 2;
    color: var(--muted); font-size: .82rem; }
  table.receipts td:nth-child(2) { grid-column: 1 / 4; grid-row: 1;
    font-weight: 600; overflow-wrap: anywhere; }
  table.receipts td:nth-child(3) { grid-column: 4; grid-row: 1;
    font-size: 1.08rem; font-weight: 600; white-space: nowrap; }
  table.receipts td:nth-child(4) { grid-column: 2; grid-row: 2; }  /* category returns */
  table.receipts td:nth-child(5) { grid-column: 3; grid-row: 2; justify-self: end; }
  table.receipts td:nth-child(6) { grid-column: 4; grid-row: 2; justify-self: end; }
  table.receipts .badge { font-size: .75rem; }
  /* the totals band closes the card stack as a summary card of its own — stacked and
     left-aligned so every figure reads without scrolling (UX audit 2026-07-19) */
  table.receipts tr.totals { display: block;
    background: var(--grid-soft); border: 1px solid var(--grid); border-radius: 6px;
    border-top: 2px solid var(--ink); padding: .35rem 0; }
  table.receipts tr.totals th {
    display: block; text-align: left; border-bottom: 0; padding: .15rem .75rem;
    background: none; border-top: 0; }
  /* admin tables fold like the ledger: the hidden columns stay reachable as filters and on
     desktop; timestamps may wrap so ΕΝΕΡΓΕΙΑ/ΛΕΠΤΟΜΕΡΕΙΕΣ get room (UI audit 2026-08-09) */
  table.admin-receipts td:nth-child(2), table.admin-receipts th:nth-child(2),
  table.admin-receipts td:nth-child(3), table.admin-receipts th:nth-child(3),
  table.admin-receipts td:nth-child(6), table.admin-receipts th:nth-child(6),
  table.users-table td:nth-child(2), table.users-table th:nth-child(2),
  table.users-table td:nth-child(6), table.users-table th:nth-child(6),
  table.users-table td:nth-child(7), table.users-table th:nth-child(7) { display: none; }
  .ts { white-space: normal; }
  /* the 9-control filter card halves in height as a two-column grid */
  .filter-bar { display: grid; grid-template-columns: 1fr 1fr; }
  .filter-bar label, .filter-bar select, .filter-bar input { width: 100%; }
}
/* phones: the ledger must fit without a side-scroll hiding the amount/actions, and every
   control must be a real finger target (UX audit 2026-07-16) */
@media (pointer: coarse) {
  td, th { padding: .45rem .4rem; }
  td:nth-child(2) { overflow-wrap: anywhere; }             /* long merchant names wrap */
  .badge, .tag { padding: .35rem .6rem; }
  /* the pen duplicates the row-tap (tr[data-href] exists precisely for coarse pointers)
     and its touch padding was the last thing pushing the delete column off a 412px
     screen — the row itself opens the receipt */
  td a[aria-label="Επεξεργασία"] { display: none; }
  /* the daily action leads: scanning is why the app exists (phone-first shell 2026-08-09) */
  #scan:not([hidden]) { order: -1; font-size: 1.1rem; padding: .95rem 1rem; }
  button.del { padding: .5rem .7rem; }
  /* min-height/width are NO-OPS on inline anchors — flex them so the floor applies */
  .monthbar a { display: inline-flex; align-items: center; justify-content: center; }
  .monthbar button, .monthbar a, .cal-head button, .cal-toggle {
    min-height: 2.6rem; min-width: 2.6rem; }
  .company-switch select { font-size: 1rem; min-height: 2.6rem; }  /* <16px focus-zooms iOS */
  .ghost { padding: .5rem .8rem; }
  .who a { display: inline-block; padding: .4rem 0; }      /* «Αλλαγή κωδικού» finger room */
}
table { border-collapse: collapse; width: 100%; background: var(--card);
        border: 1px solid var(--grid); font-size: .95rem; }
th { background: var(--grid-soft); font-size: .78rem; text-transform: uppercase;
     letter-spacing: .06em; color: var(--muted); font-weight: 600; }
td, th { border-bottom: 1px solid var(--grid); padding: .5rem .75rem; text-align: left; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* money reads in the ledger's own voice: the display serif, like the printed form */
table.receipts td.num { font-family: var(--display); font-size: 1.02em; }
tr.pending-row { background: var(--band-soft); }
tr.totals td, tr.totals th { background: var(--grid-soft); font-weight: 600; border-top: 2px solid var(--ink); }

.badge { background: var(--rule); color: #fff; border-radius: 3px; padding: .12rem .5rem;
         font-size: .8rem; font-weight: 600; text-decoration: none; }
/* pending is routine attention, not an alarm — amber, matching .notice-dup; --rule red is
   reserved for errors and destructive states (color taxonomy, UI audit 2026-08-09) */
.badge.pending { background: #C9A227; color: #26241F; }
.tag { border: 1px solid var(--grid); border-radius: 999px; padding: .05rem .6rem;
       font-size: .8rem; color: var(--muted); white-space: nowrap; }

.drop { border: 2px dashed var(--grid); border-radius: 8px; background: var(--card);
        padding: 3rem 1.5rem; text-align: center; color: var(--muted);
        transition: border-color .2s, background .2s; }
.drop.over { border-color: var(--accent); background: var(--tint); }
.drop svg { display: block; margin: 0 auto .75rem; color: var(--grid);
            transition: transform .25s ease, color .25s ease; }
.drop.over svg { transform: scale(1.08) rotate(-2deg); color: var(--accent); }
::selection { background: var(--band); color: var(--ink); }  /* marker on the form */
.drop.over svg { color: var(--accent); }

/* background upload tray — each receipt processes on its own, work continues */
.upload-queue { list-style: none; padding: 0; margin: 1.25rem 0 0;
                display: flex; flex-direction: column; gap: .5rem; }
.q-item { display: flex; align-items: center; flex-wrap: wrap; gap: .4rem .75rem; background: var(--card);
          border: 1px solid var(--grid); border-left: 4px solid var(--grid);
          border-radius: 6px; padding: .55rem .8rem; font-size: .92rem; }
.q-item[data-state="processing"] { border-left-color: var(--accent); }
.q-item[data-state="done"] { border-left-color: #2E7D32; }
.q-item[data-state="dup"] { border-left-color: var(--blue); }
.q-item[data-state="error"] { border-left-color: var(--rule); }
fieldset.plain { border: 0; padding: 0; margin: .65rem 0; }
fieldset.plain legend { font-size: .92rem; padding: 0; margin-bottom: .25rem; }
.q-thumb { width: 2.2rem; height: 2.2rem; object-fit: cover; border-radius: 3px;
           flex-shrink: 0; border: 1px solid var(--grid); }
.q-name { font-weight: 600; max-width: 14rem; overflow: hidden;
          text-overflow: ellipsis; white-space: nowrap; }
.q-status { color: var(--muted); }
.q-item[data-state="error"] .q-status { color: var(--rule); }
.q-action { margin-left: auto; }
.q-action a { font-weight: 600; text-decoration: none; }
.q-action button { padding: .3rem .7rem; font-size: .85rem; }
@media (prefers-reduced-motion: no-preference) {
  .q-item[data-state="processing"] .q-status::after {
    content: ""; animation: qdots 1.2s steps(4, end) infinite; }
  @keyframes qdots { 0% { content: ""; } 25% { content: "·"; }
                     50% { content: "··"; } 75% { content: "···"; } }
}

.cat { display: flex; gap: 1rem; margin: .8rem 0; }
.cat label { flex: 1; border: 2px solid var(--grid); border-radius: 8px; padding: .9rem;
             text-align: center; cursor: pointer; font-weight: 600; letter-spacing: .04em; }
.cat input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.cat input:checked + span { color: #fff; font-weight: 700; }
.cat label:has(input:checked) { border-color: var(--accent-deep); background: var(--accent-deep); }
.progress { color: var(--accent-deep); font-weight: 600; margin: -.3rem 0 1rem; }
.cat label:has(input:focus-visible) { outline: 3px dashed var(--accent); outline-offset: 2px; }

.camera-only { display: none; }
/* :not([hidden]) so this author rule does not override the `hidden` attribute — #scan is gated
   by JS (removes hidden only when getUserMedia exists); without the guard the button showed on
   every touch device even where the live scanner cannot run, a dead button (review 2026-08-08) */
@media (hover: none) and (pointer: coarse) {
  .camera-only:not([hidden]) { display: inline-block; } }

/* in-page QR scanner (spec: docs/superpowers/specs/2026-07-07-mobile-qr-scanner-design.md) */
/* explicit sides BEFORE the `inset` shorthand: an old WebView that ignores `inset` still gets
   a full-screen overlay from the longhand instead of a collapsed box */
.scanner { position: fixed; top: 0; right: 0; bottom: 0; left: 0; inset: 0;
           background: #000; z-index: 60; }
.scanner[hidden] { display: none; }
.scanner video { position: absolute; top: 0; left: 0; inset: 0;
                 width: 100%; height: 100%; object-fit: cover; }
.scan-frame { position: absolute; left: 50%; top: 42%; transform: translate(-50%, -50%);
              width: min(62vw, 20rem); aspect-ratio: 1 / 1; border: 3px solid rgba(255,255,255,.85);
              border-radius: 14px; box-shadow: 0 0 0 100vmax rgba(0,0,0,.35); pointer-events: none; }
.scanner.flash .scan-frame { border-color: #2E9E44; box-shadow: 0 0 0 100vmax rgba(20,80,35,.35); }
.scan-top { position: absolute; top: max(1rem, env(safe-area-inset-top)); left: 1rem; right: 1rem;
            display: flex; align-items: center; gap: .5rem; z-index: 2; }
/* scrims sit over a LIVE camera frame that can be a bright white receipt; .6 opacity + a text
   shadow keep white text/icons legible against the worst-case background (a11y review 2026-08-08) */
.scan-counter { color: #fff; font-weight: 600; background: rgba(0,0,0,.65); border-radius: 999px;
                padding: .25rem .7rem; margin-right: auto; font-variant-numeric: tabular-nums;
                text-shadow: 0 1px 3px #000; }
.scan-ctl { background: rgba(0,0,0,.65); color: #fff; border: 1px solid rgba(255,255,255,.6);
            border-radius: 999px; width: 2.6rem; height: 2.6rem; font-size: 1.1rem;
            touch-action: manipulation;
            /* the global button padding squeezed the icon out of the 2.6rem circle —
               flex-center it in a zero-padding box (owner report 2026-08-10) */
            display: inline-flex; align-items: center; justify-content: center; padding: 0; }
.scan-ctl.on { background: var(--accent-deep); border-color: var(--accent-deep); }
.scan-step { position: absolute; top: max(4rem, calc(env(safe-area-inset-top) + 3rem));
             left: 0; right: 0; text-align: center; color: #fff; text-shadow: 0 1px 3px #000;
             font-weight: 700; letter-spacing: .05em; z-index: 2; }
/* the status line carries the flow's instructions AND its error states, and in step 2 there is
   no vignette behind it — over a white receipt filling the frame, bare white text approaches
   1:1. A pill scrim keeps it legible in the bright/outdoor light employees scan in (~7:1). */
.scan-status { position: absolute; bottom: 8.5rem; left: 50%; transform: translateX(-50%);
               max-width: calc(100% - 2rem); text-align: center; color: #fff; font-weight: 600;
               background: rgba(0,0,0,.65); border-radius: 12px; padding: .4rem .8rem; z-index: 2; }
.scan-status:empty { display: none; }   /* no empty pill before the first status */
.scan-text-btn { background: rgba(0,0,0,.65); color: #fff; border: 1px solid rgba(255,255,255,.6);
                 border-radius: 999px; padding: .6rem 1.1rem; font-weight: 600;
                 text-shadow: 0 1px 3px #000; touch-action: manipulation; }
/* step 2 tints the viewfinder guide so "now photograph the whole receipt" reads at a glance */
/* once the QR is read the aiming square must GO — step 2 frames the WHOLE receipt, and a
   centered target contradicts the instruction (owner review 2026-07-17); the green tint
   alone signals the mode change */
.scanner.step2 .scan-frame { display: none; }
.scan-bottom { position: absolute; bottom: max(1.6rem, env(safe-area-inset-bottom)); left: 0; right: 0;
               display: flex; justify-content: center; align-items: center; gap: 1rem; z-index: 2; }
#scan-shutter { width: 4.4rem; height: 4.4rem; border-radius: 50%; border: 5px solid #fff;
                background: rgba(255,255,255,.25); touch-action: manipulation; padding: 0;
                /* the frame behind it can be a WHITE receipt — a dark halo keeps the white
                   ring visible in the worst case, like the overlay text scrims */
                box-shadow: 0 0 0 2px rgba(0,0,0,.4), inset 0 0 0 2px rgba(0,0,0,.25); }
#scan-shutter:active { background: #fff; }
.actions-secondary { display: flex; flex-wrap: wrap; gap: .6rem; margin: 1rem 0 .7rem; }
.actions-danger { margin-top: 1.1rem; text-align: right; }
button.primary-action { display: block; width: 100%; max-width: 22rem; padding: .7rem 1.2rem;
                        font-size: 1.05rem; }
/* phones: image + first fields must share one screenful — zoom covers the fine print */
@media (max-width: 760px) {
  img.preview { max-height: 35vh; }
  button.primary-action { max-width: none; }
}
img.preview { max-width: 100%; max-height: 30rem; border: 1px solid var(--grid);
              border-radius: 6px; cursor: zoom-in; }
/* phone: the confirm photo compacts in-flow, and once scrolled away a floating button
   keeps it one tap from the fields it verifies (phone-first shell 2026-08-09, stage 3) */
#photo-fab { display: none; }
@media (max-width: 540px) {
  .preview#receipt-img { max-height: 30vh; width: auto; max-width: 100%;
                         object-fit: contain; }
  #photo-fab:not([hidden]) { display: flex; position: fixed; z-index: 45;
    right: 1rem; bottom: calc(5.2rem + env(safe-area-inset-bottom));
    width: 3.2rem; height: 3.2rem; border-radius: 50%; padding: 0;
    align-items: center; justify-content: center;
    background: var(--accent-deep); color: #fff; border: 0;
    box-shadow: 0 4px 10px rgba(20,18,14,.35); }
}
.zoom { position: fixed; inset: 0; background: rgba(20,18,14,.92); z-index: 50;
        overflow: hidden; display: flex; align-items: center; justify-content: center;
        touch-action: none; }
/* the flex display above overrides the UA [hidden]{display:none}, so restore it
   explicitly (higher specificity) — else toggling .hidden never hides the overlay */
.zoom[hidden] { display: none; }
.zoom img { max-width: 90vw; max-height: 90vh; transform-origin: center center;
            cursor: grab; user-select: none; -webkit-user-drag: none; will-change: transform; }
.zoom img.grabbing { cursor: grabbing; }
/* keep the controls above the scaled image (which overflows over this corner) */
.zoom-controls { position: fixed; top: max(1rem, env(safe-area-inset-top));
                 right: max(1rem, env(safe-area-inset-right));
                 display: flex; gap: .4rem; z-index: 2; }
.zoom-controls button { width: 2.5rem; height: 2.5rem; border-radius: 6px; padding: 0;
                        border: 1px solid rgba(255,255,255,.4); background: rgba(0,0,0,.45);
                        color: #fff; font-size: 1.25rem; font-weight: 700; cursor: pointer; }
.zoom-controls button:hover { background: rgba(0,0,0,.75); }
.zoom-hint { position: fixed; bottom: max(1rem, env(safe-area-inset-bottom));
             left: 0; right: 0; text-align: center;
             color: #fff; font-size: .85rem; pointer-events: none; }
@media (hover: none) and (pointer: coarse) { .desktop-hint { display: none; } }
.inline { display: inline; margin: 0; }
.monthbar { display: flex; align-items: center; gap: .6rem; margin-bottom: 1rem; flex-wrap: wrap; }
.period { font-family: var(--display); font-size: 1.12rem; margin-left: .2rem;
          white-space: nowrap; }   /* the ledger period is a title, set in the ledger's face */
a.pager { text-decoration: none; font-weight: 600; padding: .3rem .7rem;
          border: 1px solid var(--grid); border-radius: 6px; background: var(--card); color: var(--ink); }
a.pager:hover { background: var(--grid-soft); }
.cal-toggle { border: 1px solid var(--grid); background: var(--card); color: var(--ink);
              font-weight: 600; border-radius: 6px; padding: .3rem .7rem; }
.cal-toggle:hover { background: var(--grid-soft); }

/* month calendar — a leaf of the ledger: red rule over the year, ruled month cells */
.calendar { border: 1px solid var(--grid); border-top: 3px solid var(--rule); border-radius: 6px;
            background: var(--card); max-width: 22rem; margin-bottom: 1rem; overflow: hidden; }
.cal-head { display: flex; align-items: center; justify-content: space-between;
            background: var(--band); border-bottom: 1px solid var(--grid); padding: .4rem .6rem; }
.cal-head button { background: none; border: 1px solid var(--ink); color: var(--ink);
                   border-radius: 6px; padding: .1rem .55rem; font-weight: 700; }
.cal-head button:hover { background: var(--ink); color: var(--band); }
.cal-year { font-family: var(--display); font-weight: 700; letter-spacing: .1em; }
.cal-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--grid); }
.cal-cell { background: var(--card); border: 0; cursor: pointer; padding: .55rem .3rem; text-align: center;
            display: flex; flex-direction: column; align-items: center; gap: .15rem; color: var(--ink); font: inherit; }
.cal-cell:hover { background: var(--grid-soft); }
.cal-cell.current { background: var(--tint); box-shadow: inset 0 0 0 2px var(--accent); }
.cal-cell .cal-mon { font-weight: 600; font-size: .9rem; }
.cal-cell .cal-count { font-size: .72rem; color: #fff; background: var(--accent-deep); border-radius: 999px;
                       min-width: 1.35rem; padding: .02rem .35rem; font-variant-numeric: tabular-nums; }
.cal-cell .cal-count.zero { background: transparent; color: var(--grid); }
