/* tisavibe console design system.
   One place for the palette + components. Formalizes the previously-inlined shell styles
   into tokens, adds reusable component classes, and makes the shell responsive. */

:root {
  /* surfaces */
  --bg:        #0c1929;   /* app background — deep navy */
  --surface:   #10233b;   /* cards */
  --surface-2: #0a1526;   /* sidebar, code, insets */
  --border:    #1d3557;
  --border-2:  #2c476b;   /* input borders */
  /* ink */
  --text:      #e7eef7;
  --text-2:    #c4d3e8;   /* nav links */
  --muted:     #93a4bd;
  --muted-2:   #6580a3;
  /* brand + semantic */
  --accent:      #7ab4f0;
  --accent-ink:  #0c1929;
  --accent-soft: #14304d;
  --good:   #5fd08a;
  --warn:   #e2b35b;
  --danger: #ffb4ad;
  /* shape */
  --r:    8px;
  --r-lg: 14px;
  --sidebar: 220px;
  /* The shared top bar's height, so a sticky sidebar can sit BELOW it and
     subtract it from its own — otherwise the sidebar runs past the bottom
     of the viewport by exactly the height of the bar. */
  --appbar-h: 49px;
}

/* ---- per-product accents -----------------------------------------------------------
   One console spans six products on two hosts, and the page furniture is identical in
   all of them — without a signal you cannot tell bookkeep from the marketing agent
   three clicks deep. Every component below already draws from --accent, so moving that
   one token re-dresses buttons, nav, chips, focus rings, bars and table hover together.

   Same surfaces, same ink, only the hue moves: six unrelated colour schemes would read
   as six unrelated products, one scheme with six accents reads as one suite.

   The hexes live here once, as --p-* on :root, so the body rule and the app switcher's
   per-product dots cannot drift apart. Kept in sync with core/theme.py PALETTE by
   core/tests_theme.py — adding a product without dressing it fails a test. */
:root {
  --p-marketing:    #b49bf5;  --p-marketing-soft:    #2a2350;  /* violet — making things */
  --p-bookkeep:     #5fd3b2;  --p-bookkeep-soft:     #0f3a33;  /* mint   — money, ledgers */
  --p-nutrition:    #a9d86f;  --p-nutrition-soft:    #2a3a17;  /* lime   — food */
  --p-intelligence: #5cc6ea;  --p-intelligence-soft: #0e3446;  /* cyan   — measurement */
  --p-markets:      #ecb75f;  --p-markets-soft:      #40320f;  /* amber  — commodities */
  --p-jobcopilot:   #f291ab;  --p-jobcopilot-soft:   #45202c;  /* rose */
  --p-ops:          #f4a261;  --p-ops-soft:          #43290f;  /* orange — operator only */
}
body[data-product="marketing"]    { --accent: var(--p-marketing);    --accent-soft: var(--p-marketing-soft); }
body[data-product="bookkeep"]     { --accent: var(--p-bookkeep);     --accent-soft: var(--p-bookkeep-soft); }
body[data-product="nutrition"]    { --accent: var(--p-nutrition);    --accent-soft: var(--p-nutrition-soft); }
body[data-product="intelligence"] { --accent: var(--p-intelligence); --accent-soft: var(--p-intelligence-soft); }
body[data-product="markets"]      { --accent: var(--p-markets);      --accent-soft: var(--p-markets-soft); }
body[data-product="jobcopilot"]   { --accent: var(--p-jobcopilot);   --accent-soft: var(--p-jobcopilot-soft); }
body[data-product="ops"]          { --accent: var(--p-ops);          --accent-soft: var(--p-ops-soft); }

/* The switcher shows all six at once, each in its own colour — that is where the
   colour/product association is actually TAUGHT, so the accent elsewhere reads as a
   location rather than as decoration. */
.appitem[data-product="marketing"]    .appitem-mark { color: var(--p-marketing); }
.appitem[data-product="bookkeep"]     .appitem-mark { color: var(--p-bookkeep); }
.appitem[data-product="nutrition"]    .appitem-mark { color: var(--p-nutrition); }
.appitem[data-product="intelligence"] .appitem-mark { color: var(--p-intelligence); }
.appitem[data-product="markets"]      .appitem-mark { color: var(--p-markets); }
.appitem[data-product="jobcopilot"]   .appitem-mark { color: var(--p-jobcopilot); }

