:root {
  color-scheme: light;
  --paper: #EAE1C6;
  --card: #F6F1DE;
  --ink: #23301F;
  --ink-muted: #5B6B4E;
  --leaf: #3F6B3A;
  --rust: #9C4A3C;
  --copper: #A6633B;
  --border: #C9BD98;

  /* shared status accents: green = ready to sell, blue = shipped, magenta = sold.
     Each color means exactly one state, everywhere - a batch still being
     provisioned (unsold, incomplete) is a different state from "shipped" and
     deliberately does not share blue with it; it gets copper instead.
     Used for the dashboard stat tiles and reused for the matching status badges
     everywhere else (batches, orders) so the same state is always the same color.
     RGB triplets alongside so the tile fills can use them at alpha 0.2. */
  --stat-green: #1B8A4A;
  --stat-green-rgb: 27, 138, 74;
  --stat-blue: #1D4ED8;
  --stat-blue-rgb: 29, 78, 216;
  --stat-magenta: #C2007A;
  --stat-magenta-rgb: 194, 0, 122;

  /* rgb variants of the base palette, for tinted tile fills that reuse the
     same claimed/unclaimed/revoked colors as the badges below */
  --ink-muted-rgb: 91, 107, 78;
  --leaf-rgb: 63, 107, 58;
  --rust-rgb: 156, 74, 60;

  /* One dedicated meaning: a 0-selling-price point (trial/test tags) -
     never reused for anything else, same rule as the status accents above. */
  --trial: #A67C00;
  --trial-rgb: 166, 124, 0;

  /* legacy aliases still referenced by templates/other rules */
  --muted: var(--ink-muted);
  --error: var(--rust);
  --success: var(--leaf);
}

* { box-sizing: border-box; }

body {
  font-family: "IBM Plex Sans", -apple-system, system-ui, sans-serif;
  background: var(--paper);
  color: var(--ink);
  margin: 0;
  line-height: 1.5;
}

a { color: var(--leaf); }

nav {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  padding: 0.85rem 1.5rem;
  background: var(--card);
  border-bottom: 2px solid var(--ink);
}

nav a {
  color: var(--ink);
  text-decoration: none;
  font-size: 0.95rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
}
nav a:hover { color: var(--leaf); }

/* Icons (Lucide, inlined via app/templates/admin/_icons.html) - neutral ink
   in the nav (these are category labels, not status), sized to sit on the
   text baseline rather than dominate it. */
.icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
nav .brand {
  font-family: "Allerta Stencil", "IBM Plex Sans", sans-serif;
  font-size: 1.3rem;
  letter-spacing: 0.03em;
  color: var(--ink);
}
.brand-logo { height: 28px; display: block; }

.login-logo { max-height: 64px; max-width: 280px; display: block; margin-bottom: 0.5rem; }
nav .spacer { flex: 1; }
nav .muted { color: var(--ink-muted); font-size: 0.88rem; }

main {
  max-width: 1320px;
  margin: 0 auto;
  padding: 2rem 1.5rem 4rem;
}

h1 {
  font-family: "Allerta Stencil", "IBM Plex Sans", sans-serif;
  font-weight: 400;
  font-size: 2rem;
  letter-spacing: 0.01em;
  margin: 0 0 0.3rem;
}

h2 {
  font-size: 1rem;
  font-weight: 600;
  margin: 2rem 0 0.75rem;
  padding-bottom: 0.4rem;
  border-bottom: 2px solid var(--ink);
}

h2.no-rule { border-bottom: none; margin: 0 0 0.4rem; padding-bottom: 0; font-size: 1.05rem; }

.table-wrap {
  overflow-x: auto;
  margin: 1rem 0;
}

table {
  width: 100%;
  border-collapse: collapse;
  background: var(--card);
}

td code, th code { white-space: nowrap; }

th, td {
  text-align: left;
  padding: 0.55rem 0.7rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.92rem;
}

th {
  color: var(--ink-muted);
  font-weight: 600;
  font-size: 0.82rem;
  border-bottom: 2px solid var(--ink);
}

th.sortable {
  cursor: pointer;
  user-select: none;
}
th.sortable:hover { color: var(--ink); }
th.sortable::after {
  content: "\2195";
  display: inline-block;
  margin-left: 0.4em;
  opacity: 0.4;
  font-size: 0.9em;
}
th.sortable.sorted-asc::after { content: "\2191"; opacity: 1; }
th.sortable.sorted-desc::after { content: "\2193"; opacity: 1; }

code {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  background: rgba(35, 48, 31, 0.08);
  padding: 0.1rem 0.35rem;
  border-radius: 3px;
  font-size: 0.88em;
}

form.stacked label {
  display: block;
  margin: 0.9rem 0 0.25rem;
  font-weight: 600;
}

