/* Hand-written additions to Bootstrap 5.3. Kept deliberately small: if Bootstrap has
   a component for it, use Bootstrap's. No build step — edit this file and reload. */

/* ── Corporate identity ─────────────────────────────────────────────────────────
   Colours and fonts from the brand guidelines, applied here and nowhere else — every
   template inherits this through base.html without a per-page override. Bootstrap's
   own semantic colours (green/red/amber for success/danger/warning) are still left
   alone for genuine success/danger/warning UI — payment status, form errors, and the
   like. The one deliberate exception is the state-pill palette below: quote and
   delivery state are the two things this whole app is organised around, so they get
   their own named palette instead of borrowing Bootstrap's four generic hues,
   navy/turquoise included for delivery state specifically — see that section for why. */

/* Mada and Open Sans, self-hosted rather than linked live from fonts.googleapis.com.
   The live <link> was the ask; this project's own no-CDN-calls rule has held for every
   other asset since M0 (see VERSIONS.md), and a live Google Fonts request is exactly
   the dependency that rule exists to avoid, so it was flagged rather than silently
   picked either way. Both are variable fonts — one file per family covers the whole
   400–700 range used below. font-display: swap shows the fallback stack immediately
   and re-flows once the real font is ready, rather than an invisible-text flash. */
@font-face {
  font-family: "Mada";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../vendor/fonts/mada/mada-variable-latin.b74d702a0d2c.woff2") format("woff2");
}
@font-face {
  font-family: "Open Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../vendor/fonts/open-sans/open-sans-variable-latin.dcf31ebe1074.woff2") format("woff2");
}

:root {
  /* Primary: dark navy. Drives the navbar (below), --bs-primary (so every Bootstrap
     utility that reads it — .text-bg-primary, .border-primary, .text-primary — follows
     automatically), and headings via --bs-heading-color. .btn-primary is overridden
     separately below because Bootstrap bakes its component colours as literal hex, not
     a reference to --bs-primary. */
  --wr-brand: #384053;
  --wr-brand-dark: #262c3a;
  /* Accent: turquoise. Links and the active-tab indicator, not the workhorse buttons —
     see .btn-primary below for why those stay navy. */
  --wr-accent: #46c0bc;
  --wr-accent-dark: #359996;
  /* The off-white canvas the white content blocks (cards, tab panels, the totals
     block) sit on top of — barely-there contrast against the pure white those blocks
     keep, so a block reads as a distinct container rather than blending into the page. */
  --wr-page-bg: #f4f5f6;
  /* The third tone: sub-sections nested inside a white block, and inline code. */
  --wr-panel-nested: #f9fafb;

  /* State-pill palette — see the dedicated section below for the classes that use
     these. Quote-state hues that aren't already a brand token; delivery state reuses
     --wr-brand/--wr-accent/--wr-accent-dark directly instead of repeating their hex
     here. */
  --wr-pill-grey: #9ca3af;
  --wr-pill-blue: #2563eb;
  --wr-pill-green: #16a34a;
  --wr-pill-red: #dc2626;
  --wr-pill-amber: #d97706;
  --wr-pill-dark-grey: #4b5563;
  /* Navy at a 20% tint (mixed toward white), for Pre-planning — computed, not
     eyeballed, so it stays traceable back to --wr-brand if that ever changes. */
  --wr-pill-navy-tint: #d7d9dd;
  /* The two text colours every pill picks between. Not pure #000/#fff: #111827 clears
     WCAG AA (>= 4.5:1) against every light pill above, including the two closest
     calls (Accepted green, Active dark teal) — --wr-brand-dark does not quite, on
     those two, so this is its own token rather than a reach for the nearest existing
     dark colour. */
  --wr-pill-dark-text: #111827;
  --wr-pill-light-text: #fff;

  --wr-font-heading: "Mada", "Segoe UI", system-ui, -apple-system, sans-serif;
  --wr-font-body: "Open Sans", "Segoe UI", system-ui, -apple-system, sans-serif;

  /* Bootstrap variables this identity actually rides on. --bs-body-bg is deliberately
     NOT touched here — .card and .table-sm both resolve their background from it
     (var(--bs-card-bg): var(--bs-body-bg) in Bootstrap's own source), so leaving it at
     Bootstrap's default white is what keeps every content block white for free. The
     page canvas becomes off-white via a direct rule on body below instead. */
  --bs-primary: var(--wr-brand);
  --bs-primary-rgb: 56, 64, 83;
  --bs-link-color-rgb: 70, 192, 188;
  --bs-link-hover-color-rgb: 53, 153, 150;
  --bs-body-font-family: var(--wr-font-body);
  --bs-body-color: #000;
  --bs-heading-color: var(--wr-brand);
}

