/* ==========================================================================
   Bold Seal — components.css
   Reusable component classes: .bs-btn (+ cta pulse), .bs-card, .bs-section
   variants, fade-in animation, eyebrow. Loaded after layout.css.

   Form styles (.bs-input, .bs-select, .bs-textarea, .bs-label, etc.) live
   in forms.css — see section 4 below for the migration note.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. Buttons — .bs-btn + variants
   -------------------------------------------------------------------------- */

.bs-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--bs-space-2);
  padding: var(--bs-space-3) var(--bs-space-6);
  min-height: 48px;              /* 48px tap target — meets WCAG 2.5.5 */
  border-radius: var(--bs-radius-md);
  font-family: inherit;
  font-size: var(--bs-font-size-base);
  font-weight: var(--bs-font-weight-bold);
  line-height: 1.2;
  text-decoration: none;
  text-align: center;
  cursor: pointer;
  transition: background var(--bs-transition-fast),
              color      var(--bs-transition-fast),
              transform  var(--bs-transition-fast),
              box-shadow var(--bs-transition-fast);
  /* Use will-change sparingly — only on CTA which animates constantly. */
}

.bs-btn:disabled,
.bs-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Primary variant — the default solid orange button. */
.bs-btn--primary {
  background: var(--bs-color-primary);
  color: var(--bs-text-inverse);
  box-shadow: var(--bs-shadow-sm);
}

.bs-btn--primary:hover {
  background: color-mix(in srgb, var(--bs-color-primary) 85%, #000);
  transform: translateY(-1px);
  box-shadow: var(--bs-shadow-md);
}

.bs-btn--primary:active {
  transform: translateY(0);
}

/* CTA variant — orange + subtle pulse animation. Used for primary page CTAs.
   Pulse draws attention without being disruptive; disabled via prefers-reduced-motion. */
.bs-btn--cta {
  background: var(--bs-color-primary);
  color: var(--bs-text-inverse);
  box-shadow: var(--bs-shadow-sm);
  will-change: box-shadow;
  animation: bs-cta-pulse 2s ease-in-out infinite;
}

.bs-btn--cta:hover {
  background: color-mix(in srgb, var(--bs-color-primary) 85%, #000);
  transform: translateY(-2px);
  box-shadow: var(--bs-shadow-lg);
  animation: none;   /* pause pulse on hover — avoids jitter */
}

.bs-btn--cta:active {
  transform: translateY(0);
}

@keyframes bs-cta-pulse {
  0%, 100% { box-shadow: 0 0 0 0   rgba(217, 140, 18, 0.4); }
  50%       { box-shadow: 0 0 0 8px rgba(217, 140, 18, 0);   }
}

@media (prefers-reduced-motion: reduce) {
  .bs-btn--cta {
    animation: none;
    box-shadow: var(--bs-shadow-md);
  }
}

/* Ghost variant — transparent bg, border, used for secondary actions. */
.bs-btn--ghost {
  background: transparent;
  color: var(--bs-color-primary);
  border: 2px solid var(--bs-color-primary);
}

.bs-btn--ghost:hover {
  background: var(--bs-color-primary);
  color: var(--bs-text-inverse);
  transform: translateY(-1px);
}

/* Ghost on dark backgrounds — white border/text. */
.bs-section--dark .bs-btn--ghost {
  color: #fff;
  border-color: #fff;
}

.bs-section--dark .bs-btn--ghost:hover {
  background: #fff;
  color: var(--bs-color-ink);
}


/* --------------------------------------------------------------------------
   2. Cards — .bs-card + variants
   -------------------------------------------------------------------------- */

.bs-card {
  background: var(--bs-surface-page);
  border-radius: var(--bs-radius-lg);
  box-shadow: var(--bs-shadow-md);
  padding: var(--bs-space-8);
}

/* Service card variant — used on home page service teasers. */
.bs-card--service {
  display: flex;
  flex-direction: column;
  gap: var(--bs-space-4);
}

.bs-card--service h3 {
  margin: 0;
}

.bs-card--service p {
  flex: 1;  /* pushes the CTA link to the bottom of the card */
  margin: 0;
}


/* --------------------------------------------------------------------------
   3. Sections — .bs-section + surface variants
   -------------------------------------------------------------------------- */

.bs-section {
  padding-block: var(--bs-space-16);
}

/* Raised — light gray background. Alternates with white to create visual rhythm. */
.bs-section--raised {
  background: var(--bs-surface-raised);
}

/* Dark — near-black background, inverted text. Used for CTA footer strips. */
.bs-section--dark {
  background: var(--bs-surface-footer);
  color: #fff;
}

.bs-section--dark h2,
.bs-section--dark h3,
.bs-section--dark p {
  color: #fff;
}

.bs-section--dark a:not(.bs-btn) {
  color: var(--bs-color-accent);
}

.bs-section--dark a:not(.bs-btn):hover {
  color: #fff;
}


/* --------------------------------------------------------------------------
   4. Grid helpers — .bs-grid-2up, .bs-grid-3up
   -------------------------------------------------------------------------- */

.bs-grid-2up {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--bs-space-8);
}

@media (min-width: 640px) {
  .bs-grid-2up {
    grid-template-columns: repeat(2, 1fr);
  }
}

.bs-grid-3up {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--bs-space-8);
}

