/* ==========================================================================
   Contractor Trade, base.css
   Reset, typography, links, focus, utilities, motion.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-15);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-variant-numeric: normal;
  overflow-x: hidden;          /* the page body never scrolls sideways */
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

img, svg, video, canvas { display: block; max-width: 100%; }
img { height: auto; }

/* An icon next to text belongs on the text's line.

   The reset above makes svg display:block to kill the inline baseline gap,
   which is right for a chart, a logo or a lone glyph in a flex row. But an
   icon written inline with its label, `icon() + ' ' + text` inside a span
   is then a block box, so it takes its own line and the label drops beneath
   it. That is what stacked every meta item icon-above-text.

   Icons from util.icon() carry .ico and align to the text baseline instead.
   Inside a flex or grid parent the browser blockifies them anyway, so this
   is safe in the places that were already correct. */
.ico { display: inline-block; vertical-align: -.15em; flex-shrink: 0; }

/* A run of icon+text pairs: the run wraps, each pair never splits. */
.meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s5);
  min-width: 0;
}
.meta > * {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
}
.meta .ico { opacity: .65; }

button, input, select, textarea {
  font: inherit;
  color: inherit;
  margin: 0;
}
button { background: none; border: none; padding: 0; cursor: pointer; }

table { border-collapse: collapse; width: 100%; }

hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: var(--s6) 0;
}

/* --- Links -----------------------------------------------------------------
   Links inherit their surrounding ink and earn affordance from an underline.
   Tinting the base `a` rule is the trap: every block-level link, a whole
   notification row, a whole card, then renders its contents in the accent. */
a {
  color: inherit;
  text-decoration: none;
}
/* `.link` on a button too: an action that opens a dialog is a button, not a
   link to nowhere, but it should still read as a link where the copy runs
   inline with text. */
a.link, button.link {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--border-strong);
  transition: text-decoration-color var(--t-fast) var(--ease);
}
button.link { color: var(--accent-ink); font: inherit; }
a.link:hover, button.link:hover { text-decoration-color: currentColor; }

/* --- Focus ----------------------------------------------------------------
   Programmatic focus targets are landmarks, not controls. `#main-content` is
   focused after every route change so a screen reader announces the new page;
   drawing a 2px ring around the whole content area is noise, and it is what
   put a stray blue line under the hero on first load. */
:focus { outline: none; }
#main-content:focus,
#main-content:focus-visible,
[tabindex="-1"]:focus,
[tabindex="-1"]:focus-visible { outline: none; box-shadow: none; }
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
.btn:focus-visible,
.input:focus-visible,
.select:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--ring);
  border-color: var(--accent);
}

/* --- Skip link ------------------------------------------------------------ */
.skip-link {
  position: absolute;
  left: var(--s4);
  top: -100px;
  z-index: 200;
  padding: var(--s3) var(--s5);
  background: var(--brand);
  color: var(--brand-on);
  border-radius: var(--r-md);
  font-weight: 600;
  transition: top var(--t-fast) var(--ease);
}
.skip-link:focus { top: var(--s4); }

/* --- Type scale -------------------------------------------------------------
   Stepped up a notch across the board. The brief was bigger, clearer text
   that can be read at a glance rather than studied, these are people
   checking a quote on a phone on site, in daylight, with dirty hands.

   Body went 14 to 15, card titles 16 to 18, secondary text 13 to 14. That
   sounds small written down and is the difference between an interface that
   reads and one that squints.

   Headlines use --display. Set that token to var(--font) and the whole
   product is all-sans again; nothing here needs touching.
   -------------------------------------------------------------------------- */
.display {
  font-family: var(--display);
  font-size: var(--fs-48);
  line-height: 1.04;
  letter-spacing: var(--display-tr);
  font-weight: 800;
}
.h1 {
  font-family: var(--display);
  font-size: var(--fs-30);
  line-height: 1.18;
  letter-spacing: var(--display-tr);
  font-weight: 700;
}

/* Below h1 it is the UI face. A serif at card-title size reads as a magazine
   rather than a tool, and these are labels on things you press. */
.h2 { font-size: var(--fs-24); line-height: var(--lh-snug);  letter-spacing: -.016em; font-weight: 620; }
.h3 { font-size: var(--fs-18); line-height: var(--lh-snug);  letter-spacing: -.01em;  font-weight: 620; }
.h4 { font-size: var(--fs-15); line-height: var(--lh-snug);  font-weight: 620; }

.lede  { font-size: var(--fs-20); line-height: 1.5; color: var(--text-2); }
.body  { font-size: var(--fs-15); line-height: var(--lh-normal); }
.small { font-size: var(--fs-14); }
.tiny  { font-size: var(--fs-12); }

.muted     { color: var(--text-2); }
.muted-2   { color: var(--text-3); }
.strong    { font-weight: 600; }
.semibold  { font-weight: 550; }