/* ── State pills ───────────────────────────────────────────────────────────────────
   One class per quote_state / delivery_state value (Project model, doc 02 §1 & §3),
   named directly off the raw enum value lowercased — .pill-{{ project.quote_state|lower }}
   and .pill-{{ project.delivery_state|lower }} — so there is no separate name-mapping
   table to keep in sync if a state is ever renamed. Every template that shows a state
   badge uses `class="badge pill-<value>"`: `.badge` still supplies Bootstrap's pill
   shape/padding/sizing, this section only supplies colour.

   Quote state reads as a business outcome, so it keeps to fairly ordinary status
   hues (grey/blue/green/red/amber). Delivery state reads as a process stage, so it is
   built from the brand pair instead — navy for the planning side, teal for the
   in-progress side — deliberately a distinct family from quote state's palette, so
   the two badges next to each other (see _base.html) never look like the same signal.
   REVIEW is styled as "Waiting" per the design spec that produced this palette: the
   spec's row list doesn't use this app's exact state names, and REVIEW/"awaiting
   review before close" is the only state left unaccounted for once the other six are
   matched by name, so that's the pairing used here.

   Separate from, and must never fight, the RAG border on a Kanban card (.wr-card-rag-*
   above) — RAG is due-date urgency, this is quote/delivery state; a card carrying
   both at once (a border colour plus a pill) should read as two different signals,
   not one. No board card renders a state pill today — the two templates that use
   these classes are the project detail header and the Quotes list. Text colour is
   chosen per background from computed WCAG contrast (>= 4.5:1), not by rule of
   thumb — see --wr-pill-dark-text above for why that isn't just #000. */

/* Quote state. */
.pill-draft { background-color: var(--wr-pill-grey); color: var(--wr-pill-dark-text); }
.pill-estimate { background-color: var(--wr-accent); color: var(--wr-pill-dark-text); }
.pill-open { background-color: var(--wr-pill-blue); color: var(--wr-pill-light-text); }
.pill-accepted { background-color: var(--wr-pill-green); color: var(--wr-pill-dark-text); }
.pill-declined { background-color: var(--wr-pill-red); color: var(--wr-pill-light-text); }
.pill-expired { background-color: var(--wr-pill-amber); color: var(--wr-pill-dark-text); }
.pill-cancelled { background-color: var(--wr-pill-dark-grey); color: var(--wr-pill-light-text); }

/* Delivery state. */
.pill-pre_planning { background-color: var(--wr-pill-navy-tint); color: var(--wr-brand); }
.pill-planning { background-color: var(--wr-brand); color: var(--wr-pill-light-text); }
.pill-staged { background-color: var(--wr-accent); color: var(--wr-pill-dark-text); }
.pill-active { background-color: var(--wr-accent-dark); color: var(--wr-pill-dark-text); }
.pill-review { background-color: var(--wr-pill-amber); color: var(--wr-pill-dark-text); }
.pill-blocked { background-color: var(--wr-pill-red); color: var(--wr-pill-light-text); }
.pill-closed { background-color: var(--wr-pill-dark-grey); color: var(--wr-pill-light-text); }

/* The canvas. Content blocks stay white/card-coloured because --bs-body-bg above was
   left untouched — see the comment on it. */
body {
  background-color: var(--wr-page-bg);
}

/* Headings, nav, buttons and form labels use Mada; body text, table data and form
   inputs use Open Sans by inheriting --bs-body-font-family above — nothing needed for
   those, only the Mada side needs stating explicitly. */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6,
.navbar-brand, .nav-link,
.btn,
.form-label {
  font-family: var(--wr-font-heading);
}

.navbar-wr360 {
  background-color: var(--wr-brand);
}

/* Bootstrap's .navbar-dark handles the text colour; this only fixes the brand link
   weight, which Bootstrap leaves to the theme. */
.navbar-wr360 .navbar-brand {
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* .btn-primary bakes its colours as literal hex on its own component variables, not a
   reference to --bs-primary, so overriding --bs-primary alone (above) does not reach
   it — confirmed against the vendored bootstrap.min.css before writing this. Navy, not
   turquoise: turquoise is the accent colour for links and the active-tab indicator: the
   "Save"/"Add" buttons stay a clear, high-contrast actionable colour, which navy on
   white already is. */
.btn-primary {
  --bs-btn-color: #fff;
  --bs-btn-bg: var(--wr-brand);
  --bs-btn-border-color: var(--wr-brand);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--wr-brand-dark);
  --bs-btn-hover-border-color: var(--wr-brand-dark);
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--wr-brand-dark);
  --bs-btn-active-border-color: var(--wr-brand-dark);
  --bs-btn-disabled-color: #fff;
  --bs-btn-disabled-bg: var(--wr-brand);
  --bs-btn-disabled-border-color: var(--wr-brand);
}