@media (min-width: 640px) {
  .bs-grid-3up {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1024px) {
  .bs-grid-3up {
    grid-template-columns: repeat(3, 1fr);
  }
}

.bs-stack--sm {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--bs-space-3);
}


/* --------------------------------------------------------------------------
   5. Fade-in animation — driven by FadeInObserver in main.js
      Elements with [data-fade-in] start invisible and slide up 8px.
      FadeInObserver adds .is-visible when they enter the viewport.
      prefers-reduced-motion: skip animation, show immediately.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  [data-fade-in] {
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 600ms ease,
                transform 600ms ease;
  }
}

[data-fade-in].is-visible {
  opacity: 1;
  transform: translateY(0);
}


/* --------------------------------------------------------------------------
   6. Eyebrow — small label above headlines (brand convention)
   -------------------------------------------------------------------------- */

.bs-eyebrow {
  display: inline-block;
  font-size: var(--bs-font-size-sm);
  font-weight: var(--bs-font-weight-bold);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--bs-color-primary);
  margin-bottom: var(--bs-space-3);
}


/* --------------------------------------------------------------------------
   7. Estimator — .bs-estimator + sub-components
      Added Chat 20. Documented in PATTERNS.md section "Estimator component".
      All IDs use estimator_[subsection]_[type] naming per PATTERNS.md.
   -------------------------------------------------------------------------- */

/* Outer wrapper — constrains max width, stacks sections vertically */
.bs-estimator {
  max-width: 720px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: var(--bs-space-8);
}

/* Each step is a <fieldset> — browser resets needed because fieldsets
   have opinionated default margins/borders across browsers. */
.bs-estimator__section {
  border: 1px solid var(--bs-color-neutral-200);
  border-radius: var(--bs-radius-lg);
  padding: var(--bs-space-6);
  margin: 0;  /* reset fieldset default margin */
}

/* Section legend — styled as a heading-like label with step number.
   Using display:flex to align the step circle + text in one line. */
.bs-estimator__section-title {
  display: flex;
  align-items: center;
  gap: var(--bs-space-3);
  font-size: var(--bs-font-size-lg);
  font-weight: var(--bs-font-weight-bold);
  color: var(--bs-color-ink);
  padding-inline: var(--bs-space-2);  /* tiny inset so it clears the border visually */
  margin-bottom: var(--bs-space-3);
}

/* Numbered circle — visual step indicator, aria-hidden in HTML */
.bs-estimator__step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--bs-radius-full);
  background: var(--bs-color-primary);
  color: #fff;
  font-size: var(--bs-font-size-sm);
  font-weight: var(--bs-font-weight-bold);
  flex-shrink: 0;
}

/* Optional badge — inline label on section legend for optional sections */
.bs-estimator__optional-badge {
  margin-left: auto;  /* push to right of legend */
  font-size: var(--bs-font-size-xs);
  font-weight: var(--bs-font-weight-normal);
  color: var(--bs-color-neutral-500);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  background: var(--bs-color-neutral-100);
  border-radius: var(--bs-radius-full);
  padding: 2px var(--bs-space-3);
}

/* Section description — helper text below the legend */
.bs-estimator__section-desc {
  font-size: var(--bs-font-size-sm);
  color: var(--bs-color-neutral-700);
  margin-bottom: var(--bs-space-4);
}

/* ── Option label card variant (sealing type radios) ──────────────────────
   Extends .bs-option-label from forms.css with a card-like border treatment.
   This variant is estimator-specific so it lives here, not in forms.css. */
.bs-option-label--card {
  display: flex;
  align-items: flex-start;
  gap: var(--bs-space-4);
  padding: var(--bs-space-4);
  border: 2px solid var(--bs-color-neutral-200);
  border-radius: var(--bs-radius-md);
  cursor: pointer;
  transition: border-color var(--bs-transition-fast),
              background   var(--bs-transition-fast);
  margin-bottom: var(--bs-space-3);
}

.bs-option-label--card:last-child {
  margin-bottom: 0;
}