.eyebrow {
  font-size: var(--fs-11);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* Tabular figures wherever numbers align in a column. */
.tnum, .num, td.num, th.num { font-variant-numeric: tabular-nums; }
.mono { font-family: var(--mono); font-size: .92em; }

.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* Clamping shows the second line; truncating hides it behind an ellipsis.
   A NAME is always clamped, never truncated, a category list that renders
   "Windows, Doors &…" has failed at the one job it has. Truncation is for a
   meta line, a preview, or a label in a genuinely narrow rail. */
.clamp-2, .clamp-3 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}
.clamp-2 { -webkit-line-clamp: 2; }
.clamp-3 { -webkit-line-clamp: 3; }

/* --- Layout utilities ----------------------------------------------------- */
.row     { display: flex; align-items: center; gap: var(--s3); }
.row-top { display: flex; align-items: flex-start; gap: var(--s3); }
.col     { display: flex; flex-direction: column; gap: var(--s3); }
.wrap    { flex-wrap: wrap; }
.between { justify-content: space-between; }
.center  { justify-content: center; }
.end     { justify-content: flex-end; }
.grow    { flex: 1 1 auto; min-width: 0; }
.shrink-0 { flex-shrink: 0; }
.self-start { align-self: flex-start; }

.grid { display: grid; gap: var(--s4); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.gap-1 { gap: var(--s1); } .gap-2 { gap: var(--s2); } .gap-3 { gap: var(--s3); }
.gap-4 { gap: var(--s4); } .gap-5 { gap: var(--s5); } .gap-6 { gap: var(--s6); }
.gap-7 { gap: var(--s7); } .gap-8 { gap: var(--s8); }

.mt-1 { margin-top: var(--s1); } .mt-2 { margin-top: var(--s2); }
.mt-3 { margin-top: var(--s3); } .mt-4 { margin-top: var(--s4); }
.mt-5 { margin-top: var(--s5); } .mt-6 { margin-top: var(--s6); }
.mt-7 { margin-top: var(--s7); } .mt-8 { margin-top: var(--s8); }
.mt-9 { margin-top: var(--s9); } .mt-10 { margin-top: var(--s10); }
.mt-11 { margin-top: var(--s11); }

.mb-1 { margin-bottom: var(--s1); } .mb-2 { margin-bottom: var(--s2); }
.mb-3 { margin-bottom: var(--s3); } .mb-4 { margin-bottom: var(--s4); }
.pt-3 { padding-top: var(--s3); }
.mb-5 { margin-bottom: var(--s5); } .mb-6 { margin-bottom: var(--s6); }
.mb-7 { margin-bottom: var(--s7); } .mb-8 { margin-bottom: var(--s8); }

.text-right  { text-align: right; }
.text-center { text-align: center; }
.w-full { width: 100%; }
/* A button used as a row: full width, text reading from the left like the
   links either side of it. Three screens set this inline. */
.row-button { width: 100%; text-align: left; }
.hidden { display: none !important; }

.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;
}

.reading { max-width: var(--content-max); }
.divider { height: 1px; background: var(--border); border: none; margin: var(--s5) 0; }
/* Inside a total or a summary block, where the rule separates two lines
   rather than two sections. Was the same inline margin override, twice. */
.divider-tight { margin: var(--s2) 0; }
/* A rule that runs between two pieces of inline content, "or sign in as".
   Two auth screens each built this from scratch with four inline properties. */
.divider-inline { flex: 1 1 auto; height: 1px; background: var(--border); border: none; margin: 0; }

@media (max-width: 860px) { .hide-sm { display: none; } }

/* --- Scrollbars ----------------------------------------------------------- */
* { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: var(--r-full);
  border: 3px solid var(--bg);
}

/* --- Motion --------------------------------------------------------------- */
@keyframes fade-in  { from { opacity: 0; } to { opacity: 1; } }
@keyframes rise-in  { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes scale-in { from { opacity: 0; transform: scale(.97); } to { opacity: 1; transform: none; } }
@keyframes pulse    { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
@keyframes spin     { to { transform: rotate(360deg); } }

.animate-in { animation: rise-in var(--t-med) var(--ease-out) both; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}
:root[data-motion="reduced"] *,
:root[data-motion="reduced"] *::before,
:root[data-motion="reduced"] *::after {
  animation-duration: 1ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 1ms !important;
}

/* --- Print ---------------------------------------------------------------- */
@media print {
  .app-rail, .app-topbar, .bottom-nav, .assistant-dock, .demo-bar, .no-print { display: none !important; }
  .page { padding: 0 !important; }
  .card { break-inside: avoid; border-color: var(--print-border); }
}

/* `hidden` means hidden, whatever display a class gives the element. Without
   this a button carrying .btn stays visible with the attribute set. */
[hidden] { display: none !important; }
