/* FenceCalc Pro — v2 component library, ported to vanilla CSS.
 *
 * **Why not the JSX.** The bundle ships React components with inline style objects. This repo
 * has no build step, no node and no npx — `server.py` is stdlib and `ui/` is plain ES modules.
 * Shipping JSX would introduce a toolchain the product does not have. So each component is
 * ported to a class, value for value, from `components/<group>/<Name>.jsx`.
 *
 * **Every value here is traceable.** Nothing was eyeballed from a screenshot: each block names
 * the source file it came from, and colours resolve through `ui/tokens-v2.css` rather than
 * being repeated as literals — with one deliberate exception noted at `.fc-btn--secondary`.
 *
 * **This is components only.** No app chrome, no layout, no screens — the UI hold is on. The
 * only consumer is `components.html` beside it.
 */

/* ─────────────────────────────────────────────────────────── Button
   Source: components/actions/Button.jsx — 7 variants x 3 sizes. */

/* ⚠⚠ **The SECOND defect on this rule caused by `<a>` not being `<button>`.** The first is the
   `text-decoration: none` inside the block below, whose comment says *"every button in
   `Button.jsx` is a `<button>` … which is exactly why porting the component's declarations one
   by one could not surface this"* — the same sentence explains this one.

   Chromium's user-agent stylesheet gives `<button>` and `<select>` `box-sizing: border-box`,
   and gives `<a>`, `<input>`, `<textarea>` and `<div>` `content-box` — measured in this browser
   rather than recalled:

     a → content-box    button → border-box    input → content-box
     textarea → content-box    select → border-box    div → content-box

   So `.fc-btn` composed correctly on all 55 `<button class="fc-btn">` in v2 and was 38px too
   wide on `a.fc-btn--md.fc-btn--full` — `18+18+1+1` of padding and border added OUTSIDE the
   `width: 100%` that `.fc-btn--full` sets. On screen 01 the button overhung its 497.8px rail
   column by exactly that 38px, `main.iq-page`'s 32px right padding absorbed all but **6px**, and
   the page scrolled sideways by 6px at 1440 and at 1280 alike.

   ⚠ **The same class on the same modifiers behaved differently by element**, which is the one
   thing a shared class exists to prevent. `screen-02`'s `fc-btn--full` is a `<button>` and never
   overflowed; `screen-01`'s is an `<a>` and always did.

   ✅ **Blast radius measured, not assumed: this moves exactly one element.** The 55 `<button>`s
   already computed `border-box`, so the declaration is a no-op for them; of the **7**
   `<a class="fc-btn">`, six are auto-width `inline-flex`, where `box-sizing` cannot change a box
   that is sized by its content. Only a definite width is affected, and `a.fc-btn--full` occurs
   once in the build.

   ⚠ **Counted with comments stripped.** The first count said 8, because the `<a class="fc-btn">`
   written two paragraphs above matched the grep — *"a scan reporting a hit is a scan matching its
   own explanatory text"*, which this repository has recorded three times.

   ⛔ **Classic never had this either** — same reason as `.fc-field__control` below:
   `ui/app.css`'s `* { box-sizing: border-box; }` is not loaded by v2. */
.fc-btn {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  /* ⚠ **Missing, and it showed on every `<a class="fc-btn">` in the build.** Screen 01's
     primary action is a link — *"Continue to material call-up →"* — so with nothing said here
     it kept the user agent's underline and rendered as underlined white text on marine. Every
     button in `Button.jsx` is a `<button>`, which has no underline to reset, which is exactly
     why porting the component's declarations one by one could not surface this. `.fc-btn--bare`
     sets its underline back, below. */
  text-decoration: none;
  font-family: var(--font-body);
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background 120ms ease, color 120ms ease;
}
.fc-btn--sm { font-size: 12px; padding: 6px 11px;  border-radius: 8px; }
.fc-btn--md { font-size: 13px; padding: 10px 18px; border-radius: var(--radius-control); }
.fc-btn--lg { font-size: 14px; padding: 13px 22px; border-radius: var(--radius-control); }
.fc-btn--full { width: 100%; }