/* Highlight the card when the radio inside is checked */
.bs-option-label--card:has(:checked) {
  border-color: var(--bs-color-primary);
  background: color-mix(in srgb, var(--bs-color-primary) 8%, transparent);
}

.bs-option-label--card:hover {
  border-color: var(--bs-color-primary);
}

.bs-option-label__input {
  margin-top: 3px;  /* optical alignment with the first line of text */
  flex-shrink: 0;
  accent-color: var(--bs-color-primary);
}

.bs-option-label__body {
  display: flex;
  flex-direction: column;
  gap: var(--bs-space-1);
}

.bs-option-label__title {
  font-weight: var(--bs-font-weight-bold);
  color: var(--bs-color-ink);
}

.bs-option-label__desc {
  font-size: var(--bs-font-size-sm);
  color: var(--bs-color-neutral-700);
}

/* ── Toggle variant (parking area checkbox) ───────────────────────────────
   Simpler: single-line, no description, just a toggleable card. */
.bs-option-label--toggle {
  display: flex;
  align-items: center;
  gap: var(--bs-space-3);
  padding: var(--bs-space-3) var(--bs-space-4);
  border: 2px solid var(--bs-color-neutral-200);
  border-radius: var(--bs-radius-md);
  cursor: pointer;
  transition: border-color var(--bs-transition-fast),
              background   var(--bs-transition-fast);
  margin-bottom: var(--bs-space-4);
}

.bs-option-label--toggle:has(:checked) {
  border-color: var(--bs-color-primary);
  background: color-mix(in srgb, var(--bs-color-primary) 8%, transparent);
}

.bs-option-label--toggle:hover {
  border-color: var(--bs-color-primary);
}

/* Reveal region — toggled visible/hidden by estimator.js in Chat 21 */
.bs-estimator__reveal {
  padding-top: var(--bs-space-4);
  border-top: 1px solid var(--bs-color-neutral-100);
}

.bs-estimator__reveal[hidden] {
  display: none;
}

/* ── Condition slider ─────────────────────────────────────────────────────
   Styling a range input cross-browser is notoriously inconsistent.
   We style the track and thumb using vendor-prefixed selectors and
   a consistent visual language — matches the brand orange. */
.bs-estimator__slider-wrap {
  padding-bottom: var(--bs-space-6);  /* room for tick labels below */
}

.bs-estimator__slider {
  width: 100%;
  height: 6px;
  appearance: none;
  -webkit-appearance: none;
  background: var(--bs-color-neutral-200);
  border-radius: var(--bs-radius-full);
  outline: none;
  cursor: pointer;
  accent-color: var(--bs-color-primary);
}

/* Webkit track fill — browsers use a gradient trick to show progress */
.bs-estimator__slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--bs-radius-full);
}

/* Thumb — the draggable handle */
.bs-estimator__slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 24px;
  height: 24px;
  border-radius: var(--bs-radius-full);
  background: var(--bs-color-primary);
  border: 2px solid #fff;
  box-shadow: var(--bs-shadow-sm);
  margin-top: -9px;  /* vertically center on the track (track is 6px, thumb 24px) */
  cursor: grab;
}

.bs-estimator__slider::-moz-range-thumb {
  width: 24px;
  height: 24px;
  border-radius: var(--bs-radius-full);
  background: var(--bs-color-primary);
  border: 2px solid #fff;
  box-shadow: var(--bs-shadow-sm);
  cursor: grab;
}

.bs-estimator__slider:focus-visible {
  outline: 3px solid var(--bs-color-secondary);
  outline-offset: 4px;
  border-radius: var(--bs-radius-full);
}

/* Tick labels — 5 evenly-spaced labels using flexbox justify-content:space-between.
   Space-between puts first label at 0% and last at 100%, matching slider stops.
   Works for exactly 5 ticks — if tick count changes, this math changes. */
.bs-estimator__slider-ticks {
  display: flex;
  justify-content: space-between;
  margin-top: var(--bs-space-2);
}

.bs-estimator__slider-ticks span {
  font-size: var(--bs-font-size-xs);
  color: var(--bs-color-neutral-500);
  text-align: center;
  /* Each tick label is at most 1/5 of the width so they don't overlap */
  flex: 0 0 20%;
}

.bs-estimator__slider-ticks span:first-child {
  text-align: left;
}

.bs-estimator__slider-ticks span:last-child {
  text-align: right;
}

/* ── Dimension input grid ─────────────────────────────────────────────────
   Length × width inputs with an × divider symbol between them.
   Collapses to a stacked layout on mobile. */
.bs-estimator__dim-grid {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: end;  /* aligns the × with the bottoms of the inputs */
  gap: var(--bs-space-4);
}

