/* Sierra Admin. Shares the storefront palette so the two feel related, but the
   admin is denser and wider -- it is a working tool, not a shopfront. */

.wrap.wide { max-width: 1400px; }

.stats {
  display: grid; gap: .75rem; margin: 1rem 0;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.stat {
  background: #fff; border: 1px solid var(--line); border-radius: 10px;
  padding: .9rem 1rem; display: flex; flex-direction: column; gap: .15rem;
  text-decoration: none; color: inherit;
}
a.stat:hover { border-color: var(--navy); }
.stat-n { font-size: 1.6rem; font-weight: 600; color: var(--navy); line-height: 1.1; }
.stat-l { font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }

.card.attention { border-left: 4px solid #d68910; }

.filters {
  display: flex; align-items: end; gap: .75rem; flex-wrap: wrap;
  background: #fff; border: 1px solid var(--line); border-radius: 10px;
  padding: .85rem 1rem; margin: 1rem 0;
}
.filters label { margin: 0 0 .2rem; font-size: .78rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--muted); }
.filters select, .filters input[type=text] { width: auto; min-width: 190px; }
.filters .btn-primary.inline { margin: 0; padding: .5rem 1.1rem; }

.prices td.num, .prices th.num { text-align: right; font-variant-numeric: tabular-nums; }
.prices tbody tr:hover { background: #f8fafc; }
.prices a { color: var(--navy); }

/* The admin is dense; keep long free-text columns from pushing the table wide. */
.prices td.small { max-width: 420px; overflow: hidden; text-overflow: ellipsis; }

/* Superseded by the sidebar layout: the rail is the only navy surface.
   Left as a comment because its specificity (body.staff .topbar) beat
   .staff-topbar and silently kept the old colour. */

/* ---------------------------------------------------------------------------
   Sidebar layout.

   Modelled on Pressero's admin, which Sierra's staff already know: a dark rail
   on the left, storefronts expanding in place to their own sections. Keeping
   the muscle memory is the point -- this is a tool people use all day, and a
   novel navigation scheme would be a cost, not a feature.

   The site menus are <details>/<summary>, so expanding works with JavaScript
   disabled and needs no state on the server.
--------------------------------------------------------------------------- */

:root {
  --rail: 248px; --rail-bg: #03326A; --rail-line: #1b4a86;
  /* One number drives the logo block and the top bar, so the two
     line up across the fold instead of drifting apart. */
  --header-h: 64px;

  /* The dashboard palette. These are literal values, not references to
     app.css, on purpose: app.css's :root is overwritten per request with the
     customer's own palette (app/theming.py), and the dashboard is
     cross-tenant. Pointing at those would tint the admin UI with whichever
     storefront was looked at last.

     They were used throughout this file before they were ever defined, which
     is silent -- an undefined var() makes the whole declaration compute to
     `unset`, so `background: var(--navy)` just went transparent and the
     dashboard's bars, current-tab pills and buttons rendered white on white.
     tests/test_static_assets.py now fails if that comes back. */
  --navy: #03326A;      /* Sierra navy, matching --rail-bg */
  --green: #6B9338;     /* Sierra green, the accent rule */
  --ink: #1B2733;       /* body text */
  --muted: #5B6B7C;     /* secondary labels */
  --line: #dde4ec;      /* card, table and toolbar rules */
}

body.staff { background: #f4f6f9; }
.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--rail); flex: 0 0 var(--rail);
  background: var(--rail-bg); color: #cfdcea;
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
}
.sidebar-brand {
  display: flex; align-items: center; gap: .6rem;
  padding: .95rem 1rem; border-bottom: 1px solid var(--rail-line);
  position: sticky; top: 0; background: var(--rail-bg); z-index: 1;
}
.sidebar-mark {
  width: 26px; height: 26px; border-radius: 6px; background: #6B9338;
  color: #fff; font-weight: 700; display: grid; place-items: center; font-size: .9rem;
}
.sidebar-title { color: #fff; font-weight: 600; letter-spacing: .01em; }

.sidebar-nav { padding: .5rem 0 2rem; }
.snav {
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  padding: .5rem 1rem; color: #cfdcea; text-decoration: none; font-size: .9rem;
  border-left: 3px solid transparent;
}
.snav:hover { background: #0a4183; color: #fff; }
.snav.is-active { background: #0a4183; color: #fff; border-left-color: #6B9338; }
.snav-count {
  background: #6B9338; color: #16304a; font-size: .72rem; font-weight: 700;
  border-radius: 999px; padding: 0 .4rem; min-width: 1.2rem; text-align: center;
}
.snav-heading {
  margin: 1rem 0 .25rem; padding: 0 1rem; font-size: .68rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em; color: #7f9ab5;
}

.sidebar details.site > summary {
  list-style: none; cursor: pointer; padding: .45rem 1rem;
  border-left: 3px solid transparent; display: block;
}
.sidebar details.site > summary::-webkit-details-marker { display: none; }
.sidebar details.site > summary:hover { background: #0a4183; }
.sidebar details.site[open] > summary { background: #0a4183; border-left-color: #6B9338; }
.site-name { display: block; color: #eaf1f8; font-size: .87rem; }
.site-host { display: block; color: #7f9ab5; font-size: .7rem; }
.snav.sub { padding-left: 1.9rem; font-size: .83rem; color: #a8bed4; }
.snav.sub.visit a { color: #7fb2e0; text-decoration: none; font-size: .83rem; }

.content { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
/* Selectors are body-scoped so they outrank the storefront's .topbar rules,
   which paint white text for a dark bar. */
body.staff .staff-topbar {
  background: #ffffff; color: var(--ink);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 2;
  height: var(--header-h); box-sizing: border-box;
  padding: 0 1.25rem;
}
body.staff .staff-topbar .who {
  color: var(--ink); opacity: 1; font-size: .88rem; font-weight: 500;
}
body.staff .staff-topbar .topnav a,
body.staff .staff-topbar .linklike {
  color: var(--navy); opacity: 1; font-weight: 600; font-size: .88rem;
}
body.staff .staff-topbar .linklike:hover { text-decoration: underline; }
body.staff .staff-topbar .crumb { font-weight: 600; color: var(--ink); }
/* A quiet divider so "Sign out" reads as an action, not part of the name. */
body.staff .staff-topbar .topnav {
  display: flex; align-items: center; gap: .9rem;
}
body.staff .staff-topbar .topnav .who {
  padding-right: .9rem; border-right: 1px solid var(--line);
}
.hamburger {
  display: none; background: none; border: 0; font-size: 1.2rem;
  cursor: pointer; color: var(--ink); padding: 0 .6rem 0 0;
}
body.staff .content .wrap.wide { max-width: 1500px; padding-top: 1.25rem; }

@media (max-width: 900px) {
  .sidebar { position: fixed; z-index: 5; transform: translateX(-100%); transition: transform .18s ease; }
  .sidebar.open { transform: none; }
  .hamburger { display: inline-block; }
}

/* The lockup is dark-on-white, so it needs a white plate to sit on inside the
   navy rail -- reversing it out would need a light-on-dark variant of the file. */
/* Same total height as the top bar (border included), so the green rule under
   the lockup continues the top bar's bottom border in one unbroken line. */
.sidebar-brand {
  height: var(--header-h); box-sizing: border-box;
  /* flex-shrink: 0 is load-bearing, not tidiness. .sidebar is a column
     flexbox, so this is a flex item, and `height` on a flex item is only a
     hypothetical size -- once the nav outgrew the rail (nested per-site
     sections did it) the default flex-shrink:1 squashed this box from 64px to
     44px and cut the top off the lockup. The whole point of --header-h is
     that this stays exactly as tall as the top bar so the green rule runs
     unbroken across the fold. */
  flex: 0 0 var(--header-h);
  padding: 0 16px; background: #ffffff;
  border-bottom: 3px solid var(--green, #6B9338);
  display: flex; align-items: center;
}
.sidebar-logo { display: block; line-height: 0; }
.sidebar-logo img { display: block; width: 168px; height: auto; }

/* ---------------------------------------------------------------------------
   Stock.

   Colour is a hint, never the message: an out-of-stock row also says "out" in
   text, because a tinted row is invisible to anyone who cannot distinguish the
   tint and disappears entirely in a printed copy of the screen. */
.prices tr.row-low  { background: #fffaf0; }
.prices tr.row-out  { background: #fdf2f0; }
.tag {
  display: inline-block; margin-left: .4rem; padding: 0 .4rem;
  border-radius: 999px; font-size: .68rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .03em; vertical-align: middle;
}
.tag-low { background: #fff0d6; color: #7a4b00; }
.tag-out { background: #fbdcd7; color: #8c1d18; }

.inline-form { display: flex; gap: .35rem; align-items: center; }
.inline-form input[type=text] { width: auto; padding: .3rem .45rem; font-size: .85rem; }
.btn-small {
  padding: .32rem .7rem; border: 1px solid var(--navy); border-radius: 5px;
  background: var(--navy); color: #fff; font: inherit; font-size: .82rem;
  font-weight: 600; cursor: pointer;
}
.btn-small:hover { background: #00294f; }
.inline-check {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .85rem; text-transform: none; letter-spacing: 0; color: var(--ink);
}
.inline-check input { width: auto; }

/* Catalogue rows. A thumbnail is the fastest way to confirm you are looking at
   the right product, which is most of what this screen is for. */
.thumb-cell { width: 52px; padding-right: 0; }
.row-thumb {
  width: 42px; height: 42px; object-fit: contain; display: block;
  background: #fff; border: 1px solid var(--line); border-radius: 4px;
}
.row-thumb.lg { width: 120px; height: 120px; }
.prices th { white-space: nowrap; }

/* Pagination and sorting. The row range and total are the point: a list that
   silently shows the first N looks identical to a complete one. */
.pager {
  display: flex; flex-wrap: wrap; align-items: center; gap: .45rem;
  margin: 1rem 0 0; font-size: .875rem;
}
.pager a {
  padding: .25rem .55rem; border: 1px solid var(--line); border-radius: 5px;
  color: var(--navy); text-decoration: none;
}
.pager a:hover { border-color: var(--navy); background: #f4f8fc; }
.pager-now {
  padding: .25rem .55rem; border: 1px solid var(--navy); border-radius: 5px;
  background: var(--navy); color: #fff; font-weight: 700;
}
.pager-info { color: #5b6b7c; margin-right: .35rem; }
.pager-size { margin-left: auto; color: #5b6b7c; }
.pager-size a { padding: .15rem .4rem; }

.prices th.is-sorted { background: #eef4fb; }
.prices th a { color: inherit; text-decoration: none; display: inline-block; }
.prices th a:hover { text-decoration: underline; }
.sort-arrow { margin-left: .3rem; font-size: .7rem; }

/* Storefront switcher. Links rather than a <select> so it works with no
   script -- the dropdown it replaced used an inline onchange, which the CSP
   blocks, so it silently did nothing. */
.site-tabs {
  display: flex; flex-wrap: wrap; gap: .35rem;
  margin: 0 0 1.1rem; padding-bottom: .55rem;
  border-bottom: 1px solid var(--line);
}
.site-tab {
  padding: .38rem .8rem; border: 1px solid var(--line); border-radius: 6px;
  color: var(--navy); text-decoration: none; font-size: .9rem; font-weight: 600;
  background: #fff;
}
.site-tab:hover { border-color: var(--navy); background: #f4f8fc; }
.site-tab.is-current {
  background: var(--navy); border-color: var(--navy); color: #fff;
}

/* Count beside a tab label, e.g. Detail 3. Inherits colour from the tab so it
   stays legible when the tab inverts to navy on white. */
.tab-count {
  display: inline-block; margin-left: .3rem; padding: 0 .35rem;
  border-radius: 999px; font-size: .78rem; font-weight: 700;
  background: rgba(3, 50, 106, .1); color: inherit;
}
.site-tab.is-current .tab-count { background: rgba(255, 255, 255, .22); }

/* Status history on the order screen's Status tab. Open, not a <details>:
   there the history is the content, unlike the Detail tab where it was a
   footnote inside a cell. */
.history-list { margin: 0; padding-left: 1rem; }
.history-list li { margin: .15rem 0; }

/* Tree indentation as a spacer element: padding-left on the cell would also
   indent the thumbnail column and misalign every row. */
.cat-indent { display: inline-block; width: calc(var(--depth) * 1.35rem); }
.cat-branch { color: #9fb0c2; margin-right: .15rem; }
.cat-table td { vertical-align: middle; }

.site-picker {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin: 0 0 .6rem;
}
.site-picker label {
  margin: 0; font-size: .78rem; text-transform: uppercase;
  letter-spacing: .04em; color: #5b6b7c;
}
.site-picker select { width: auto; min-width: 230px; }
.site-count { color: #5b6b7c; font-size: .82rem; margin-left: .2rem; }

.check-cell { width: 34px; text-align: center; }
.check-cell input { width: auto; margin: 0; }
.bulk-bar {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin: .75rem 0 0; padding: .7rem .85rem;
  background: #f4f8fc; border: 1px solid var(--line); border-radius: 8px;
}
.bulk-bar label {
  margin: 0; font-size: .78rem; text-transform: uppercase;
  letter-spacing: .04em; color: #5b6b7c;
}
.bulk-bar select { width: auto; min-width: 240px; }

/* Status tabs with counts. The count is the point: "Pending Approval 7" tells
   you there is work stalled at a customer without clicking anything. */
.status-tabs {
  display: flex; flex-wrap: wrap; gap: .35rem; margin: 0 0 .9rem;
}
.status-tab {
  padding: .35rem .75rem; border: 1px solid var(--line); border-radius: 999px;
  color: var(--navy); text-decoration: none; font-size: .875rem; font-weight: 600;
  background: #fff;
}
.status-tab:hover { border-color: var(--navy); background: #f4f8fc; }
.status-tab.is-current { background: var(--navy); border-color: var(--navy); color: #fff; }
.status-n {
  display: inline-block; margin-left: .3rem; padding: 0 .35rem;
  border-radius: 999px; background: #eef2f6; color: #35485c;
  font-size: .75rem; font-weight: 700;
}
.status-tab.is-current .status-n { background: rgba(255,255,255,.22); color: #fff; }
/* Orders waiting on a customer's approver have not reached Sierra at all. */
.status-tab.tab-pending_approval .status-n { background: #ffe6b8; color: #6b4300; }
.clear-link { align-self: center; font-size: .85rem; }

/* An empty work queue is the right answer, so it should look settled rather
   than like a page that failed to load. */
.queue-clear { border-left: 4px solid #6B9338; }
.queue-clear h2 { color: #3f5c1f; }
.card.attention { border-left: 4px solid #f0a202; }

/* ---------------------------------------------------------------------------
   Dashboard: reporting. Charts are inline SVG -- the CSP forbids external
   scripts, and one line plus four bar charts does not justify vendoring a
   library. */
.dash-head {
  display: flex; justify-content: space-between; align-items: flex-start;
  flex-wrap: wrap; gap: .75rem;
}
.period-tabs { display: flex; flex-wrap: wrap; gap: .3rem; }
.period-tab {
  padding: .3rem .7rem; border: 1px solid var(--line); border-radius: 999px;
  color: var(--navy); text-decoration: none; font-size: .82rem; font-weight: 600;
  background: #fff;
}
.period-tab:hover { border-color: var(--navy); background: #f4f8fc; }
.period-tab.is-current { background: var(--navy); border-color: var(--navy); color: #fff; }

.delta {
  font-size: .72rem; font-weight: 700; padding: .05rem .35rem;
  border-radius: 999px; background: #eef2f6; color: #35485c; vertical-align: middle;
}
.delta.up { background: #e4f2dc; color: #3f5c1f; }
.delta.down { background: #fbe4e2; color: #8c1d18; }
.stat-sub { font-size: .8rem; font-weight: 600; color: #5b6b7c; }

.chart { width: 100%; height: auto; display: block; }
.chart .grid { stroke: #e3eaf1; stroke-width: 1; }
.chart .axis { fill: #7e8fa0; font-size: 11px; }
.chart .area { fill: rgba(3,50,106,.10); }
.chart .line { fill: none; stroke: var(--navy); stroke-width: 2;
               stroke-linejoin: round; stroke-linecap: round; }
.chart .dot { fill: var(--navy); }
.chart-ends { display: flex; justify-content: space-between; margin: .2rem 0 0; }

/* Each bar states its value in text: a chart whose only content is bar length
   cannot be read from a printout or by a screen reader. */
.barlist { list-style: none; margin: 0; padding: 0; }
.barlist li {
  display: grid; grid-template-columns: minmax(6rem, 11rem) 1fr auto;
  align-items: center; gap: .6rem; padding: .22rem 0;
}
.bar-label {
  font-size: .85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bar-track { background: #eef2f6; border-radius: 3px; height: .8rem; overflow: hidden; }
.bar-fill { display: block; height: 100%; background: var(--navy); border-radius: 3px; }
.bar-value { font-size: .82rem; font-variant-numeric: tabular-nums; color: #35485c; }
@media (max-width: 720px) {
  .barlist li { grid-template-columns: 1fr auto; }
  .bar-track { grid-column: 1 / -1; }
}

/* ---------------------------------------------------------------------------
   Staff access (admin groups)

   The permission grid is a plain table of radios rather than a tree widget.
   Pressero's version is a collapsible tree, which looks tidier and hides the
   thing you need to see: that a Denied anywhere beats an Allowed everywhere.
   Nine rows fit on one screen, so there is nothing to collapse.
--------------------------------------------------------------------------- */

.back {
  display: inline-block; margin-bottom: .5rem;
  color: var(--navy); text-decoration: none; font-size: .85rem; font-weight: 600;
}
.back:hover { text-decoration: underline; }

.check { display: flex; align-items: center; gap: .45rem; font-size: .88rem;
         padding: .18rem 0; cursor: pointer; }
.check input { width: auto; margin: 0; }

.checkgrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: .1rem .9rem; margin: .5rem 0 .2rem;
}

.perms td { vertical-align: top; }
.perms thead th:not(:first-child) { text-align: center; width: 90px; }
.perms tbody td:not(:first-child) { text-align: center; }
.perms input[type=radio] { width: auto; margin: 0; }

.actions { display: flex; align-items: center; gap: .6rem; margin: 1rem 0; }

.card.warn { border-left: 3px solid #C77700; }

.danger { margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--line);
          display: flex; align-items: center; gap: .7rem; }
.btn-danger {
  padding: .32rem .7rem; border: 1px solid #B3261E; border-radius: 5px;
  background: #fff; color: #B3261E; font: inherit; font-size: .82rem;
  font-weight: 600; cursor: pointer;
}
.btn-danger:hover { background: #B3261E; color: #fff; }

/* Visible to screen readers only. The permission radios are identified by
   their column header visually, which a screen reader does not convey. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Second level inside a storefront: Catalog, User Management.

   Mirrors Pressero's nested per-site menu. Nested <details> rather than a
   JavaScript accordion, for the same reason as the first level -- the CSP
   forbids inline script, and a disclosure widget does not justify a bundle. */
.sidebar details.subsection > summary {
  list-style: none; cursor: pointer; padding: .35rem 1rem .35rem 1.9rem;
  font-size: .83rem; color: #a8bed4; position: relative;
}
.sidebar details.subsection > summary::-webkit-details-marker { display: none; }
.sidebar details.subsection > summary:hover { color: #eaf1f8; }
/* A caret, so a closed section reads as "there is more in here" rather than
   as a dead label. */
.sidebar details.subsection > summary::after {
  content: "\203A"; position: absolute; left: 1.15rem; top: .3rem;
  transition: transform .12s ease; display: inline-block;
}
.sidebar details.subsection[open] > summary::after { transform: rotate(90deg); }
.sidebar details.subsection[open] > summary { color: #eaf1f8; }

.snav.sub2 {
  padding-left: 2.7rem; font-size: .81rem; color: #a8bed4;
}
.snav.sub2.is-active { background: #0a4183; color: #fff; border-left-color: #6B9338; }

/* A permission that is recorded from Pressero but that this app does not act
   on yet -- budgets, assets, customer-side user admin. Muted rather than
   hidden: dropping them would lose exactly what someone needs when they build
   the feature, but showing them as live would be a lie. */
.tag-idle {
  background: #eef1f5; color: #5B6B7C; border: 1px dashed #c4ced9;
}

/* A permission row this app does not read yet. Dimmed, not hidden: someone
   setting the switch deserves to know it changes nothing today. */
.perms tr.inert td:first-child { color: var(--text-muted); }

/* Pressero puts an edit pencil on every grid row and staff are used to
   reaching for it; the row's main link goes where the row is about. */
.edit-pencil {
  color: var(--muted); text-decoration: none; margin-left: .4rem;
  font-size: .9rem; opacity: .55;
}
.edit-pencil:hover { color: var(--navy); opacity: 1; }

/* Hovering a day's whole column, not the 5px dot. Invisible until hovered,
   then a faint band so the pointer has something to confirm against while the
   native tooltip appears. */
.chart .hover-band { fill: transparent; cursor: default; }
.chart .hover-band:hover { fill: rgba(3, 50, 106, .06); }
/* The dot is decoration; the band above it owns the pointer. */
.chart .dot { pointer-events: none; }

/* Readiness checklist. A tick is not enough on its own -- the row also states
   the count, so "done" is checkable rather than asserted. */
.ready-yes { color: #4b8b2b; font-weight: 700; }
.ready-no  { color: #C77700; font-weight: 700; font-size: 1.2rem; }

/* Per-line production status. Compact because it sits in a table cell that
   also has to show the history without pushing the row open. */
.inline-status { display: flex; gap: .3rem; align-items: center; }
.inline-status select.mini { min-width: 0; width: auto; font-size: .8rem; padding: .15rem .3rem; }
.history > summary { cursor: pointer; font-size: .75rem; color: var(--muted); margin-top: .25rem; }
.history ul { margin: .3rem 0 0; padding-left: .9rem; font-size: .75rem; }
.history li { margin-bottom: .2rem; }

/* The imported-but-not-launched storefronts, folded away in the sidebar.
   Dimmer than the live ones on purpose: they are configurable but nothing
   answers on their hostname yet, and a staff member should be able to tell
   the two apart at a glance rather than by reading the name. */
.site-pending > summary {
  display: flex; flex-direction: column; gap: .1rem;
  padding: .5rem .9rem; cursor: pointer; color: #9fb6cf;
  border-top: 1px solid rgba(255, 255, 255, .12); margin-top: .3rem;
}
.site-pending > summary:hover { color: #fff; }
.site-pending > summary .site-host { font-size: .74rem; opacity: .75; }
.site-pending .site > summary { opacity: .72; }
.site-pending .site > summary:hover { opacity: 1; }