.fc-btn--primary { background: var(--fc-marine); color: #fff; }
.fc-btn--primary:hover:not(:disabled) { background: var(--fc-marine-hover); }

.fc-btn--secondary { background: #fff; color: var(--fc-marine); border-color: var(--fc-line-strong); }
/* #F3F2EE is a hover-only wash that exists in Button.jsx and in no token. Kept as a literal
   rather than invented as a token: a token is a promise the design system made, and this is
   not one it made. Flagged in REVIEW.md as a question for the designer. */
.fc-btn--secondary:hover:not(:disabled) { background: #F3F2EE; }

.fc-btn--ghost { background: transparent; color: var(--fc-steel); }
.fc-btn--ghost:hover:not(:disabled) { background: #F3F2EE; }

.fc-btn--danger { background: #fff; color: var(--fc-red); border-color: var(--field-attention-border); }
.fc-btn--danger:hover:not(:disabled) { background: var(--field-attention-bg); }

.fc-btn--success { background: var(--fc-green); color: #fff; }
.fc-btn--success:hover:not(:disabled) { background: #0B5334; }

.fc-btn--dark-outline { background: transparent; color: var(--fc-mist-strong); border-color: rgba(255,255,255,0.25); }
.fc-btn--dark-outline:hover:not(:disabled) { background: rgba(255,255,255,0.08); color: #fff; }

.fc-btn--bare {
  background: transparent; color: var(--fc-marine); border: none;
  padding: 2px 4px; font-weight: 600;
  text-decoration: underline; text-decoration-color: rgba(23,59,83,0.3); text-underline-offset: 3px;
}

.fc-btn:disabled { background: var(--fc-line); color: var(--fc-faint); border-color: transparent; cursor: default; }

/* ──────────────────────────────────────────────────── Field — four states
   Source: components/forms/Field.jsx + tokens/colors.css, which agree exactly.

   **The four states are the product's most important visual distinction.** An estimator scans
   for colour to find what they can change. Override is the one that matters most: a blank
   override means the engine's figure stands, NOT zero. */

.fc-field { display: block; }
.fc-field__label {
  display: flex; align-items: baseline; gap: 6px;
  font-family: var(--font-body); font-size: 12px; font-weight: 600;
  color: var(--fc-steel); margin-bottom: 5px;
}
.fc-field__dot { width: 7px; height: 7px; border-radius: 999px; align-self: center; flex: none; }
.fc-field__dot--override  { background: var(--field-override-dot); }
.fc-field__dot--attention { background: var(--field-attention-dot); }
.fc-field__ref {
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 400; color: var(--fc-faint);
}
/* ⚠⚠ **`box-sizing` is load-bearing here and its absence was a real defect for nine days.**
   `width: 100%` with `padding: 9px 12px` and a 1px border renders `12+12+1+1 = 26px` wider than
   the containing block under the default `content-box`. Against `.fc-card`, padded
   `--pad-card-x: 18px` plus its own 1px border, the control crossed the drawn card edge by
   `26 - 19 = 7px` — measured on the live page at 1440 and 1280, on **five screens, 15 controls
   per width**, and on **screen 06** it pushed the document into horizontal scroll at a 1440px
   viewport (1526 → 1440 once fixed).

   ⚠ **Screen 01 also scrolled sideways by 6px and this was NOT the cause** — that one is an
   `a.fc-btn--md.fc-btn--full`, it survived this fix unchanged, and it was recorded in the
   contract as a separate defect rather than claimed here. An earlier draft of this comment
   blamed it on the overhang; the measurement said otherwise and the measurement won.
   ✅ **Fixed separately on 30 Aug — see the `.fc-btn` comment above.** It was the identical
   `content-box` shape one element along, which is why the two read as one bug and are not.

   ⛔ **Classic never had this**: `ui/app.css` carries `* { box-sizing: border-box; }` and v2
   screens do not load that file — they load `tokens-v2.css`, this file, `chrome.css` and
   `screens.css`, none of which reset it. **The component was authored for border-box**; the
   padding numbers only compose against the card if it is on.

   ⚠ Three v2 rules already carry a local `box-sizing: border-box` — `.mc-ops__in`,
   `.iq-block-add`, `.jf-folder`. **Three people met this bug and each patched their own element**,
   which is why it survived in the shared rule. Fixed here, at the cause, rather than a fourth
   time at a symptom. */
.fc-field__control {
  box-sizing: border-box;
  width: 100%; padding: 9px 12px; border-radius: var(--radius-control);
  color: var(--fc-ink); font-size: 13.5px; border: 1px solid transparent;
}
.fc-field__control--numeric {
  font-family: var(--font-mono); font-size: 13px; text-align: right; font-variant-numeric: tabular-nums;
}
.fc-field--input     .fc-field__control { background: var(--field-input-bg);     border-color: var(--field-input-border); }
.fc-field--override  .fc-field__control { background: var(--field-override-bg);  border-color: var(--field-override-border); }
.fc-field--attention .fc-field__control { background: var(--field-attention-bg); border-color: var(--field-attention-border); }

/* The calculated state is not an input — it is a read-only well carrying the ƒ marker. */
.fc-field--calc .fc-field__control {
  background: var(--field-calc-bg); border-color: transparent;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.fc-field__fx { font-family: var(--font-mono); font-size: 11px; color: var(--fc-mute); }
.fc-field__value { font-family: var(--font-mono); font-size: 13px; font-variant-numeric: tabular-nums; }

.fc-field__hint { font-size: 12px; color: var(--fc-mute); line-height: 1.4; margin-top: 5px; }
.fc-field--attention .fc-field__hint { color: var(--fc-red); }

/* ─────────────────────────────────────────────────────────── StatusChip
   Source: components/display/StatusChip.jsx — 4 tones. */

.fc-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-body); font-size: 12.5px; font-weight: 700;
  padding: 6px 13px; border-radius: 999px; white-space: nowrap; border: 1px solid transparent;
}
.fc-chip--ready   { background: var(--fc-green-tint); color: var(--fc-green); border-color: #BFDCCB; }
.fc-chip--review  { background: var(--fc-red-tint);   color: var(--fc-red);   border-color: var(--fc-red-line); }
.fc-chip--pending { background: var(--fc-amber-tint); color: var(--fc-amber); border-color: var(--fc-amber-line); }
.fc-chip--neutral { background: var(--fc-line-soft);  color: var(--fc-steel); border-color: #E0DDD5; }
/* ⚠ **`display` beats the `hidden` attribute**, so a chip carrying `hidden` renders anyway. Found
   for the third time on 2026-08-18, when r82's "sent" chip was visible before anything had been
   sent. The rule lives here, beside the `display` that causes it, rather than in each screen. */
.fc-chip[hidden] { display: none !important; }

/* StatusWord — a job status as a plain coloured word. Won green, Lost red, everything else
   marine. Source: StatusChip.jsx StatusWord. */
.fc-status { font-family: var(--font-body); font-size: 13px; font-weight: 700; color: var(--fc-marine); }
.fc-status--won  { color: var(--fc-green); }
.fc-status--lost { color: var(--fc-red); }

/* ────────────────────────────────────────────────────────── NoticeBanner
   Source: components/display/NoticeBanner.jsx — 4 tones. */

.fc-notice { display: flex; align-items: flex-start; gap: 12px; border-radius: var(--radius-card); padding: 13px 16px; border: 1px solid transparent; }
/* **Load-bearing, and this project has paid for it once already.** `display: flex` on an
   element carrying `hidden` beats the UA stylesheet's `[hidden] { display: none }`, so the
   notice sits on screen permanently announcing an override that is not there. That is exactly
   the `.savebar[hidden]` defect `CLAUDE.md` records — every source-level test passed and only
   the rendered page showed it. Any component here given `display` needs this line with it. */
.fc-notice[hidden] { display: none; }
.fc-notice__glyph { font-family: var(--font-mono); font-size: 12px; line-height: 1.4; }
.fc-notice__body  { flex: 1; }
.fc-notice__kicker { font-family: var(--font-body); font-size: 12px; font-weight: 700; color: var(--fc-ink); margin-bottom: 3px; }
.fc-notice__text   { font-size: 13px; line-height: 1.5; }

.fc-notice--note      { background: #fff; border-color: var(--fc-line); }

/* ⚠ **The `ok` tone, and the design bundle asked for it by name.** `Form Controls.md`:
   *"`NoticeBanner tone="ok"` does not exist. The done state passes it … so it silently falls
   back to `note` and the success state renders as a neutral white banner with no glyph."*
   **A silent fallback to the neutral tone is exactly the class of defect a green build never
   reports** — the markup is right, the CSS is absent, and nothing anywhere raises.

   Values are the spec's, and all three already had tokens: `#E4F3EA` is `--fc-green-tint` and
   `#0F6A43` is `--fc-green`, matching StatusChip's `ready`. `#BFDCCB` is the one literal — the
   green line has no token, which `screens.css` already records at its own use of it. */
.fc-notice--ok        { background: var(--fc-green-tint); border-color: #BFDCCB; }
.fc-notice--ok        .fc-notice__text  { color: var(--fc-ink); }
.fc-notice--ok .fc-notice__glyph { color: var(--fc-green); }
.fc-notice--note      .fc-notice__text { color: var(--fc-steel); }
.fc-notice--attention { background: var(--fc-red-tint); border-color: var(--fc-red-line); }
.fc-notice--attention .fc-notice__text, .fc-notice--attention .fc-notice__glyph { color: var(--fc-red); }
.fc-notice--info      { background: var(--fc-info-tint); border-color: #C3DCEA; }
.fc-notice--info      .fc-notice__text  { color: var(--fc-ink); }
.fc-notice--info      .fc-notice__glyph { color: var(--fc-info); font-size: 15px; }
.fc-notice--dark      { background: var(--fc-marine); }
.fc-notice--dark      .fc-notice__text { color: var(--fc-mist-strong); }
.fc-notice--dark      .fc-notice__kicker, .fc-notice--dark .fc-notice__glyph { color: var(--fc-sky); }

/* ─────────────────────────────────────────────────────────────────── Card */

.fc-card {
  background: var(--surface-card); border: 1px solid var(--fc-line);
  border-radius: var(--radius-card); box-shadow: var(--shadow-rest); padding: var(--pad-card-sm);
}
.fc-card--sm { padding: var(--pad-card-sm); }
/* **The card's head band — `Card.jsx`, and it had no rule here at all until 20 Aug.**
   Screens emit `<div class="fc-card__head">` (screen 02's Scale card among them) and it styled
   as a bare div: no padding, no rule under it, body text size. The design gives the head its own
   padding and a `--fc-line-soft` separator, and the negative margins are what let a band sit
   flush inside a card that has its own padding. */
.fc-card__head {
  margin: calc(-1 * 16px) calc(-1 * 18px) 16px;   /* pull out to the card's own edges */
  padding: var(--pad-card-head);
  border-bottom: 1px solid var(--fc-line-soft);
  font-family: var(--font-display); font-weight: 700; font-size: 14.5px;
  letter-spacing: var(--tracking-h2); color: var(--fc-ink);
}
.fc-card__title { font: var(--h3); color: var(--fc-ink); margin-bottom: 4px; }
.fc-card__meta  { font-size: var(--text-meta); color: var(--fc-mute); }

/* ────────────────────────────────────────────────────────────── DataTable
   Money is mono, tabular and right-aligned — always. A column of figures that does not line
   up is a column nobody checks. */

.fc-table { width: 100%; border-collapse: collapse; font-size: var(--text-detail); }
/* ⚠ **Sentence case at 12px in `--fc-steel` until 20 Aug, and the design has never drawn it
   that way.** `DataTable.jsx` heads are 11.5px / 700 / **uppercase** / 0.05em in `--fc-mute` —
   an overline, not a smaller version of the body text. Sentence-case heads sit at almost the
   same weight and colour as the cells beneath them, so the eye finds no edge between the
   label and the data and the whole table reads as one grey block. Same family as the
   `.iq-kicker` finding earlier in this pass: most of why a screen full of correct content
   reads as flat is micro-typography that was rounded to "close enough". */
.fc-table th {
  text-align: left; padding: var(--pad-th); font-family: var(--font-body);
  font-size: var(--text-fine); font-weight: 700; color: var(--fc-mute);
  text-transform: uppercase; letter-spacing: var(--track-caps);
  border-bottom: 1px solid var(--fc-line); white-space: nowrap;
}
.fc-table td { padding: var(--pad-cell); border-bottom: 1px solid var(--fc-line-soft); color: var(--fc-ink); }
.fc-table tbody tr:last-child td { border-bottom: none; }
.fc-table .num, .fc-table th.num {
  font-family: var(--font-mono); text-align: right; font-variant-numeric: tabular-nums;
}
.fc-table th.num { font-family: var(--font-body); }

/* A money pill — a figure that needs to read as a total rather than a cell. */
.fc-money {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 600;
  background: var(--fc-well); border-radius: var(--radius-badge); padding: 3px 9px;
}
/* ⚠ pending — a line with no base cost. **Never renders as $0.00**: it is excluded from the
   total, and showing zero would let an unpriced line footing quietly. */
.fc-pending { font-family: var(--font-mono); font-size: 12px; color: var(--fc-amber); white-space: nowrap; }

/* ───────────────────────────────────────────────────── SectionRule / KVList */

.fc-rule { display: flex; align-items: center; gap: 12px; margin: 22px 0 14px; }
.fc-rule__label { font: var(--overline); letter-spacing: var(--track-overline); text-transform: uppercase; color: var(--fc-mute); white-space: nowrap; }
.fc-rule__line  { flex: 1; height: 1px; background: var(--fc-line); }

.fc-kv { display: grid; grid-template-columns: auto 1fr; gap: 7px 18px; font-size: var(--text-detail); }
.fc-kv__k { color: var(--fc-steel); }
.fc-kv__v { color: var(--fc-ink); text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* ────────────────────────────────────────────────────────────── DarkPanel */

.fc-dark { background: var(--fc-marine); color: var(--fc-mist-strong); border-radius: var(--radius-card); padding: var(--pad-panel); }
.fc-dark__title { font: var(--h3); color: #fff; }
.fc-dark .fc-kv__k { color: var(--fc-mist); }
.fc-dark .fc-kv__v { color: #fff; }

/* ───────────────────────────────────────────────── SegmentedControl / FilterChip */

/* ⚠ **Five values here were near-misses, and the control is on both screens Bevan failed** —
   the A4/A3 pair on 03 and 04, engine/manual on 03, and row 73's charge mode. `Component
   Library.md` (22 Aug) publishes the numbers for the first time and none of these were right:
   the trough was `--fc-well` `#F7F6F2` against the design's `#F1EFEA`, its gap 3px against 2px,
   the option `7px 14px` against `6px 13px`, and a resting option was **600** where the design
   sets **500** — a whole weight step, which is why an unselected option read as half-selected.

   ⚠ **The active pill's shadow is the control's own, not `--shadow-rest`.** The library's
   closing note says *"two shadows only"* and then this component states a third,
   `0 1px 2px rgba(28,36,43,0.1)` — tighter and twice the opacity. **The specific section wins
   over the summary rule**, and it is written as a literal here because promoting it to a token
   would make a third entry in a vocabulary that document says has two. */
.fc-seg { display: inline-flex; background: var(--fc-line-soft); border-radius: var(--radius-control); padding: 3px; gap: 2px; }
.fc-seg__opt {
  font-family: var(--font-body); font-size: 12.5px; font-weight: 500; color: var(--fc-steel);
  background: transparent; border: none; border-radius: 8px; padding: 6px 13px; cursor: pointer;
  white-space: nowrap;
}
.fc-seg__opt[aria-selected="true"] {
  background: #fff; color: var(--fc-marine); font-weight: 700;
  box-shadow: 0 1px 2px rgba(28, 36, 43, 0.1);
}

/* ⚠ **This was `.fc-chip`'s geometry wearing a different border, and the design draws a
   different control.** `FilterChip` is 13px / **600** / `8px 15px` / `gap: 7px`, resting on
   `#4C5A66` inside `#E0DDD5` — not `--fc-steel` inside `--fc-line-strong`. `#4C5A66` is a new
   colour with no token: **left as a literal deliberately**, because a one-use hex named into the
   palette is how a palette stops meaning anything. The radius stays 12px against StatusChip's
   999px, and the library says why: *"a chip you can press is squarer than a chip that only
   reports."* */
.fc-filter {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-body); font-size: 13px; font-weight: 600;
  background: #fff; color: #4C5A66;
  border: 1px solid #E0DDD5; border-radius: var(--radius-chip);
  padding: 8px 15px; cursor: pointer;
}
.fc-filter[aria-pressed="true"] { background: var(--fc-marine); color: #fff; border-color: var(--fc-marine); }

/* ──────────────────────────────────────────────────────────── FieldKeyLegend
   The legend reads the SAME custom properties the cells do, so the caption cannot drift from
   what it describes — the one failure mode a legend has available to it. */

/* `FieldKeyLegend.jsx` — geometry and gaps are its own, and so is the heading. */
.fc-legend { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.fc-legend__key {
  font-family: var(--font-body); font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; color: var(--fc-faint);
}
.fc-legend__item { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--fc-steel); }
.fc-legend__sw { width: 22px; height: 13px; border-radius: 5px; border: 1px solid transparent; flex: none; }
.fc-legend__sw--input     { background: var(--field-input-bg);     border-color: var(--field-input-border); }
.fc-legend__sw--calc      { background: var(--field-calc-bg); }
.fc-legend__sw--override  { background: var(--field-override-bg);  border-color: var(--field-override-border); }
.fc-legend__sw--attention { background: var(--field-attention-bg); border-color: var(--field-attention-border); }