.hint { color: var(--ink-muted); font-size: 0.85em; }

form.stacked .hint {
  display: block;
  font-weight: 400;
  color: var(--ink-muted);
  font-size: 0.85em;
  margin-top: 0.15rem;
}

input[type=text], input[type=email], input[type=password], input[type=number], select, textarea {
  width: 100%;
  max-width: 420px;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 3px;
  background: var(--card);
  color: var(--ink);
  font-size: 1rem;
  font-family: inherit;
}

input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--leaf);
  outline-offset: 2px;
}

button, .button {
  display: inline-block;
  margin-top: 1rem;
  padding: 0.55rem 1.1rem;
  border: 1.5px solid var(--ink);
  background: var(--ink);
  color: var(--paper);
  border-radius: 3px;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  text-decoration: none;
}

button.secondary, .button.secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink);
}

button.danger, .button.danger {
  background: transparent;
  color: var(--rust);
  border-color: var(--rust);
}

button:disabled {
  cursor: default;
  opacity: 0.4;
}

.inline-form { display: inline; margin-right: 0.4rem; }

/* Featured-photo preview carousel - shared by the Dashboard hero and the
   Featured page. A flat photo tray with a solid caption strip underneath
   (deliberately not a gradient scrim over the image), plus the same
   cut-corner + punch-hole accent used on the stat tiles, tying it back to
   the rest of the system instead of reading as a bolted-on slider widget. */
.carousel {
  position: relative;
  overflow: hidden;
  border: 1.5px solid var(--ink);
  background: var(--card);
  margin: 1.5rem 0;
}

.carousel::before {
  content: "";
  position: absolute;
  top: 11px;
  left: -7px;
  width: 34px;
  height: 2px;
  background: var(--ink);
  transform: rotate(-42deg);
  transform-origin: left center;
  z-index: 2;
}
.carousel::after {
  content: "";
  position: absolute;
  top: 11px;
  left: 13px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--ink);
  background: var(--paper);
  z-index: 2;
}

.carousel-stage {
  position: relative;
  width: 100%;
  height: 200px;
  background: var(--paper);
}
.carousel.carousel-large .carousel-stage { height: 380px; }

.carousel-slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1s ease;
}
.carousel-slide.is-active { opacity: 1; }

.carousel-caption {
  display: flex;
  align-items: baseline;
  gap: 0.65rem;
  padding: 0.7rem 1rem;
  background: var(--ink);
  border-top: 1.5px solid var(--ink);
}
.carousel-plant { color: var(--paper); font-weight: 600; font-size: 1rem; }
.carousel-owner { color: rgba(234, 225, 198, 0.65); font-size: 0.85rem; }
.carousel-count {
  margin-left: auto;
  color: rgba(234, 225, 198, 0.65);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 0.8rem;
}

.carousel-empty {
  padding: 2.5rem 1rem;
  text-align: center;
  color: var(--ink-muted);
}
.carousel-empty p { margin: 0.2rem 0; }
.carousel.carousel-large .carousel-empty { padding: 4.5rem 1rem; }

@media (prefers-reduced-motion: reduce) {
  .carousel-slide { transition: none; }
}

.photo-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  margin: 1rem 0 1.5rem;
}
.photo-grid figure { margin: 0; width: 160px; }
.photo-grid img {
  width: 100%;
  height: 120px;
  object-fit: cover;
  border: 1.5px solid var(--ink);
  display: block;
  background: var(--card);
}
.photo-grid figcaption { font-size: 0.8rem; color: var(--ink-muted); margin-top: 0.3rem; }

.errors { color: var(--rust); font-size: 0.85em; margin: 0.15rem 0 0; padding-left: 1rem; }

ul.flashes { list-style: none; padding: 0; margin: 0 0 1.25rem; }
.flash {
  padding: 0.6rem 0.8rem;
  border-left: 3px solid;
  margin-bottom: 0.5rem;
  font-size: 0.92rem;
  background: var(--card);
}
.flash.error { border-color: var(--rust); color: var(--rust); }
.flash.success { border-color: var(--leaf); color: var(--leaf); }

/* --- Dashboard hero --------------------------------------------------- */

.hero { margin-bottom: 1.75rem; }
.hero .tagline { color: var(--ink-muted); font-size: 0.95rem; }

/* --- Stat tiles: shaped like a hang-tag, since that's the product ----- */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 1rem;
  margin: 1.25rem 0 1.75rem;
}

.stat {
  position: relative;
  overflow: hidden; /* clips the corner accent below to the box's own border, so the
                        border itself is a plain rectangle and every edge always renders */
  background: rgba(var(--stat-green-rgb), 0.2);
  border: 1.5px solid var(--ink);
  border-left: 4px solid var(--stat-green);
  padding: 1.1rem 1.1rem 1rem 2.3rem;
}