* { box-sizing: border-box; margin: 0; padding: 0; }
body { font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
       background: var(--bg); color: var(--text); min-height: 100vh; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1 { font-size: 22px; margin-bottom: 6px; text-transform: lowercase; }
h2 { font-size: 16px; }
h3 { font-size: 14px; }
.sub { color: var(--muted); font-size: 13.5px; margin-bottom: 22px; }
.muted { color: var(--muted); }
code { background: var(--surface-2); padding: 2px 6px; border-radius: 5px; font-size: 13px; }

button, .btn { font: inherit; font-weight: 600; padding: 9px 15px; border: 0; border-radius: var(--r);
               background: var(--accent); color: var(--accent-ink); cursor: pointer; }
button:hover, .btn:hover { filter: brightness(1.06); text-decoration: none; }
button.ghost, .btn.ghost { background: transparent; color: var(--text); border: 1px solid var(--border-2); }
button.sm, .btn.sm { padding: 5px 11px; font-size: 13px; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

input, textarea, select { background: var(--bg); color: var(--text); border: 1px solid var(--border-2);
                          border-radius: var(--r); padding: 9px 11px; font: inherit; }
textarea { resize: vertical; }

/* ---- app shell (body.app) ----------------------------------------------------------
   Column, not row: the shared top bar (.appbar) spans the full width above the
   sidebar+main pair, the same shape the bookkeep host always had. Before this, the app
   console kept its account controls and workspace switcher inside the sidebar while
   bookkeep kept them in a header — the same customer met two different arrangements
   of the same three controls depending on which product they were in. */
body.app { display: flex; flex-direction: column; min-height: 100vh; }
aside { width: var(--sidebar); background: var(--surface-2); border-right: 1px solid var(--border);
        padding: 20px 16px; position: sticky; top: var(--appbar-h);
        height: calc(100vh - var(--appbar-h));
        flex-shrink: 0; overflow-y: auto; }
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 16px; }
.wordmark { font-size: 17px; font-weight: 800; display: flex; align-items: center; gap: 8px;
            color: var(--text); min-width: 0; }
.wordmark .mark { width: 28px; height: 28px; border-radius: 7px; background: var(--accent); color: var(--accent-ink);
                  display: flex; align-items: center; justify-content: center; font-weight: 900;
                  flex-shrink: 0; }
/* The product name can be two words ("food & nutrition") in a 220px sidebar. */
.wm-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wm-caret { font-size: 9px; opacity: .6; flex-shrink: 0; }
/* A hit area a thumb can actually find; the glyph alone was ~22px. */
.hamburger { display: none; font-size: 22px; line-height: 1; cursor: pointer; color: var(--text);
             padding: 10px 12px; margin: -10px -12px; border-radius: var(--r); }

/* ---- app switcher ------------------------------------------------------------------
   Was ~700 characters of inline style per item. As classes it can also be made to fit
   a 360px phone, where the old fixed 250px panel ran off the right edge. */
.appswitch { position: relative; min-width: 0; }
.appswitch > summary { list-style: none; cursor: pointer; }
.appswitch > summary::-webkit-details-marker { display: none; }
.appmenu { position: absolute; z-index: 60; top: calc(100% + 8px); left: 0;
           width: max-content; max-width: min(320px, calc(100vw - 32px)); min-width: 240px;
           background: var(--surface); border: 1px solid var(--border);
           border-radius: var(--r-lg); padding: 8px;
           box-shadow: 0 12px 30px rgba(0,0,0,.45); }
.appitem { display: flex; gap: 10px; align-items: center; padding: 9px 8px;
           border-radius: var(--r); color: var(--text); }
.appitem:hover { background: var(--surface-2); text-decoration: none; }
.appitem.is-soon { opacity: .5; }
.appitem-mark { width: 26px; height: 26px; border-radius: 7px; flex-shrink: 0;
                display: inline-flex; align-items: center; justify-content: center;
                font-weight: 800; font-size: 13px; background: var(--surface-2); }
.appitem-text { flex: 1; min-width: 0; }
.appitem-name { display: block; font-size: 13px; }
.appitem-name .muted { font-size: 10px; }
.appitem-tag { display: block; font-size: 10.5px; color: var(--muted);
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.appitem-cta { flex-shrink: 0; font-size: 9.5px; text-transform: uppercase;
               letter-spacing: .06em; color: var(--muted); border: 1px solid var(--border-2);
               border-radius: 999px; padding: 2px 8px; }
.navtoggle { position: absolute; left: -9999px; }
aside select { width: 100%; margin-bottom: 16px; }
nav a { display: block; padding: 8px 10px; border-radius: var(--r); color: var(--text-2); font-size: 14px; }
nav a:hover, nav a.active { background: var(--accent-soft); color: #fff; text-decoration: none; }
nav .group { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted-2);
             margin: 16px 0 6px 10px; }
nav form { margin-top: 10px; }

main { flex: 1; min-width: 0; padding: 32px 40px; max-width: 940px; }

.banner { padding: 9px 14px; border-radius: var(--r); margin-bottom: 16px; font-size: 13.5px; }
.banner.warn   { background: #4a3a12; color: #ffd98a; }
.banner.hijack { background: #3a1f4a; color: #e0b4ff; }
.msg { background: var(--accent-soft); padding: 10px 14px; border-radius: var(--r); margin-bottom: 12px; font-size: 14px; }
.msg.error { background: #4a1f22; color: var(--danger); }

.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
        padding: 20px 22px; margin-bottom: 16px; }

/* ---- reusable components (to migrate inline styles into) --------------------------- */
.kpi-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr)); }
.kpi .label { color: var(--muted); font-size: 12px; }
.kpi .value { font-size: 26px; font-weight: 800; }
.row { padding: 7px 0; border-bottom: 1px solid var(--border); }
.row:last-child { border-bottom: 0; }
.chip { font-size: 10px; text-transform: uppercase; letter-spacing: .06em; }
/* `.ok` is an alias for `.good`: four templates (pricing, dev_index, dev_product) were
   already written against it, so those chips have been rendering with no colour at all
   — a "subscribed" badge the same grey as everything around it. */
.chip.good, .chip.ok { color: var(--good); }
.chip.warn { color: var(--warn); } .chip.bad { color: var(--danger); }
.chip.info { color: var(--accent); } .chip.dim { color: var(--muted); }

/* Statement line badges (bookkeep_statements/badges.py): hue says something is
   missing, fill says how much. The glyph itself carries filled vs hollow (● vs ○);
   these classes only supply colour, so "hollow amber" and "filled amber" share one
   rule on purpose. */
.bk-dot { font-size: 15px; }
.bk-dot.green { color: var(--good); }
.bk-dot.blue { color: var(--accent); }
.bk-dot.amber, .bk-dot.hollow { color: var(--warn); }
.bk-dot.grey { color: var(--muted-2); }

/* ---- tables ------------------------------------------------------------------------
   Every table in the console was unstyled until now: no cell padding, no rules, headers
   the same weight as data. On a dark background that reads as a wall. These are plain
   element selectors on purpose — there are a dozen tables across bookkeep and pantry and
   they should all look the same without each template opting in. */
table { width: 100%; border-collapse: collapse; font-size: 14px; }
thead th { text-align: left; font-size: 11px; font-weight: 700; text-transform: uppercase;
           letter-spacing: .07em; color: var(--muted-2); white-space: nowrap;
           padding: 0 14px 9px 0; border-bottom: 1px solid var(--border); }
tbody td { padding: 12px 14px 12px 0; border-bottom: 1px solid var(--border);
           vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: rgba(122,180,240,.04); }
th:last-child, td:last-child { padding-right: 0; }
/* Numbers line up only if the glyphs are the same width — tabular-nums makes a column
   of quantities scannable instead of ragged. */
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* A table that overflows scrolls in its own box rather than widening the page. EVERY
   table in the console is wrapped in one of these (enforced by core/tests_theme.py) —
   a nine-column purchase-order table was pushing a whole phone screen sideways, and
   the next table someone adds would have done it again.
   The fade on the right edge is the only cue that there is more to see: a scrollbar
   does not appear on a touch device until you are already scrolling. */
.table-wrap {
  --wrap-bg: var(--bg);
  overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
  /* Two opaque covers that scroll WITH the content, over two shadows that do not: the
     shadow is therefore visible only on the side that has content off-screen. */
  background:
    linear-gradient(to right, var(--wrap-bg), transparent) left center,
    linear-gradient(to left,  var(--wrap-bg), transparent) right center,
    radial-gradient(farthest-side at 0 50%,   rgba(0,0,0,.45), transparent) left center,
    radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,.45), transparent) right center;
  background-repeat: no-repeat;
  background-size: 26px 100%, 26px 100%, 13px 100%, 13px 100%;
  background-attachment: local, local, scroll, scroll;
}
.card .table-wrap { --wrap-bg: var(--surface); }