/* Links: turquoise, via --bs-link-color-rgb above. Only the hover state needs its own
   rule — Bootstrap darkens on hover using a colour-mix Sass built at compile time, which
   is baked into the vendored CSS and cannot be redirected by a variable at runtime. */
a:hover {
  color: var(--wr-accent-dark);
}

/* The active tab's own indicator — Bootstrap's default is a neutral border/background
   swap with no colour, so this is a direct rule rather than chasing the
   --bs-nav-tabs-link-active-* variable chain through several layers of indirection for
   one visual detail. */
.nav-tabs .nav-link.active {
  color: var(--wr-brand);
  border-bottom: 2px solid var(--wr-accent);
  font-weight: 600;
}

/* Project and quote numbers are identifiers people read aloud and compare by eye, so
   they get a fixed-width face wherever they appear. */
.wr-number {
  font-family: var(--bs-font-monospace);
  font-variant-ligatures: none;
}

/* Small print under a page heading. Bootstrap's .text-muted is the colour; this is
   the size and spacing. */
.wr-subtle {
  font-size: 0.875rem;
  margin-bottom: 0;
}

/* htmx sets aria-busy while a request is in flight. Using the attribute rather than
   htmx's own class keeps the styling independent of htmx's class names. */
[aria-busy="true"] {
  opacity: 0.55;
  transition: opacity 120ms ease-in;
}

/* Alpine's x-cloak needs this rule to exist, or the element flashes visible before
   Alpine boots. It is the one line Alpine cannot supply itself. */
[x-cloak] {
  display: none !important;
}

/* Fixed-width editable tables (BOM, and Services/Scope once they need the same
   treatment). With the browser's default auto table layout, a column's width is
   whatever its widest cell content wants — an unconstrained <input> can claim far more
   room than its data needs, which is what made the BOM row too wide for a 1366px
   laptop screen. table-layout: fixed makes column widths come from the <colgroup>
   instead, so every input gets exactly the room its column was given (Bootstrap's
   .form-control is width: 100% of its container already — it just needs a sized
   container to fill). One column left without a width in its <colgroup> absorbs
   whatever space the fixed columns don't use; that is deliberately Description. */
.wr-table-fixed {
  table-layout: fixed;
  width: 100%;
}

/* form-control's own horizontal padding plus a table-sm cell's padding can push a
   narrow column's content past its border under table-layout: fixed unless box-sizing
   is border-box — Bootstrap's reboot already sets this globally, so no override is
   needed here; noted because it is the thing that would silently break this if
   Bootstrap's reboot were ever dropped from a page.
*/

/* Explicit-width form rows — the flex equivalent of .wr-table-fixed above, for the
   "add a line" / "add a section" / per-axis-update rows that are not literal <table>s.
   Bootstrap's percentage grid (col-md-N) cannot overflow a viewport, but it still
   hands a <select> or short input far more room than its content needs; each field
   below gets an explicit width sized to what it actually shows, and the one field
   that is genuinely free text gets .wr-field-grow instead. */
.wr-form-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: flex-end;
}
.wr-field-grow {
  flex: 1 1 200px;
  min-width: 0;
}
/* A <select>'s box can be narrower than its longest real option without breaking
   anything — the open dropdown list sizes itself independently in every browser that
   matters here. This just clips the closed, currently-selected value gracefully
   instead of forcing every row wider to fit one rare long option. */