.stat.stat-shipped { background: rgba(var(--stat-blue-rgb), 0.2); border-left-color: var(--stat-blue); }
.stat.stat-sold { background: rgba(var(--stat-magenta-rgb), 0.2); border-left-color: var(--stat-magenta); }

/* Same color mapping as the status badges: claimed = leaf (success), revoked
   = rust (error). Unclaimed isn't a color, it's the absence of one - a plain
   neutral tile, same idea as the "pending" badge - so it can't be confused
   with claimed's green at a glance the way two pale green tints would be.
   "All" is neutral too (it isn't a status), but anchored with the strong ink
   border so it doesn't read as just a fainter unclaimed tile. */
.stat.stat-all { background: var(--card); border-left-color: var(--ink); }
.stat.stat-unclaimed { background: var(--card); border-left-color: var(--border); }
.stat.stat-claimed { background: rgba(var(--leaf-rgb), 0.2); border-left-color: var(--leaf); }
.stat.stat-revoked { background: rgba(var(--rust-rgb), 0.2); border-left-color: var(--rust); }

/* Stat tiles that double as filter links get a physical "pick me up" cue -
   a hard offset shadow like a paper tag sitting slightly proud of the page,
   consistent with the flat cut-paper look (a soft blurred shadow would not
   be). The active filter's tile is flush/pressed-in instead, so at a glance
   the raised tiles read as pressable and the flat one reads as "selected". */
.stat-grid-hint { margin: 0.25rem 0 0.7rem; }

a.stat {
  display: block;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  box-shadow: 3px 3px 0 rgba(35, 48, 31, 0.18);
  transition: box-shadow 0.12s ease, transform 0.12s ease;
}
a.stat:hover {
  box-shadow: 4px 4px 0 rgba(35, 48, 31, 0.28);
  transform: translate(-1px, -1px);
}
a.stat.is-active {
  box-shadow: none;
  transform: translate(3px, 3px);
  border-left-width: 7px;
}
.stat.is-active .value { font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  a.stat { transition: none; }
}

/* a cut-corner accent line, purely decorative - contained by overflow:hidden
   above so it can never leave a gap in the tile's real border */
.stat::before {
  content: "";
  position: absolute;
  top: 11px;
  left: -7px;
  width: 34px;
  height: 2px;
  background: var(--ink);
  transform: rotate(-42deg);
  transform-origin: left center;
}

.stat::after {
  content: "";
  position: absolute;
  top: 11px;
  left: 13px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--ink);
  background: var(--paper);
}

.stat .value {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 2rem;
  font-weight: 500;
  line-height: 1.1;
}
.stat .label {
  display: flex;
  align-items: center;
  gap: 0.4em;
  color: var(--ink-muted);
  font-size: 0.85rem;
  margin-top: 0.2rem;
}
.stat .label .icon { width: 18px; height: 18px; }
.stat-ready .label .icon { color: var(--stat-green); }
.stat-shipped .label .icon { color: var(--stat-blue); }
.stat-sold .label .icon { color: var(--stat-magenta); }

/* Filter bar: a text search plus a status dropdown that combine (both are
   applied together), distinct from the stat tiles above which are one-click
   shortcuts to a single status. */
.filter-bar {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin: 1.25rem 0 0.5rem;
}
.filter-bar .field {
  display: flex;
  flex-direction: column;
  flex: 1 1 260px;
}
.filter-bar .field-narrow { flex: 0 1 180px; }
.filter-bar label {
  font-weight: 600;
  font-size: 0.85rem;
  margin-bottom: 0.3rem;
}
.filter-bar input, .filter-bar select {
  max-width: none;
  margin: 0;
}
.filter-bar button { margin: 0; }
.filter-bar .clear-filters {
  align-self: center;
  font-size: 0.85rem;
  color: var(--ink-muted);
}
.filter-bar .clear-filters:hover { color: var(--rust); }

.result-count {
  color: var(--ink-muted);
  font-size: 0.85rem;
  margin: 0.75rem 0 0;
}

.qr-panel {
  display: flex;
  align-items: flex-start;
  gap: 1.25rem;
  margin: 1rem 0 1.5rem;
  flex-wrap: wrap;
}
.qr-panel img {
  width: 220px;
  height: 220px;
  border: 1.5px solid var(--ink);
  background: var(--card);
  display: block;
}
.qr-panel-info { flex: 1 1 260px; min-width: 240px; }
.qr-panel-info .button { margin-top: 0.5rem; margin-right: 0.6rem; }
.qr-panel-info .button:last-child { margin-right: 0; }