/* ---- pantry shelf -------------------------------------------------------------------
   Each shelf row carries three separate <form>s (staple, ran-out, set-quantity). A form
   is block-level, so unstyled they stacked and every row became three tall — the single
   biggest reason this page was unreadable. Laying them out inline puts a row back on one
   line. */
.pantry td form { display: inline-flex; align-items: center; gap: 6px; margin: 0; }
.pantry .actions { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
.pantry .qty { display: flex; align-items: center; gap: 6px; justify-content: flex-end; }
.pantry .qty input { width: 68px; padding: 5px 8px; text-align: right;
                     font-variant-numeric: tabular-nums; }
.pantry .food { font-weight: 600; }
.pantry .unit { color: var(--muted); font-size: 12.5px; }
/* The buy list is the part you read in the shop, so it gets size rather than a table. */
.pantry .buylist { list-style: none; display: flex; flex-wrap: wrap; gap: 8px; }
.pantry .buylist li { display: flex; align-items: center; gap: 7px; background: var(--surface-2);
                      border: 1px solid var(--border); border-radius: 999px;
                      padding: 6px 13px; font-size: 14px; }
.pantry .addform { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
.pantry .addform label { display: flex; flex-direction: column; gap: 5px; font-size: 12px;
                         color: var(--muted); font-weight: 600; }
.pantry .addform label.inline { flex-direction: row; align-items: center; }

/* ---- review: move a receipt's vendor ----------------------------------------------- */
.vendor-move { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
               margin: 6px 0 12px; font-size: 13.5px; }
.vendor-move label { display: flex; align-items: center; gap: 6px; color: var(--muted); }
.vendor-move select { padding: 5px 9px; max-width: 260px; }
.vendor-move .muted { font-size: 12.5px; }

/* ---- bookkeep shell (body.bk) ------------------------------------------------------
   bookkeep had no navigation at all: a wordmark, a content block, and an ad-hoc
   "back" link at the foot of each screen. Switching workspace meant returning to the
   root and starting again. This is a sidebar on a desktop and a fixed bottom bar on a
   phone — the app is used standing in a kitchen or a shop, where a hamburger puts
   every destination two taps away behind a control you must go looking for. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden;
           clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

body.bk { display: flex; flex-direction: column; min-height: 100vh; }

/* ---- the shared top bar ------------------------------------------------------------
   Rendered by templates/_appbar.html on BOTH hosts and in every product. It carries the
   three things true wherever you are: which product you are in, which workspace you are
   working in, and whose account this is. */
.appbar { display: flex; align-items: center; gap: 14px; padding: 10px 20px;
          background: var(--surface-2); border-bottom: 1px solid var(--border);
          position: sticky; top: 0; z-index: 20; }
.appbar .wordmark { font-size: 15px; }
.appbar .wordmark:hover { text-decoration: none; }
/* Everything account-shaped sits together on the right, in one group, so the eye
   learns one place for "who am I and where am I working". */
.appbar-right { margin-left: auto; display: flex; align-items: center; gap: 12px;
                min-width: 0; }
.appbar-switch { display: flex; align-items: center; gap: 6px; }
.appbar-switch select { padding: 6px 10px; font-size: 13.5px; max-width: 46vw; }
/* The email can be long and is the LEAST important of the three — it truncates first
   rather than pushing the workspace switcher off the bar. */
.appbar-account { font-size: 13px; color: var(--muted); min-width: 0; max-width: 22ch;
                  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.appbar-account:hover { color: var(--text); }
.appbar-home { font-size: 12px; color: var(--muted); text-decoration: none; }
.appbar-home:hover { color: var(--text); text-decoration: none; }
.appbar-signout { margin: 0; }
.appbar-signout button { white-space: nowrap; }

.shell-body { display: flex; flex: 1; min-height: 0; }
.bk-side { width: var(--sidebar); flex-shrink: 0; background: var(--surface-2);
           border-right: 1px solid var(--border); padding: 16px 12px; }
.bk-side a { display: flex; align-items: center; gap: 8px; padding: 9px 11px;
             border-radius: var(--r); color: var(--text-2); font-size: 14px; }
.bk-side a:hover { background: var(--accent-soft); color: #fff; text-decoration: none; }
.bk-side a.on { background: var(--accent-soft); color: #fff; font-weight: 600; }
.bk-group { font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
            color: var(--muted-2); margin: 18px 0 6px 11px; }

.bk-badge { background: var(--accent); color: var(--accent-ink); font-size: 11px;
            font-weight: 700; border-radius: 999px; padding: 1px 7px; line-height: 1.5; }

/* The bottom bar exists only on phones; the sidebar covers everything above. */
.bk-tabs { display: none; }

@media (max-width: 760px) {
  .bk-side { display: none; }
  .appbar { padding: 8px 14px; gap: 10px;
            padding-left: max(14px, env(safe-area-inset-left));
            padding-right: max(14px, env(safe-area-inset-right)); }
  .appbar .wordmark { font-size: 14px; }
  /* Four things do not fit across 347px of phone, and cramming them made the bar
     overflow the screen. The switcher gets its own full-width row instead of a share of
     one: it is the most-used control here, a full-width select is a far better thumb
     target than a squeezed one, and the product / account / sign-out line above it
     stays intact — so the bar still carries all three facts rather than dropping one. */
  .appbar { flex-wrap: wrap; row-gap: 9px; }
  /* `display: contents` dissolves the group so its three children become flex items of
     the bar itself. Nesting could not produce the layout: flex breaks lines on an
     item's max-content width, so a group containing a full-width switcher never fits
     beside the wordmark and wrapped ENTIRELY below it — three rows, 159px, a fifth of
     the phone screen spent on chrome before any content. Dissolved, the switcher alone
     takes the second row and the account stays up top where it belongs. */
  .appbar-right { display: contents; }
  .appbar-switch { order: 3; flex: 1 1 100%; }
  /* Which workspace you are in decides which books a receipt lands in, so this is the
     control that must NOT trigger iOS's zoom-on-focus — you would be picking a set of
     books from a magnified, sideways-scrolled page. 16px is the threshold. */
  .appbar-switch select { font-size: 16px; max-width: none; flex: 1; }
  /* Primary navigation, and 28px tall — padded to a thumb rather than a cursor. */
  .appbar .wordmark { padding: 6px 0; }
  .appbar-account {
    margin-left: auto;   /* carries what the dissolved group used to hold */
    padding: 10px 0;     /* was a 20px tap target */
    /* Truncates rather than pushing sign-out off the edge. At 18ch the three items came
       to exactly the available width and sign-out wrapped to a row of its own, so this
       is sized to leave headroom rather than to fit precisely — the email is the least
       important of the three and the one that can afford to be a fragment. */
    max-width: 12ch;
  }

  .bk-tabs { display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
             background: var(--surface-2); border-top: 1px solid var(--border);
             padding-bottom: env(safe-area-inset-bottom); }
  .bk-tabs a { flex: 1; display: flex; flex-direction: column; align-items: center;
               gap: 2px; padding: 9px 4px 8px; font-size: 11px; color: var(--muted);
               position: relative; }
  .bk-tabs a:hover { text-decoration: none; }
  .bk-tabs a.on { color: var(--accent); }
  .bk-tabs .ic { font-size: 17px; line-height: 1; }
  /* Anchored to the icon rather than inline, so a two-digit count cannot shove the
     label sideways and make the tabs jump between pages. */
  .bk-tabs .bk-badge { position: absolute; top: 3px; left: 50%; margin-left: 5px;
                       font-size: 10px; padding: 0 5px; }

  /* Clear the fixed bar, including the home indicator on a notched phone. */
  body.bk.bk-in-workspace main { padding-bottom: calc(74px + env(safe-area-inset-bottom)); }
}

/* Small phones — an iPhone SE is 320px and the bar has one row's worth of space too
   little there, which pushed sign-out down and cost a third row. The product NAME is
   the thing that goes: the coloured mark still says which product this is, and tapping
   it opens a menu that names every one of them. */
@media (max-width: 360px) {
  .appbar .wm-name { display: none; }
}

/* Workspace chooser — the one screen that is not inside a workspace, so it is also
   the only one with no nav. Cards rather than a list because picking the wrong set
   of books is the expensive mistake here, and a row of links invites a mis-tap. */
.bk-picker { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
.bk-pick { display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
           margin-bottom: 0; color: var(--text); }
.bk-pick:hover { border-color: var(--accent); text-decoration: none; }
.bk-pick-name { font-size: 17px; font-weight: 700; }
.bk-pick .muted { font-size: 13px; }

/* ---- monthly bar charts (food history) ----------------------------------------------
   CSS bars rather than an SVG or a charting library: the values are a dozen numbers a
   month apart, the heights are computed in Python (pantry/insights.py — the template
   language cannot divide), and nothing here needs an axis a person would read against.

   Fixed-width columns inside a .table-wrap, so twelve months scroll sideways on a phone
   instead of squeezing to slivers too thin to tell apart. */
.barchart { display: flex; align-items: flex-end; gap: 10px; height: 190px;
            padding: 4px 2px 0; min-width: min-content; }
.bar-col { display: flex; flex-direction: column; justify-content: flex-end;
           align-items: center; gap: 5px; height: 100%; flex: 0 0 52px; }
/* The bar grows from the bottom of a flex box that already reserves the label's room,
   so a tall month cannot push its own label off the chart. */
.bar { width: 100%; background: var(--accent); border-radius: 4px 4px 0 0;
       min-height: 2px; flex-shrink: 0; }
.bar.alt { background: var(--accent-soft); border: 1px solid var(--accent); }
.bar-val { font-size: 11px; color: var(--muted); white-space: nowrap;
           font-variant-numeric: tabular-nums; }
.bar-lab { font-size: 10.5px; color: var(--muted-2); white-space: nowrap; }

/* A single stacked bar for the macro split. Proportions only — there is no scale to
   read, which is why it carries its numbers in the line beneath rather than an axis. */
.split { display: flex; height: 12px; border-radius: 999px; overflow: hidden;
         background: var(--surface-2); }
.split-part { display: block; height: 100%; }
.split-part.protein { background: var(--accent); }
.split-part.fat     { background: var(--warn); }
.split-part.carbs   { background: var(--accent-soft); }

/* ---- legal pages (body.legal) -------------------------------------------------------
   A privacy policy is read by a Play reviewer with no account and by a customer
   deciding whether to install, so it renders with no shell and no session. Measure is
   capped near 68 characters: full-width prose on a laptop is the other way to make a
   document unreadable. */
body.legal { padding: 28px 20px calc(56px + env(safe-area-inset-bottom));
             padding-left: max(20px, env(safe-area-inset-left));
             padding-right: max(20px, env(safe-area-inset-right)); }
.legal-doc { max-width: 68ch; margin: 0 auto; }
.legal-doc .wordmark { margin-bottom: 26px; }
.legal-doc .wordmark:hover { text-decoration: none; }
.legal-doc h1 { margin-bottom: 4px; }
.legal-doc h2 { font-size: 15px; margin: 26px 0 6px; }
.legal-doc p, .legal-doc li { color: var(--text-2); line-height: 1.65; }
.legal-doc p { margin-bottom: 10px; }
.legal-doc ul { margin: 0 0 10px 20px; }
.legal-doc li { margin-bottom: 7px; }
.legal-doc strong { color: var(--text); }

/* ---- auth screens (body.auth) ----------------------------------------------------- */
body.auth { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 20px; }
.auth-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
             padding: 32px 34px; width: 100%; max-width: 380px; box-shadow: 0 12px 40px rgba(0,0,0,.4); }
.auth-card .wordmark { margin-bottom: 6px; }
.auth-card label { font-size: 12px; color: var(--muted); display: block; margin: 12px 0 4px; font-weight: 600; }
.auth-card input { width: 100%; }
.auth-card button { width: 100%; margin-top: 16px; }
.auth-card .err { background: #4a1f22; color: var(--danger); padding: 9px 12px; border-radius: var(--r); font-size: 14px; margin-bottom: 12px; }
.auth-card .links { margin-top: 14px; font-size: 12.5px; text-align: center; color: var(--muted); }

/* ---- responsive: collapse the sidebar into a top bar + hamburger drawer ------------ */
@media (max-width: 760px) {
  /* BOTH of these, and the second one is the one that matters. The sidebar and the
     content used to be children of <body>, so stacking the body stacked them. The
     shared top bar wrapped them in `.shell-body` — which is itself `display:flex` —
     and this rule went on pointing at the body, which is no longer the flex container
     holding them. The sidebar still went full-width, the row direction still applied,
     and `main` was pushed to x=375 and squeezed to THIRTY-TWO PIXELS: the entire app
     console was a nav screen with the page scrolled off the right edge of the phone. */
  body.app { flex-direction: column; }
  .shell-body { flex-direction: column; }
  aside { width: 100%; height: auto; position: sticky; top: 0; z-index: 40; border-right: 0;
          border-bottom: 1px solid var(--border); padding: 12px 16px; overflow: visible; }
  .topbar { margin-bottom: 0; }
  .hamburger { display: block; }
  /* The drawer scrolls inside itself. The sidebar is sticky here so the wordmark and
     the menu button stay reachable while you read; without a cap, a workspace with
     every product subscribed makes a nav taller than the phone, and the bottom entries
     become unreachable because the sticky box has already stopped moving. */
  .navwrap { display: none; margin-top: 12px; }
  .navtoggle:checked ~ .navwrap { display: block;
                                  max-height: calc(100vh - var(--appbar-h) - 96px);
                                  overflow-y: auto; overscroll-behavior: contain; }
  main { padding: 20px 16px calc(28px + env(safe-area-inset-bottom)); max-width: 100%; }

  /* Every nav destination is a thumb target here, not a mouse target. */
  nav a { padding: 11px 10px; }

  /* iOS Safari ZOOMS THE PAGE when you focus a field whose text is under 16px, and it
     does not zoom back out afterwards — you finish the form scrolled sideways in a
     magnified layout. Every control in the console was 15px. This is the single most
     consequential mobile fix in this file. */
  input, textarea, select, button, .btn { font-size: 16px; }
  /* The one !important in this file, and it is load-bearing. Dense tables carry
     controls with the size written inline (`style="font-size:12px"`), and an inline
     declaration beats any selector — so the rule above silently lost on exactly the
     screens it exists for. This is a platform constraint rather than a preference:
     nothing gains by overriding it, and a template author cannot be expected to
     remember it. Fields only; a small BUTTON does not zoom, so buttons keep theirs. */
  input:not([type=hidden]), textarea, select { font-size: 16px !important; }
  /* A control the thumb can hit, and stacked full-width rather than crammed side by
     side at 40% width each. */
  input, textarea, select { min-height: 44px; }
  button, .btn { min-height: 44px; padding: 11px 16px; }
  button.sm, .btn.sm { min-height: 38px; padding: 8px 13px; font-size: 14px; }

  h1 { font-size: 20px; }
  .card { padding: 16px 15px; }
  /* A pill is a label most places and a FILTER on the ops pages, where the same 24px
     chip is something you are meant to hit. Only the tappable ones grow — a status
     badge inside a table row has no business being thumb-sized. */
  a.pill { min-height: 32px; display: inline-flex; align-items: center; padding: 0 11px; }

  /* Label-and-values rows wrap instead of running off the screen. The economy page is
     the worst case and shows why this belongs here rather than in one template: a
     120px sparkline plus three min-width columns is ~390px of fixed content in 313px
     of card. It only surfaced on the CANADA page — the US rows had no data, so the
     wide branch never rendered and the page measured clean. */
  .row { flex-wrap: wrap; }
  /* Landscape/notched phones put the sidebar edge under the rounded corner. */
  aside, main { padding-left: max(16px, env(safe-area-inset-left));
                padding-right: max(16px, env(safe-area-inset-right)); }
}

/* ---- the customer's own surfaces ----------------------------------------------------
   The account home, the product gateways and the account section. These were written
   with the styling inline, which made them impossible to keep consistent with each
   other; this is the same layout expressed once. */

/* A responsive grid of cards. --min is the point at which a column may not shrink
   further — below it the grid drops to fewer columns rather than squeezing.
   240px is chosen against the actual content box, not the viewport: with the 220px
   sidebar, a 785px tablet leaves ~485px, which fits two columns at 240 and only one
   at 260. The card copy is a short tagline, so 240 is not cramped. */
.grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(min(var(--min, 240px), 100%), 1fr)); }
.grid.tight { gap: 12px; --min: 210px; }

/* One product on the account home. The whole card is the target — a card with a small
   link in it makes you aim, and this is the page's primary action. */
.door { display: flex; flex-direction: column; gap: 6px; margin-bottom: 0; color: var(--text);
        position: relative; overflow: hidden;
        transition: border-color .12s ease, transform .12s ease; }
/* The card spends its product's colour: a soft wash from its own --accent-soft (each
   door re-points that token via [data-product] below), and a lit border when ready.
   Children lift above the wash — a positioned pseudo otherwise paints over in-flow text. */
.door::before { content: ''; position: absolute; inset: 0 0 auto 0; height: 54px;
                background: linear-gradient(to bottom, var(--accent-soft), transparent);
                opacity: .5; pointer-events: none; }
.door > * { position: relative; }
.door.is-ready { border-color: var(--accent); }
.door.acct { border-style: dashed; }
.door:hover { border-color: var(--accent); text-decoration: none; transform: translateY(-1px); }
.door:active { transform: none; }
@media (prefers-reduced-motion: reduce) { .door { transition: none; } .door:hover { transform: none; } }
.door-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.door-mark { font-size: 20px; line-height: 1; width: 38px; height: 38px; border-radius: 10px;
             display: flex; align-items: center; justify-content: center; font-weight: 800;
             background: var(--accent-soft); color: var(--accent); }
.door h2 { font-size: 15px; margin-top: 8px; }
.door p { font-size: 12.5px; color: var(--muted); }
.door-next { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.door-go { font-size: 11.5px; color: var(--accent); margin-top: 2px; }

/* A door with nothing behind it — a product you already have, or one not on sale yet.
   It stays on the page because "what is not on offer" is part of the answer to "what is
   on offer", and a gap where a product used to be reads as a bug.

   `cursor: default` and no hover lift, because the whole point is that it does not
   invite a click. The markup makes the same promise — a dimmed card is rendered as a
   div, never a link — and this is the half a customer can see. */
.door.is-dim { opacity: .45; cursor: default; }
.door.is-dim:hover { transform: none; border-color: var(--line); }

/* A card's own accent, so the six doors on the home page are six colours rather than
   six copies of the current one. Same values as the body rules — one palette. */
.door[data-product="marketing"]    { --accent: var(--p-marketing);    --accent-soft: var(--p-marketing-soft); }
.door[data-product="bookkeep"]     { --accent: var(--p-bookkeep);     --accent-soft: var(--p-bookkeep-soft); }
.door[data-product="nutrition"]    { --accent: var(--p-nutrition);    --accent-soft: var(--p-nutrition-soft); }
.door[data-product="intelligence"] { --accent: var(--p-intelligence); --accent-soft: var(--p-intelligence-soft); }
.door[data-product="markets"]      { --accent: var(--p-markets);      --accent-soft: var(--p-markets-soft); }
.door[data-product="jobcopilot"]   { --accent: var(--p-jobcopilot);   --accent-soft: var(--p-jobcopilot-soft); }
.door.is-soon { opacity: .55; }
.door.is-soon:hover { border-color: var(--border); transform: none; }

/* Progress through a product's setup checklist. */
.meter { height: 5px; background: var(--surface-2); border-radius: 999px; overflow: hidden; margin: 10px 0 0; }
.meter > span { display: block; height: 100%; background: var(--accent); border-radius: 999px; }

/* A status pill. .chip is a bare text style; this is the bordered form. */
/* A run of links or chips written back to back in a {% for %} has NO break opportunity:
   adjacent inline elements with no whitespace between them form ONE unbreakable line
   box, so a dozen tickers or a watchlist of terms rendered as a single line and ran off
   the side of the phone — 641px of it, in the worst case found. Flex with a gap both
   wraps and spaces them, replacing the per-item margin these all used to carry. */
.wrapline { display: flex; flex-wrap: wrap; gap: 9px; }
.wrapline.tight { gap: 6px; }

.pill { flex-shrink: 0; font-size: 10px; text-transform: uppercase; letter-spacing: .06em;
        font-weight: 700; padding: 3px 9px; border-radius: 999px; white-space: nowrap;
        border: 1px solid var(--border-2); color: var(--muted); }
.pill.on   { border-color: transparent; background: var(--accent-soft); color: var(--accent); }
.pill.todo { border-color: transparent; background: #4a3a12; color: #ffd98a; }

/* The setup checklist on a gateway. */
.steps { list-style: none; display: grid; gap: 4px; }
.step { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0;
        border-bottom: 1px solid var(--border); }
.step:last-child { border-bottom: 0; }
.step.done { opacity: .5; }
.step-n { flex: 0 0 22px; height: 22px; border-radius: 999px; display: flex;
          align-items: center; justify-content: center; font-size: 11px; font-weight: 700;
          background: var(--surface-2); color: var(--muted); }
.step.done .step-n { background: var(--accent-soft); color: var(--accent); }
.step-body { flex: 1; min-width: 0; }
.step-label { font-size: 13.5px; font-weight: 600; }
.step.done .step-label { text-decoration: line-through; }
.step-hint { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }

/* Section heads between bands of cards. */
.band { margin-top: 28px; }
.band h2 { font-size: 13px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted-2); }
.band .note { font-size: 12px; color: var(--muted); margin-bottom: 10px; }

/* A workspace band header reads as a PLACE: kind icon, full-size name, kind badge.
   `.band .band-place h2` out-specifies `.band h2` because these headers opt out of the
   quiet uppercase label the other bands ("waiting to be placed", "your account") keep. */
/* Wraps so a long workspace name on a phone drops the pill to a second line rather
   than squeezing the name into an overflow. */
.band-place { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 9px;
              margin-bottom: 11px; }
.band-place .ws-ic { font-size: 16px; line-height: 1; }
.band .band-place h2 { font-size: 16px; text-transform: none; letter-spacing: 0;
                       color: var(--text); }
.kindpill { font-size: 9.5px; font-weight: 700; text-transform: uppercase;
            letter-spacing: .07em; background: var(--surface-2);
            border: 1px solid var(--border); color: var(--muted);
            border-radius: 999px; padding: 2px 8px; white-space: nowrap; }
.band-settings { margin-left: auto; font-size: 12px; color: var(--muted); }
.band-settings:hover { color: var(--text); }

/* Choosing what a workspace is for, on create and in settings. */
.kinds { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr)); }
.kinds label { display: block; cursor: pointer; }
.kinds input { position: absolute; opacity: 0; width: 0; height: 0; }
.kindcard { display: block; padding: 13px; margin-bottom: 0; border: 1px solid var(--border);
            border-radius: var(--r-lg); background: var(--surface);
            transition: border-color .12s, background .12s; }
.kinds input:checked + .kindcard { border-color: var(--accent); background: var(--accent-soft); }
.kinds input:focus-visible + .kindcard { outline: 2px solid var(--accent); outline-offset: 2px; }
.kindcard .ic { font-size: 20px; }
.kindcard strong { display: block; font-size: 13.5px; margin-top: 4px; }
.kindcard .muted { display: block; font-size: 12px; }

/* The account section's tabs. Scrollable rather than wrapped on a narrow phone, so the
   row stays one line and the page below does not shift as you move between tabs. */
.acct-tabs { display: flex; gap: 4px; margin: 14px 0 18px; border-bottom: 1px solid var(--border);
             padding-bottom: 10px; overflow-x: auto; scrollbar-width: none; }
.acct-tabs::-webkit-scrollbar { display: none; }
.acct-tabs a { padding: 8px 13px; border-radius: var(--r); font-size: 13.5px;
               color: var(--text-2); white-space: nowrap; }
.acct-tabs a:hover { background: var(--surface-2); text-decoration: none; }
.acct-tabs a.on { background: var(--accent-soft); color: var(--accent); font-weight: 700; }

/* One product's billing row. */
.subrow { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
.subrow .door-mark { flex-shrink: 0; }
.subrow-body { flex: 1 1 200px; min-width: 0; }
.subrow-body strong { font-size: 14px; }
.subrow-body .muted { display: block; font-size: 12.5px; }

/* A definition list reads better than a two-column table for key/value facts, and it
   does not need a scroll container on a phone. */
.facts { display: grid; grid-template-columns: minmax(9em, auto) 1fr; gap: 8px 18px;
         font-size: 13.5px; margin-top: 10px; }
.facts dt { color: var(--muted); }
.facts dd { min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 460px) { .facts { grid-template-columns: 1fr; gap: 2px 0; }
                            .facts dd { margin-bottom: 8px; } }

/* ---- ergonomics that are not width-dependent ---------------------------------------
   An <a> wrapping a <button> is an inline box around a block one: the anchor collapses
   to text height, so the focus ring and any hover styling sit in the wrong place even
   though the button inside looks fine. Every action link in the console is written that
   way, so fix the pattern rather than each site. */
a:has(> button) { display: inline-block; }
/* Actions sitting together should wrap on a narrow screen instead of overflowing. */
.actions-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

/* Feature toggles on the workspace settings page. A big hit area with the
   explanation beside it: the decision is "does this workspace do that job", which
   is not obvious from a two-word label. */
.bk-toggle { display: flex; gap: 12px; align-items: flex-start; padding: 14px 0;
             border-bottom: 1px solid var(--border); cursor: pointer; }
.bk-toggle:last-child { border-bottom: 0; }
.bk-toggle input { margin-top: 3px; width: 18px; height: 18px; flex-shrink: 0; }
.bk-toggle span { display: flex; flex-direction: column; gap: 3px; }
.bk-toggle .muted { font-size: 13px; line-height: 1.45; }

/* The four numbers that decide whether a receipt can be booked. Laid out in a row
   so the one that disagrees is obvious at a glance — the build error alone said
   something was wrong and nothing about which number to change. */
.bk-recon { display: flex; flex-wrap: wrap; gap: 22px; align-items: center;
            padding: 14px 18px; font-variant-numeric: tabular-nums; }
.bk-recon .lbl { display: block; font-size: 11px; text-transform: uppercase;
                 letter-spacing: .07em; color: var(--muted-2); margin-bottom: 2px; }
.bk-recon > span { font-size: 15px; }

/* Adding a line or a tax the extraction missed. */
.bk-add { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px;
          margin: 14px 0; font-size: 13px; }
.bk-add label { display: flex; flex-direction: column; gap: 4px; color: var(--muted); }
.bk-add input, .bk-add select { padding: 6px 9px; }

/* The books: receipts / journal / trial balance. */
.bk-subtabs { display: flex; gap: 4px; flex-wrap: wrap; margin: -6px 0 18px;
              border-bottom: 1px solid var(--border); }
.bk-subtabs a { padding: 8px 14px; font-size: 13.5px; color: var(--muted);
                border-bottom: 2px solid transparent; margin-bottom: -1px; }
.bk-subtabs a:hover { color: #fff; text-decoration: none; }
.bk-subtabs a.on { color: #fff; font-weight: 600; border-bottom-color: var(--accent); }

.bk-window { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px;
             margin: 0 0 16px; font-size: 13px; }
.bk-window label { display: flex; flex-direction: column; gap: 4px; color: var(--muted); }
.bk-window input { padding: 6px 9px; }

/* The tick column on an account's ledger: which lines go on the printable claim.
   Narrow and fixed, so the columns that carry meaning keep the width. Cells are left
   empty rather than the column dropped when a line cannot be claimed — a row that
   lost its first cell would shunt every other cell one place left. */
.pick-col { width: 28px; padding-right: 0; }
.pick-col input { margin: 0; }

/* The claim form sits between the window form and the ledger, and is a sentence about
   what the button will do rather than a row of controls. */
#claim-picker { display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
                margin: 0 0 16px; }

/* One posted entry. Read a transaction at a time, so the card is the unit. */
.je { margin-bottom: 12px; padding: 14px 16px; }
.je-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; }
.je-date { font-variant-numeric: tabular-nums; font-weight: 600; }
.je-vendor { font-weight: 600; }
.je-id { margin-left: auto; font-size: 12px; }
.je-memo { font-size: 13px; margin: 6px 0 0; }
.je table { margin-top: 8px; }

.tb-ok { border-color: var(--good); }
.tb-bad { border-color: var(--danger); color: var(--danger); }

/* One submit for every account dropdown in the lines table. */
.bk-bulk { display: flex; align-items: center; gap: 12px; margin: 12px 0 0; }
.bk-bulk .muted { font-size: 12.5px; }

/* Converting a receipt paid in another currency. */
.bk-fx { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px;
         margin: 14px 0; font-size: 13px; }
.bk-fx form { display: flex; align-items: flex-end; gap: 8px; margin: 0; }
.bk-fx label { display: flex; flex-direction: column; gap: 4px; color: var(--muted); }
.bk-fx input { padding: 6px 9px; }

/* Settling a duplicate, beside the warning that names it. */
.bk-dismiss { display: flex; align-items: center; gap: 8px; margin: 10px 0 0; }
.bk-dismiss select { padding: 5px 9px; }

/* Naming the food lines, in place. */
.bk-foodlines { list-style: none; display: grid; gap: 6px; margin: 10px 0; }
.bk-foodlines li { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.bk-foodlines .printed { font-family: ui-monospace, monospace; font-size: 12px;
                         color: var(--muted); min-width: 15em; }
.bk-foodlines input { padding: 5px 9px; min-width: 14em; }
/* Removing a line the extraction invented. Quiet until reached for — it is beside
   every row, and only a handful of rows are ever phantoms. */
.bk-foodlines .bk-delfood { padding: 2px 8px; line-height: 1; color: var(--muted);
                            border-color: transparent; }
.bk-foodlines .bk-delfood:hover { color: #ff8b8b; border-color: var(--border-2);
                                  filter: none; }

/* The one irreversible act in bookkeep. Closed by default and last on the page —
   quiet until reached for, and never adjacent to the controls used every day. */
.bk-danger { margin: 28px 0 8px; border: 1px solid var(--border);
             border-radius: var(--r-lg); padding: 12px 16px; }
.bk-danger[open] { border-color: var(--danger); }
.bk-danger > summary { cursor: pointer; font-size: 13.5px; color: var(--muted);
                       list-style: none; }
.bk-danger[open] > summary { color: var(--danger); font-weight: 600;
                             margin-bottom: 10px; }
.bk-danger > summary::-webkit-details-marker { display: none; }
.bk-danger > summary::before { content: '⚠ '; }
.bk-danger p { font-size: 12.5px; margin: 6px 0; }
.bk-danger input[type=text] { max-width: 12em; }
.bk-danger button { background: var(--danger); color: #2a0d0d; }

/* Settling a receipt the detector never flagged. Same collapsed shape as the delete
   block below it, deliberately WITHOUT the warning marker or the red button: this one
   deletes nothing and "Put it back" undoes it, so dressing it as danger would teach
   people to fear the reversible act and read past the irreversible one. */
.bk-settle { margin: 28px 0 8px; border: 1px solid var(--border);
             border-radius: var(--r-lg); padding: 12px 16px; }
.bk-settle > summary { cursor: pointer; font-size: 13.5px; color: var(--muted);
                       list-style: none; }
.bk-settle[open] > summary { color: var(--text); font-weight: 600;
                             margin-bottom: 10px; }
.bk-settle > summary::-webkit-details-marker { display: none; }
.bk-settle p { font-size: 12.5px; margin: 6px 0; }
.bk-settle input[type=text] { max-width: 12em; }

/* Sortable column headers. */
.bk-sort { color: inherit; display: inline-flex; align-items: center; gap: 4px; }
.bk-sort:hover { color: #fff; }
.bk-sort.on { color: #fff; }
.bk-sort .arrow { font-size: 9px; opacity: .8; }

/* Ledger settings and the chart of accounts. */
.bk-field { display: flex; flex-direction: column; gap: 5px; margin: 14px 0 4px;
            font-size: 12px; color: var(--muted); font-weight: 600; }
.bk-field select { padding: 7px 10px; max-width: 280px; font-size: 14px; }
tr.off td { opacity: .55; }

/* Sub-accounts on the trial balance. */
tr.child td:first-child { padding-left: 18px; }
tr.child .tree { color: var(--muted-2); margin-right: 6px; }
tr.rolled td { border-top: 0; font-size: 12.5px; }
tr.rolled .num { font-weight: 700; }

/* Preset date ranges above the from/to boxes. */
.bk-periods { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.bk-periods a { padding: 5px 11px; font-size: 12.5px; color: var(--muted);
                border: 1px solid var(--border); border-radius: 999px; }
.bk-periods a:hover { color: #fff; border-color: var(--accent); text-decoration: none; }
.bk-periods a.on { color: var(--accent-ink); background: var(--accent);
                   border-color: var(--accent); font-weight: 600; }

/* "Where does it go?" — the categorisation assistant. */
.bk-ask { display: flex; gap: 8px; margin: 14px 0 22px; flex-wrap: wrap; }
.bk-ask input { flex: 1 1 22em; padding: 9px 12px; }
.bk-answer { border: 1px solid var(--border); border-radius: var(--r);
             padding: 12px 14px; margin: 0 0 12px; }
.bk-answer .q { font-weight: 600; margin: 0 0 4px; }
.bk-answer .verdict { margin: 0 0 6px; font-size: 15px; }
.bk-answer .why { margin: 0 0 8px; color: var(--muted); }
.bk-answer .stamp { font-size: 12px; margin: 6px 0 0; }
.bk-makerule { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bk-makerule input { padding: 5px 9px; min-width: 14em; }

/* The same question, asked from the line it is about, on the review screen.
   Deliberately quiet: it sits on rows nobody has placed yet, and a receipt can
   carry forty of them, so a loud control would read as forty errors. */
.bk-askline { width: 20px; height: 20px; padding: 0; border-radius: 50%;
              font-size: 12px; line-height: 1; vertical-align: middle;
              margin-left: 6px; color: var(--muted); }
.bk-askline:hover:not(:disabled) { color: var(--fg); }
.bk-askline:disabled { opacity: .5; }
.bk-lineanswer { display: block; margin: 4px 0 0; font-size: 12.5px;
                 line-height: 1.45; max-width: 34em; }
.bk-lineanswer .verdict { font-weight: 600; }
/* The reason goes on its own line under the verdict — the cell is already the
   widest on the row and a reason run inline would push the totals off screen. */
.bk-lineanswer .why { display: block; }

/* Buying habits — descriptive / diagnostic / predictive / prescriptive. */
.bk-bar { display: flex; align-items: center; gap: 10px; padding: 4px 0; }
.bk-bar-label { flex: 0 0 15em; font-size: 13.5px; }
.bk-bar-track { flex: 1; height: 10px; background: var(--surface-2);
                border-radius: 999px; overflow: hidden; }
.bk-bar-fill { display: block; height: 100%; background: var(--accent);
               border-radius: 999px; }
.bk-bar-value { flex: 0 0 4.5em; text-align: right; font-variant-numeric: tabular-nums;
                color: var(--muted); font-size: 13px; }
/* One food's whole history end to end; a tick per shopping trip. Evenly spaced
   ticks are the habit — the eye reads the rhythm faster than the number does. */
.bk-rhythm { width: 100%; min-width: 8em; height: 14px; display: block; }
.bk-rhythm-rule { stroke: var(--border-2); stroke-width: 1; }
.bk-rhythm-tick { stroke: var(--accent); stroke-width: 1.6; }
.kpi .value .unit { font-size: 13px; font-weight: 400; color: var(--muted); }

/* Recipes — cards, hero, ingredients. */
.bk-cards { display: grid; gap: 12px; margin: 14px 0;
            grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr)); }
.bk-card { display: flex; flex-direction: column; gap: 6px; padding: 10px;
           background: var(--surface); border: 1px solid var(--border);
           border-radius: var(--r-lg); color: var(--text); font-size: 13.5px; }
.bk-card:hover { border-color: var(--accent); text-decoration: none; }
.bk-card img { width: 100%; aspect-ratio: 1; object-fit: cover;
               border-radius: var(--r); display: block; }
.bk-card-name { font-weight: 600; line-height: 1.3; }
.bk-hero { width: 100%; max-width: 420px; border-radius: var(--r-lg);
           display: block; margin: 10px 0; }
.bk-ingredients { list-style: none; display: grid; gap: 4px; margin: 8px 0 16px;
                  grid-template-columns: repeat(auto-fill, minmax(min(230px, 100%), 1fr)); }
.bk-ingredients .measure { color: var(--muted); display: inline-block;
                           min-width: 6em; font-variant-numeric: tabular-nums; }
.bk-method { white-space: pre-line; color: var(--text-2); font-size: 14px; }

/* Cook from the shelf: what you have beside what you would still need. */
.bk-cookable h3 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.bk-cookable .bk-bar-track { display: block; margin: 8px 0 14px; }
.bk-haveneed { display: grid; gap: 18px; grid-template-columns: 1fr 1fr; }
/* A `1fr` track has `min-width: auto`, so a child wider than the column at its
   min-content size pushes the TRACK out instead of shrinking — and the .table-wrap
   inside it never gets the chance to scroll, which is the whole reason it is there.
   Found when a five-column price table dragged this grid 36px past a 320px screen.
   Same shape as the `minmax(320px, 1fr)` overflow: a track that cannot shrink below
   its contents is not a responsive track. */
.bk-haveneed > * { min-width: 0; }
.bk-haveneed h4 { font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
                  color: var(--muted-2); margin-bottom: 6px; }
.bk-haveneed ul { list-style: none; display: grid; gap: 3px; font-size: 13.5px; }
.bk-haveneed div:first-child li { color: var(--good); }
@media (max-width: 640px) { .bk-haveneed { grid-template-columns: 1fr; } }

/* Cookable cards lead with the picture — the data always had a thumbnail. */
.bk-cookhead { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 12px; }
.bk-thumb { flex: 0 0 96px; }
.bk-thumb img { width: 96px; height: 96px; object-fit: cover;
                border-radius: var(--r); display: block; }
.bk-cookmeta { flex: 1; min-width: 0; }
.bk-cookmeta h3 { margin-bottom: 2px; }
.bk-noimg { display: flex; align-items: center; justify-content: center;
            aspect-ratio: 1; background: var(--surface-2); border-radius: var(--r);
            font-size: 30px; font-weight: 800; color: var(--muted-2); }
.bk-keep { display: flex; gap: 10px; flex-wrap: wrap; align-items: center;
           margin: 14px 0; }
.bk-keep form { display: flex; gap: 6px; align-items: center; }
@media (max-width: 560px) { .bk-thumb, .bk-thumb img { flex-basis: 64px; width: 64px; height: 64px; } }