@media (max-width: 480px) {
  /* Stack vertically on very small screens — hide the × divider on mobile
     because stacked inputs don't need it visually. */
  .bs-estimator__dim-grid {
    grid-template-columns: 1fr;
  }

  .bs-estimator__dim-separator {
    display: none;
  }
}

.bs-estimator__dim-separator {
  font-size: var(--bs-font-size-2xl);
  color: var(--bs-color-neutral-400);
  padding-bottom: var(--bs-space-2);  /* optically align with input bottom border */
  text-align: center;
}

.bs-estimator__dim-input {
  width: 100%;
}

/* ── Add-ons grid ─────────────────────────────────────────────────────────
   Two-column at ≥480px, stacked below */
.bs-estimator__addons-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--bs-space-4);
}

@media (min-width: 480px) {
  .bs-estimator__addons-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.bs-estimator__addon-input {
  width: 100%;
}

/* Rate hint — tiny "($ / unit)" note in the label */
.bs-estimator__rate-hint {
  font-size: var(--bs-font-size-xs);
  font-weight: var(--bs-font-weight-normal);
  color: var(--bs-color-neutral-500);
  margin-left: var(--bs-space-1);
}

/* ── Output card ──────────────────────────────────────────────────────────
   Extends .bs-card. Prominently displays the estimate range.
   Populated by estimator.js in Chat 21. */
.bs-estimator__output {
  text-align: center;
}

/* Placeholder shown before dimensions are entered */
.bs-estimator__output-placeholder {
  padding: var(--bs-space-8) 0;
}

.bs-estimator__output-hint {
  color: var(--bs-color-neutral-500);
  font-style: italic;
}

/* Result shown after estimator.js calculates */
.bs-estimator__output-result[hidden] {
  display: none;
}

.bs-estimator__output-label {
  font-size: var(--bs-font-size-sm);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--bs-color-neutral-500);
  margin-bottom: var(--bs-space-2);
}

/* Range display — the "$270 – $330" number. Large and prominent. */
.bs-estimator__output-range {
  font-size: var(--bs-font-size-4xl);
  font-weight: var(--bs-font-weight-bold);
  color: var(--bs-color-primary);
  line-height: 1.1;
  margin-bottom: var(--bs-space-3);
}

.bs-estimator__output-disclaimer {
  font-size: var(--bs-font-size-sm);
  color: var(--bs-color-neutral-500);
  margin-bottom: var(--bs-space-6);
}

/* ── "How it works" steps list ───────────────────────────────────────────
   Ordered list with generous spacing. No custom counters — browser defaults. */
.bs-estimator__steps-list {
  padding-left: var(--bs-space-6);
  display: flex;
  flex-direction: column;
  gap: var(--bs-space-4);
  margin-bottom: var(--bs-space-6);
}

.bs-estimator__steps-list li {
  font-size: var(--bs-font-size-base);
  line-height: var(--bs-line-height-base);
}


/* ==========================================================================
   SVG Service-Area Map — .town-path interactivity (Chat 25b)
   Paths are transparent by default (fill-opacity="0" set in brockport-map.html).
   These rules give them visible hover/focus states without touching the SVG.

   Architecture note: styles live here (not inline in service-areas.html) so
   they follow the project's stylesheet-first pattern and can be extended in
   future chats without touching HTML.
   ========================================================================== */

/* Wrapper — constrains the inline SVG to the same max-width used by the
   placeholder div it replaced, centered in the section container. */
.bs-map-wrap {
  width: 100%;
  max-width: 860px;
  margin: 0 auto;
}

/* The SVG itself — fluid width, preserves aspect ratio. */
.bs-map-wrap svg {
  width: 100%;
  height: auto;
  display: block;
}

/* Town path hit targets — hover fill using brand primary at low opacity so
   the existing county color (#fff) and grid lines show through underneath.
   transition-property targets only fill so we don't inadvertently animate
   stroke or other properties added in future chats. */
.town-path {
  transition: fill-opacity var(--bs-transition-fast);
  cursor: pointer;
}

.town-path:hover,
.town-path:focus {
  fill: var(--bs-color-primary);
  fill-opacity: 0.25;
  outline: none;           /* focus ring handled below via :focus-visible */
}

/* Keyboard focus ring — uses outline on the SVG <a> wrapper since SVG paths
   don't render outlines reliably cross-browser. The <a> wrapping each path
   receives the :focus-visible event. */
.town-link:focus-visible {
  outline: 3px solid var(--bs-color-primary);
  outline-offset: 2px;
}

/* Orleans placeholder links — same hover style as Monroe. The `cursor:pointer`
   still signals interactivity; actual pages land in Chat 26+. */