.claim-url-box {
  border: 1px solid var(--border);
  background: var(--card);
  border-radius: 3px;
  padding: 0.8rem;
  margin: 0.75rem 0 1.25rem;
  word-break: break-all;
}

.badge {
  display: inline-block;
  padding: 0.12rem 0.5rem;
  border-radius: 2px;
  font-size: 0.78rem;
  font-weight: 600;
  border: 1.5px solid var(--border);
}
.badge.unclaimed { color: var(--ink-muted); }
.badge.claimed { color: var(--leaf); border-color: var(--leaf); }
.badge.revoked { color: var(--rust); border-color: var(--rust); }
.badge.shipped { color: var(--stat-blue); border-color: var(--stat-blue); }

/* same color mapping as the dashboard stat tiles */
.badge.unsold { color: var(--stat-green); border-color: var(--stat-green); }
.badge.unsold.in-progress { color: var(--copper); border-color: var(--copper); }
.badge.sold { color: var(--stat-magenta); border-color: var(--stat-magenta); }

/* Featured-photo moderation: same good/bad/undecided convention as
   claimed/revoked/unclaimed above, applied to a different queue */
.badge.pending { color: var(--ink-muted); }
.badge.approved { color: var(--leaf); border-color: var(--leaf); }
.badge.rejected { color: var(--rust); border-color: var(--rust); }

/* A 0-selling-price point (trial/test tags) - a dashed border marks it as
   an annotation layered on top of a batch/tag/price, not a lifecycle
   status like the solid-border badges above. */
.badge.trial {
  color: var(--trial);
  border-color: var(--trial);
  border-style: dashed;
  margin-left: 0.35em;
}

/* --- Financial dashboard: P&L table, monthly breakdown, exports ----- */

.pl-statement { margin: 1.25rem 0; }
.pl-statement table { max-width: 560px; }
.pl-statement .pl-total td, .pl-statement .pl-total th { border-top: 2px solid var(--ink); font-weight: 600; }
.pl-statement .pl-subtotal td, .pl-statement .pl-subtotal th { font-weight: 600; }
.pl-statement td.amount { text-align: right; font-family: "IBM Plex Mono", ui-monospace, monospace; }
.pl-statement .negative, td.negative { color: var(--rust); }

/* A step down from h2 - "Profit & loss", "Monthly breakdown", "Total
   report" are subsections of the one "Financial dashboard" h2 above them,
   not peer sections of their own, so they get a lighter rule than h2's
   full underline instead of competing with it. */
.section-title {
  font-size: 1rem;
  font-weight: 600;
  margin: 1.75rem 0 0.4rem;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid var(--border);
}

/* A row of buttons that belongs to the section-title right above it - own
   paragraph, left-aligned, same placement pattern as "New batch"/"Add a
   cost" elsewhere, instead of floating a button off to the side of a
   heading (which reads as unanchored rather than clearly "part of" it). */
.section-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; margin: 0 0 1rem; }
.section-actions .button { margin-top: 0; }

/* Simple two-bar-per-month visualizer: a revenue bar and a total-cost bar,
   both scaled against the same table-wide max, so a glance across the
   column shows both trend and, within a row, whether that month made or
   lost money - no axes or legend needed beyond the one line in the header. */
.month-bar-cell { min-width: 130px; }
.month-bar {
  height: 7px;
  background: rgba(35, 48, 31, 0.08);
  border-radius: 1px;
  overflow: hidden;
  margin-bottom: 3px;
}
.month-bar:last-child { margin-bottom: 0; }
.month-bar-fill { height: 100%; }
.month-bar-revenue .month-bar-fill { background: var(--stat-green); }
.month-bar-cost .month-bar-fill { background: var(--rust); }

.report-links { white-space: nowrap; font-size: 0.85rem; }
.report-links a { margin-right: 0.6rem; }
.report-links a:last-child { margin-right: 0; }

.data-quality-note { color: var(--rust); }
.trial-note { color: var(--trial); }
.trial-note .badge.trial { margin-left: 0; margin-right: 0.4em; }

.featured-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1.25rem;
  margin: 1.5rem 0;
}

.featured-card {
  border: 1.5px solid var(--ink);
  background: var(--card);
}
.featured-card img {
  width: 100%;
  height: 180px;
  object-fit: cover;
  display: block;
  border-bottom: 1.5px solid var(--ink);
}
.featured-card-body { padding: 0.9rem; }
.featured-card-body p { margin: 0.3rem 0; font-size: 0.9rem; }
.featured-card-body form { margin-top: 0.75rem; }
.featured-card-body button { margin-top: 0; }

@media (prefers-reduced-motion: no-preference) {
  a, button, .button { transition: color 0.12s ease, border-color 0.12s ease; }
}