.wr-select-clip {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Canvas / panel / sub-section: three visually distinct layers ──────────────────
   body (off-white, above) -> a top-level .card (white, shadowed) -> a .card nested
   inside another .card (near-white, flat). --bs-body-bg is deliberately left at
   Bootstrap's default white (see the comment above it) so every .card is white for
   free; this section only adds the shadow that makes a top-level card visibly lift off
   the off-white body, and the third tone for the sub-sections nested inside one
   (Scope's per-section cards, the BOM/Services "Add a line" cards, DoD's per-axis
   cards) — found necessary when a screenshot showed the tab panel landing on the same
   tone as the body, because that panel was a plain bordered <div> with no background
   class at all, not a .card — fixed in the templates, not just here. */
.card {
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
.card .card {
  background-color: var(--wr-panel-nested);
  border-color: var(--bs-border-color);
  box-shadow: none;
}

/* ── Kanban boards ────────────────────────────────────────────────────────────────
   Horizontal lanes that scroll as a strip rather than wrapping, because a wrapped
   Kanban stops being a Kanban. Each lane is a fixed width so the board reads the same
   whether a lane holds one card or twenty. */
.wr-board {
  display: flex;
  gap: 0.75rem;
  overflow-x: auto;
  padding-bottom: 0.5rem;
  align-items: flex-start;
}
.wr-lane {
  flex: 0 0 300px;
  background-color: var(--wr-page-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  transition: flex-basis 0.15s ease;
}
@media (prefers-reduced-motion: reduce) {
  .wr-lane { transition: none; }
}
.wr-lane-header {
  font-family: var(--wr-font-heading);
  font-weight: 600;
  font-size: 0.875rem;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--bs-border-color);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.wr-lane-title {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.wr-lane-collapse-btn {
  flex: 0 0 auto;
  border: 0;
  background: none;
  padding: 0 0.125rem;
  line-height: 1;
  color: inherit;
  font-size: 1rem;
  cursor: pointer;
}
/* min-height so an empty lane is still a drop target — a zero-height lane cannot be
   dragged into, which is the one thing every Kanban has to get right. */
.wr-lane-body {
  padding: 0.5rem;
  min-height: 120px;
}

/* Lane collapse (UAT #11) — a header-only strip so every stage stays reachable
   without horizontal scrolling, whatever the lane count. flex-basis is what the
   "fit to screen" shrink math below reads, same as the expanded default above. */
.wr-lane.wr-lane-collapsed {
  flex-basis: 40px;
  overflow: hidden;
}
.wr-lane.wr-lane-collapsed .wr-lane-body {
  display: none;
}
.wr-lane.wr-lane-collapsed .wr-lane-header {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  padding: 0.5rem 0.25rem;
  justify-content: flex-start;
}
/* Counter-rotate the toggle glyph itself back upright — only the label needs to
   read top-to-bottom in a 40px-wide strip. */
.wr-lane.wr-lane-collapsed .wr-lane-collapse-btn {
  writing-mode: horizontal-tb;
}

/* "Fit to screen" (UAT #11): a lane's preferred size stays 300px (or 40px
   collapsed) — this only turns on the shrink flexbox already knows how to do,
   so the board's total width becomes min(sum of each lane's own flex-basis, the
   viewport) for free, with no JS layout math. overflow-x: hidden is tidiness,
   not what makes the shrink happen — flex-shrink prevents the overflow itself. */
.wr-board.wr-fit-screen {
  overflow-x: hidden;
}
.wr-board.wr-fit-screen .wr-lane {
  flex-shrink: 1;
  min-width: 0;
}
.wr-card {
  background-color: #fff;
  border: 1px solid var(--bs-border-color);
  /* Neutral by default, deliberately not the brand accent (owner-reported: teal here
     read as a RAG colour, sitting in RAG's own visual slot on a project card, when it
     was only ever the identity colour). A card with a real RAG gets it from
     .wr-card-rag-* below, which is more specific and wins; a card with none — a
     PRE_PLANNING project, or any activity card, which never has RAG at all — stays
     this grey. Teal is still fine for links and the active-tab indicator; it is not a
     status colour, and this is the one place it was being read as one. */
  border-left: 3px solid var(--bs-gray-400);
  border-radius: var(--bs-border-radius);
  padding: 0.5rem 0.625rem;
  margin-bottom: 0.5rem;
  cursor: grab;
  font-size: 0.875rem;
}
.wr-card:last-child {
  margin-bottom: 0;
}
/* SortableJS applies these two classes itself during a drag. */
.wr-card-ghost {
  opacity: 0.4;
}
.wr-card-drag {
  cursor: grabbing;
}
/* RAG stripe on a project card. Semantic colours, deliberately Bootstrap's own rather
   than brand navy/turquoise — this is status, not identity. */
.wr-card-rag-RED { border-left-color: var(--bs-danger); }
.wr-card-rag-AMBER { border-left-color: var(--bs-warning); }
.wr-card-rag-GREEN { border-left-color: var(--bs-success); }

/* Completion ring (UAT #12) — a conic-gradient donut, not an SVG or a JS chart
   library: --pct (0-100, set inline per card) is the one moving part, and the
   ::after punches the centre out to #fff to match .wr-card's own background
   above, which is what makes this read as a ring rather than a filled dot. Sized
   for a card's own text (0.875rem, see .wr-card) to sit inline with the title. */
.wr-completion-ring {
  --pct: 0;
  display: inline-block;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: conic-gradient(var(--bs-success) calc(var(--pct) * 1%), var(--bs-gray-300) 0);
  position: relative;
}
.wr-completion-ring::after {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: #fff;
}

/* Comment notes (doc 01 §7). A left rule rather than a card per note: a thread of six
   short notes as six cards is mostly border. */
.wr-note {
  border-left: 3px solid var(--bs-border-color);
  padding-left: 0.5rem;
  margin-bottom: 0.5rem;
}
.wr-note:last-child {
  margin-bottom: 0;
}
/* Rendered markdown sits inside a note, so its first and last block must not add the
   vertical margin a standalone <p> would. */
.wr-markdown > :first-child { margin-top: 0; }
.wr-markdown > :last-child { margin-bottom: 0; }
.wr-markdown p { margin-bottom: 0.25rem; }
.wr-markdown ul { margin-bottom: 0.25rem; padding-left: 1.25rem; }
.wr-markdown code {
  background-color: var(--wr-panel-nested);
  padding: 0.05rem 0.25rem;
  border-radius: 0.2rem;
}
/* The per-activity notes row. No top border, so the notes read as belonging to the
   activity row above rather than as a row of their own. */
.wr-notes-row > td {
  border-top: 0;
}
.wr-notes > summary {
  cursor: pointer;
}

/* A textarea living in a fixed-layout table cell. Vertical resize only — the column
   width is fixed, so a horizontal drag would push the control out of its own cell —
   and a floor of one line, so it cannot be dragged away to nothing. */
.wr-cell-textarea {
  resize: vertical;
  min-height: calc(1.5em + 0.5rem + 2px);
}

/* ── Page-level hierarchy ─────────────────────────────────────────────────────────
   Owner feedback: title, filters, board/table and footer all read at the same weight,
   so the eye has nowhere obvious to land. These three classes are the fix, defined
   once here and applied per page in the markup — a page opts in by using them, rather
   than every page's own <h1>/filter row being hand-tuned to look right on its own. */

/* Clearly the largest, boldest thing on the page — bigger than any .h4/.h6 a card or
   tab uses elsewhere, so it reads as the answer to "what page is this", not one more
   heading among several. */
.wr-page-title {
  font-size: 1.75rem;
  font-weight: 700;
  margin-bottom: 0.25rem;
}

/* The title block and the filter/action row beneath it need to read as two distinct
   zones, not one continuous block — a rule and real padding underneath the title does
   that; margin alone was not enough separation on its own. */
.wr-page-header {
  padding-bottom: 1rem;
  margin-bottom: 1.5rem;
  border-bottom: 1px solid var(--bs-border-color);
}

/* The gap between the filter row and the board/table/list below it. Filters are a
   control panel *above* the content, not part of the data — this is what makes that
   true visually, not just structurally. */
.wr-content-gap {
  margin-top: 1.75rem;
}

/* ── Progressive disclosure for "how this works" text ────────────────────────────
   A page explaining its own mechanics in a permanent paragraph teaches the workflow on
   every load, which is the opposite of what someone using the app daily needs. The
   default view keeps one short line saying what the section *is*; the "how it works"
   detail lives behind this, opened on demand. Plain <details>/<summary> — no JS, free
   keyboard and screen-reader support, and it degrades to "just more text" if CSS fails. */
.wr-info {
  margin-top: 0.25rem;
}
.wr-info > summary {
  cursor: pointer;
  font-size: 0.8125rem;
  color: var(--wr-accent-dark);
  list-style: none;
}
/* Both marker styles killed explicitly — ::-webkit-details-marker for Chrome/Safari,
   list-style above for Firefox, which does not use a marker pseudo-element at all. */
.wr-info > summary::-webkit-details-marker {
  display: none;
}
.wr-info > summary::before {
  content: "ⓘ ";
}
.wr-info > summary:hover {
  text-decoration: underline;
}
.wr-info[open] > summary {
  margin-bottom: 0.25rem;
}
.wr-info-body {
  font-size: 0.8125rem;
}

/* The footer is metadata about the page, not content on it — deliberately the
   quietest thing on screen. Smaller than .wr-subtle's 0.875rem, which everything else
   "small" on the page already uses; the footer needs to read as a step down from that,
   not level with it. */
footer.wr-footer {
  font-size: 0.75rem;
}
