@charset "UTF-8";
/* ==========================================================================
   Roxy Glass, Inc. | Design system and site styles
   --------------------------------------------------------------------------
   One stylesheet, one set of tokens. Page templates carry no inline styles.

   Palette note. The brand palette is built on architectural glass blues,
   a deep navy ink and cool neutrals. Green is reserved for verified and
   available states. Amber through red is reserved for urgency, warning and
   error, and is always paired with text or an icon so color is never the
   only signal. No purple, violet, lavender, lilac, plum, purple leaning
   indigo or purple leaning magenta appears anywhere in this file.

   Contents
     1.  Tokens
     2.  Reset and base
     3.  Layout primitives
     4.  Typography
     5.  Buttons and links
     6.  Header and navigation
     7.  Hero
     8.  Sections and background rhythm
     9.  Cards and grids
     10. Trust, proof and credentials
     11. Answer blocks, tables and lists
     12. Accordions and tabs
     13. Interactive tools
     14. Forms
     15. Footer
     16. Utilities, states and motion
   ========================================================================== */

/* ==========================================================================
   1. Tokens
   ========================================================================== */

:root {
  /* Brand ------------------------------------------------------------- */
  --ink: #0c1b2a;
  --ink-soft: #161c22;
  --ink-muted: #242b31;

  --brand: #0a5aa8;
  --brand-dark: #084b8e;
  --brand-darker: #063a6e;
  --brand-tint: #e8f1fa;
  --brand-tint-2: #cfe2f5;

  --accent: #0b6e7f;
  --accent-tint: #e4f3f5;

  /* On the deep panels. The closing call to action band and the footer sit on
     navy, where the brand blue is too close to the ground to read. These are
     the lifted counterparts, checked for contrast against #0c1b2a. */
  --on-dark: #ffffff;
  --on-dark-soft: #d7e6f3;
  --on-dark-muted: #9fb8cf;
  --brand-light: #7cc0f7;
  --accent-light: #63cddb;

  /* Status ------------------------------------------------------------ */
  --ok: #15803d;
  --ok-tint: #e9f7ef;
  --warn: #052d49;
  --warn-tint: #fdf1e3;
  --danger: #b42318;
  --danger-tint: #fdeceb;

  /* Neutrals ---------------------------------------------------------- */
  --surface: #ffffff;
  --surface-2: #f6f9fc;
  --surface-3: #edf2f7;
  --line: #d8e2ec;
  --line-strong: #b9c9d9;

  /* Typography -------------------------------------------------------- */
  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, "Noto Sans", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  --step--1: clamp(0.84rem, 0.82rem + 0.10vw, 0.90rem);
  --step-0: clamp(1.00rem, 0.97rem + 0.16vw, 1.10rem);
  --step-1: clamp(1.15rem, 1.09rem + 0.30vw, 1.33rem);
  --step-2: clamp(1.35rem, 1.24rem + 0.55vw, 1.72rem);
  --step-3: clamp(1.60rem, 1.40rem + 1.00vw, 2.25rem);
  --step-4: clamp(1.90rem, 1.55rem + 1.75vw, 3.00rem);
  --step-5: clamp(2.20rem, 1.65rem + 2.75vw, 3.75rem);

  /* Spatial rhythm. Every gap on the site is one of these steps. ------- */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4rem;
  --sp-9: 5rem;
  --sp-10: 6.5rem;

  --section-y: clamp(3rem, 2rem + 5vw, 5.5rem);

  /* Shape and depth --------------------------------------------------- */
  --radius-sm: 6px;
  --radius: 10px;
  --radius-lg: 16px;
  --radius-xl: 24px;

  --shadow-sm: 0 1px 2px rgba(12, 27, 42, 0.06),
    0 1px 3px rgba(12, 27, 42, 0.05);
  --shadow: 0 4px 10px rgba(12, 27, 42, 0.06),
    0 12px 28px rgba(12, 27, 42, 0.07);
  --shadow-lg: 0 10px 24px rgba(12, 27, 42, 0.09),
    0 28px 60px rgba(12, 27, 42, 0.10);

  --container: 1180px;
  --measure: 68ch;

  --ring: 0 0 0 3px #ffffff, 0 0 0 6px var(--brand);

  --t-fast: 130ms;
  --t-base: 200ms;
  --t-slow: 380ms;
  --ease: cubic-bezier(0.32, 0.72, 0.28, 1);

  --header-h: 68px;
}

@media (min-width: 1024px) {
  :root {
    --header-h: 84px;
  }
}

/* ==========================================================================
   2. Reset and base
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + var(--sp-4));
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--step-0);
  line-height: 1.65;
  color: var(--ink-soft);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

/* Reusable inline icons carry their own outline drawing rules in the SVG.
   This class gives them a predictable text-sized box when a component does
   not provide a larger icon container of its own. */
.roxy-icon {
  width: 1em;
  height: 1em;
  flex: none;
}

/* Fluid embeds must not push the page sideways on a narrow screen. */
iframe {
  max-width: 100%;
  border: 0;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 0 var(--sp-4);
  color: var(--ink);
  line-height: 1.2;
  font-weight: 700;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

p,
ul,
ol,
dl,
table,
figure,
blockquote {
  margin: 0 0 var(--sp-5);
}

p:last-child,
ul:last-child,
ol:last-child {
  margin-bottom: 0;
}

p {
  text-wrap: pretty;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--sp-7) 0;
}

strong,
b {
  font-weight: 650;
  color: var(--ink);
}

abbr[title] {
  text-decoration-style: dotted;
  cursor: help;
}

::selection {
  background: var(--brand-tint-2);
  color: var(--ink);
}

/* One visible focus treatment for the whole site. It is drawn outside the
   element with a white inner ring, so it stays visible on white cards, on
   tinted panels and on the dark footer. */
:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--radius-sm);
}

.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: var(--sp-4);
  z-index: 200;
  padding: var(--sp-3) var(--sp-5);
  background: var(--ink);
  color: #fff;
  font-weight: 600;
  border-radius: var(--radius);
  transform: translateY(-200%);
  transition: transform var(--t-base) var(--ease);
}

.skip-link:focus {
  transform: translateY(0);
}

/* ==========================================================================
   3. Layout primitives
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--sp-5);
}

@media (min-width: 768px) {
  .container {
    padding-inline: var(--sp-6);
  }
}

.container--narrow {
  max-width: 1024px;
}

.prose {
  max-width: var(--measure);
}

.stack>*+* {
  margin-top: var(--sp-5);
}

.stack-sm>*+* {
  margin-top: var(--sp-3);
}

.stack-lg>*+* {
  margin-top: var(--sp-6);
}

.cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
}

/* ==========================================================================
   Layout children cannot stretch the page
   --------------------------------------------------------------------------
   A grid or flex item is min-width:auto by default, which means it refuses
   to become narrower than its widest content. One data table with a 520px
   minimum width was therefore stretching its column, its section and the
   whole document to 596px inside a 390px phone, and every heading and
   paragraph on the page ran off the right edge with it.

   Setting min-width:0 lets the column be as narrow as the screen and hands
   the overflow back to the element that is meant to own it, which is the
   scrolling wrapper around the table.

   This is the correct fix rather than overflow-x:hidden on the body. Hiding
   it on the body would mask the same bug in future, and it would break the
   sticky navigation bar, because a scroll container ancestor stops position
   sticky from working.
   ========================================================================== */

.grid > *,
.split > *,
.hero__grid > *,
.hero__aside > *,
.page-hero__grid > *,
.footer-grid > *,
.tool__step > * {
  min-width: 0;
}

.split {
  display: grid;
  gap: var(--sp-7);
}

@media (min-width: 900px) {
  .split {
    grid-template-columns: 1fr 1fr;
    align-items: center;
  }

  .split--wide-left {
    grid-template-columns: 1.25fr 1fr;
  }

  .split--wide-right {
    grid-template-columns: 1fr 1.25fr;
  }

  .split--aside {
    grid-template-columns: minmax(0, 1fr) 340px;
    align-items: start;
  }
}

/* ==========================================================================
   4. Typography
   ========================================================================== */

.h1,
h1 {
  font-size: var(--step-5);
  letter-spacing: -0.025em;
}

.h2,
h2 {
  font-size: var(--step-4);
  letter-spacing: -0.02em;
}

.h3,
h3 {
  font-size: var(--step-2);
}

.h4,
h4 {
  font-size: var(--step-1);
}

.eyebrow {
  display: inline-block;
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--brand);
  margin-bottom: var(--sp-3);
}

.lede {
  font-size: var(--step-1);
  line-height: 1.6;
}

.section-head {
  max-width: 760px;
  margin-bottom: var(--sp-7);
}

.section-head--center {
  margin-inline: auto;
  text-align: center;
}

.muted {
  color: var(--ink-muted);
}

.small {
  font-size: var(--step--1);
}

/* ==========================================================================
   5. Buttons and links
   ========================================================================== */

a {
  color: var(--brand-dark);
  text-decoration-color: var(--brand-tint-2);
  text-underline-offset: 0.18em;
  text-decoration-thickness: 0.08em;
  transition: color var(--t-fast) var(--ease),
    text-decoration-color var(--t-fast) var(--ease);
}

a:hover {
  color: var(--brand-darker);
  text-decoration-color: currentColor;
}

.btn {
  --btn-bg: var(--brand);
  --btn-fg: #ffffff;
  --btn-bd: var(--brand);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 48px;
  padding: var(--sp-3) var(--sp-6);
  border: 1px solid var(--btn-bd);
  border-radius: var(--radius);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: inherit;
  font-weight: 650;
  line-height: 1.25;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease),
    box-shadow var(--t-fast) var(--ease);
}

.btn:hover {
  --btn-bg: var(--brand-dark);
  --btn-bd: var(--brand-dark);
  color: #fff;
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}

.btn:active {
  transform: translateY(0);
  box-shadow: none;
}

.btn--secondary {
  --btn-bg: #ffffff;
  --btn-fg: var(--brand-dark);
  --btn-bd: var(--line-strong);
}

.btn--secondary:hover {
  --btn-bg: var(--brand-tint);
  --btn-bd: var(--brand);
  color: var(--brand-darker);
}

.btn--urgent {
  --btn-bg: var(--warn);
  --btn-bd: var(--warn);
}

.btn--urgent:hover {
  --btn-bg: #052d49;
  --btn-bd: #052d49;
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: #ffffff;
  --btn-bd: rgba(255, 255, 255, 0.55);
}

.btn--ghost:hover {
  --btn-bg: rgba(255, 255, 255, 0.12);
  --btn-bd: #fff;
  color: #fff;
}

.btn--lg {
  min-height: 56px;
  padding: var(--sp-4) var(--sp-7);
  font-size: var(--step-1);
}

.btn--full {
  width: 100%;
}

.btn[disabled],
.btn[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.55;
  transform: none;
  box-shadow: none;
}

.btn__icon {
  flex: none;
  width: 1.15em;
  height: 1.15em;
  fill: currentColor;
}

/* A link that reads as the next step rather than as a button. */
.link-next {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 650;
  text-decoration: none;
}

.link-next::after {
  content: "";
  width: 0.7em;
  height: 0.7em;
  border-right: 2px solid currentColor;
  border-top: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--t-fast) var(--ease);
}

.link-next:hover::after {
  transform: rotate(45deg) translate(2px, -2px);
}

/* ==========================================================================
   6. Header and navigation
   ========================================================================== */

.site-header {
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(12, 27, 42, 0.05);
}

/* Tier 1: Top Utility Bar */
.top-utility-bar {
  display: none;
  background: var(--ink);
  color: #fff;
  font-size: var(--step--1);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

@media (min-width: 1024px) {
  .top-utility-bar {
    display: block;
  }
}

.top-utility-bar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 40px;
}

/* The two clusters sit on one baseline. Items inside the left cluster are
   separated by a thin rule rather than by whitespace alone, so the strip
   reads as one row of related facts instead of three floating fragments. */
.top-utility-bar__left,
.top-utility-bar__right {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-width: 0;
}

.top-utility-bar__left > * + * {
  padding-inline-start: var(--sp-4);
  border-inline-start: 1px solid rgba(255, 255, 255, 0.18);
}

.utility-status {
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  white-space: nowrap;
}
.utility-status--open { color: #4ade80; } /* green-400 */
.utility-status--closed { color: #f87171; } /* red-400 */

.utility-tag {
  background: rgba(255, 255, 255, 0.15);
  padding: 2px 8px;
  border-radius: 4px;
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
}

/* The tag carries its own background, so the rule before it would crowd the
   pill. The gap alone is enough separation there. */
.top-utility-bar__left > .utility-tag {
  border-inline-start: 0;
  padding-inline-start: 0;
}

.utility-value {
  color: rgba(255, 255, 255, 0.8);
  white-space: nowrap;
}

/* The credential line is the first thing to go when the strip runs out of
   room. It is the longest item in the row, and the same three credentials
   are shown in full in the branding bar directly underneath it. */
@media (max-width: 1199px) {
  .utility-value {
    display: none;
  }
}

.utility-link {
  white-space: nowrap;
}

.utility-link {
  color: #fff;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-weight: 600;
  transition: opacity var(--t-fast) var(--ease);
}
.utility-link:hover {
  opacity: 0.8;
}
.utility-link--phone {
  font-weight: 700;
}

/* Mobile Top Utility */
/* Tier 2: Main Branding and Trust Banner
   --------------------------------------------------------------------------
   Three parts on one row: the logo, the credential cluster, and the two
   calls to action. The cluster takes whatever space is left in the middle and
   centres itself in it, so the bar reads identity, then proof, then action.

   Everything here is sized so the row still fits at 1024px, which is where
   the three part layout first appears. Nothing is hidden to make room. The
   logo, the button padding and the text on the second button each give up a
   little instead, because a credential that disappears at some window widths
   is not a credential a customer can rely on. */
.main-branding-bar {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.main-branding-bar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: 64px;
  padding-block: var(--sp-3);
}

@media (min-width: 1024px) {
  .main-branding-bar__inner {
    min-height: 88px;
    gap: var(--sp-5);
  }
}

.brand-link {
  display: inline-flex;
  align-items: center;
  flex: none;
  text-decoration: none;
  border-radius: var(--radius-sm);
}

/* The logo file is a wide banner, so its height decides how much of the row
   it eats. It steps up with the space available rather than holding 187px of
   a 360px phone. */
.brand-logo {
  display: block;
  width: auto;
  height: 56px;
}

@media (min-width: 480px) {
  .brand-logo {
    height: 62px;
  }
}

@media (min-width: 1280px) {
  .brand-logo {
    height: 72px;
  }
}

/* -- The credential cluster --------------------------------------------
   The cluster only joins the branding bar at 1200px. Below that the same
   badges are shown by the full width strip underneath instead.

   The reason is arithmetic. The container caps at 1180px, so the widest this
   row ever gets is 1116px of usable space, and the logo, three credentials
   and two calls to action do not fit in it much under 1200. The previous
   answer was to delete the reviews badge between 1024px and 1280px, which
   made a credential come and go with the window size. Moving the whole
   cluster down one tier keeps all three visible at every width, and gives
   them more room rather than less. */

.header-trust-cluster {
  display: none;
}

@media (min-width: 1200px) {
  .header-trust-cluster {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 1 auto;
    min-width: 0;
    gap: var(--sp-4);
    margin-inline: auto;
  }

  /* A short hairline between credentials, set to the height of the badge
     rather than the height of the bar. A full height rule made the cluster
     read as a table of three cells; this reads as one row of related proof
     and keeps the spacing even however wide each badge text runs. */
  .header-trust-cluster > * + * {
    position: relative;
    padding-inline-start: var(--sp-4);
  }

  .header-trust-cluster > * + *::before {
    content: "";
    position: absolute;
    top: 50%;
    inset-inline-start: 0;
    width: 1px;
    height: 32px;
    margin-top: -16px;
    background: var(--line);
  }
}

.trust-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  border-radius: var(--radius-sm);
  color: var(--ink);
  text-decoration: none;
  transition: opacity var(--t-fast) var(--ease);
}

/* Two of the three credentials open a public record. They get the same quiet
   hover as the utility links, so it is discoverable that they can be checked. */
a.trust-badge:hover {
  opacity: 0.75;
}

/* Every credential mark sits in the same square, so three logos of three
   different shapes still line up on one baseline and carry the same visual
   weight. Before this each image was forced to 40px tall, which made the
   tall narrow BBB mark tower over the others. */
.trust-badge__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 34px;
  height: 34px;
  color: var(--brand);
}

.trust-badge__mark img,
.trust-badge__mark svg {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* Each credential is a two line block: what it is, then the reference that
   proves it. Both lines stay on one line each. Allowing them to wrap made
   the badges different heights and pushed the row out of alignment, which
   is the opposite of what a credential bar is for. */
.trust-text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  min-width: 0;
  line-height: 1.2;
  white-space: nowrap;
}

.trust-text strong {
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--ink);
}

.trust-text small {
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* The rating badge uses the same two line block as the other credentials, so
   the score and stars sit on the top line where BBB A+ sits, and the review
   count sits on the small line where ACCREDITED sits. That is what keeps the
   cluster flush instead of stair stepped. */
.trust-rating__top {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

.trust-rating__score {
  font-size: 0.95rem;
  font-weight: 800;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* The stars are drawn to the rating, not stamped from a five star picture,
   so a 4.6 shows four and a bit. See roxy_star_meter() in includes/reviews.php. */
.star-meter {
  display: block;
  flex: none;
  width: auto;
  height: 14px;
}

/* The same stars in running text, on the reviews page and anywhere else
   roxy_rating_badge() is called. */
.rating-inline {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}

.rating-inline .star-meter {
  height: 16px;
}

/* -- The header calls to action ---------------------------------------- */

.header-actions {
  display: none;
}

@media (min-width: 1024px) {
  .header-actions {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    flex: none;
  }

  /* A phone number that wraps mid number is unreadable and made both header
     buttons two lines tall. These never wrap. */
  .header-actions .btn {
    padding-inline: var(--sp-4);
    font-size: 0.9375rem;
    white-space: nowrap;
  }
}

@media (min-width: 1280px) {
  .header-actions .btn {
    padding-inline: var(--sp-5);
    font-size: var(--step-0);
  }
}

/* -- The credential strip -----------------------------------------------
   Below 1200px this is where the credentials live, so it is not a mobile
   only component. It is the full width row under the branding bar. */

.mobile-trust-strip {
  display: block;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  padding-block: var(--sp-3);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* The badges are wider than a phone, so this strip scrolls sideways. Fading
     the trailing edge is what tells someone that, instead of leaving the last
     credential looking as though it had been chopped in half. */
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 32px), transparent 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 32px), transparent 100%);
}

.mobile-trust-strip::-webkit-scrollbar {
  display: none;
}

/* It stands down only once the branding bar is wide enough to carry the
   cluster itself. The two must swap at the same width or the credentials
   would appear twice, or not at all. */
@media (min-width: 1200px) {
  .mobile-trust-strip {
    display: none;
  }
}

.mobile-trust-strip__inner {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  width: max-content;
  padding-inline: var(--sp-5);
}

@media (min-width: 768px) {
  .mobile-trust-strip__inner {
    padding-inline: var(--sp-6);
  }
}

/* The same hairline as the desktop cluster, so the strip reads as one row of
   proof rather than badges floating eight pixels apart. */
.mobile-trust-strip__inner > * + * {
  position: relative;
  padding-inline-start: var(--sp-4);
}

.mobile-trust-strip__inner > * + *::before {
  content: "";
  position: absolute;
  top: 50%;
  inset-inline-start: 0;
  width: 1px;
  height: 28px;
  margin-top: -14px;
  background: var(--line);
}


/* Tier 3: Primary Navigation Bar */
/* This tier holds the navigation at every width. It used to be display:none
   below 1024px, which also hid the panel the mobile menu button opens,
   because #primary-nav lives inside it. Pressing the button set the right
   attributes and added the right class, and nothing could ever appear.

   Closed, the bar collapses to no height at all: the nav inside is hidden
   and the container has no vertical padding, so there is no empty strip
   under the header. */
.primary-nav-bar {
  display: block;
  background: var(--surface);
}

@media (min-width: 1024px) {
  .primary-nav-bar {

    /* Only this tier follows the page down. Sticking the whole three tier
       header would hold about 170 pixels of a desktop viewport for the
       entire visit. Sticking the navigation alone costs roughly 48 pixels
       and keeps every section of the site one click away from anywhere on
       a long service or city page.

       Mobile is deliberately left alone. It has less viewport to give, and
       it already carries the call and estimate buttons in the bar fixed to
       the bottom of the screen. */
    position: sticky;
    top: 0;
    z-index: 100;

    /* Once it detaches from the bar above it, the navigation is a white
       strip sitting on white page content. The rule and the shadow are what
       keep it reading as header furniture rather than as part of the page. */
    border-bottom: 1px solid var(--line);
    box-shadow: 0 1px 3px rgba(12, 27, 42, 0.06);
  }
}

/* These four rules were written when this bar was navy and were never
   updated when it went white. Every one of them painted white on white:
   the hover state was invisible, and box-shadow:none also cancelled the
   underline that marks the page you are on, so the navigation showed no
   current page anywhere on the site. */
.primary-nav-bar .nav-link {
  color: var(--link);
  padding: var(--sp-3) var(--sp-4);
  font-weight: 600;
  border-radius: 0;
}

.primary-nav-bar .nav-link:hover,
.primary-nav-bar .nav-link:focus-visible {
  background: var(--brand-tint);
  color: var(--brand-darker);
}

.primary-nav-bar .nav-link[aria-current="page"] {
  background: var(--brand-tint);
  color: var(--brand-darker);
  box-shadow: inset 0 -3px 0 var(--brand);
}

/* The current page marker and the focus ring are both box-shadows, and the
   marker is the more specific rule, so without this the one nav link a
   keyboard user is most likely to land on would lose its ring. */
.primary-nav-bar .nav-link[aria-current="page"]:focus-visible {
  box-shadow: inset 0 -3px 0 var(--brand), var(--ring);
}

.primary-nav-bar .submenu-toggle {
  color: var(--brand-dark);
}

.primary-nav-bar .submenu-toggle:hover {
  background: var(--brand-tint);
  color: var(--brand-darker);
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: var(--sp-2) var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  font-weight: 650;
  cursor: pointer;
}

@media (min-width: 1024px) {
  .nav-toggle {
    display: none;
  }
}

.nav-toggle__bars {
  position: relative;
  width: 18px;
  height: 12px;
}

.nav-toggle__bars span {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--ink);
  border-radius: 2px;
  transition: transform var(--t-base) var(--ease), opacity var(--t-fast) var(--ease);
}

.nav-toggle__bars span:nth-child(1) {
  top: 0;
}

.nav-toggle__bars span:nth-child(2) {
  top: 5px;
}

.nav-toggle__bars span:nth-child(3) {
  top: 10px;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(1) {
  transform: translateY(5px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(2) {
  opacity: 0;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(3) {
  transform: translateY(-5px) rotate(-45deg);
}

/* -- Primary navigation ------------------------------------------------ */

.primary-nav {
  display: none;
}

.primary-nav.is-open {
  display: block;
}

@media (min-width: 1024px) {

  .primary-nav,
  .primary-nav.is-open {
    display: block;
  }
}

.nav-list {
  list-style: none;
  margin: 0;
  padding: 0 0 var(--sp-5);
  border-top: 1px solid var(--line);
}

@media (min-width: 1024px) {
  .nav-list {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
    padding: 0;
    border-top: 0;
  }
}

.nav-item {
  position: relative;
  border-bottom: 1px solid var(--line);
  color: var(--brand-dark);
}

@media (min-width: 1024px) {
  .nav-item {
    border-bottom: 0;
  }
}

.nav-link {
  display: block;
  padding: var(--sp-4) 0;
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
}

@media (min-width: 1024px) {
  .nav-link {
    padding: var(--sp-2) var(--sp-4);
    border-radius: var(--radius-sm);
    white-space: nowrap;
  }

  .nav-link:hover {
    background: var(--brand-tint);
    color: var(--brand-darker);
  }
}

.nav-link[aria-current="page"] {
  color: var(--brand-dark);
}

@media (min-width: 1024px) {
  .nav-link[aria-current="page"] {
    background: var(--brand-tint);
    box-shadow: inset 0 -2px 0 var(--brand);
  }
}

/* A parent that both links to its hub and opens a submenu. The link and the
   toggle are separate controls, so the hub page is always reachable. */
.nav-parent {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
}

@media (min-width: 1024px) {
  .nav-parent {
    justify-content: flex-start;
    gap: 0;
  }
}

.nav-parent .nav-link {
  flex: 1 1 auto;
}

@media (min-width: 1024px) {
  .nav-parent .nav-link {
    flex: 0 0 auto;
    padding-right: var(--sp-2);
  }
}

.submenu-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 44px;
  height: 44px;
  padding: 0;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  cursor: pointer;
}

@media (min-width: 1024px) {
  .submenu-toggle {
    width: 30px;
    height: 34px;
    margin-right: var(--sp-2);
  }

  .submenu-toggle:hover {
    background: var(--brand-tint);
    color: var(--brand-darker);
  }
}

.submenu-toggle__chevron {
  width: 0.55em;
  height: 0.55em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--t-base) var(--ease);
}

.submenu-toggle[aria-expanded="true"] .submenu-toggle__chevron {
  transform: translateY(1px) rotate(-135deg);
}

.submenu {
  display: none;
  list-style: none;
  margin: 0;
  padding: 0 0 var(--sp-4) var(--sp-4);
}

.submenu.is-open {
  display: block;
}

@media (min-width: 1024px) {
  .submenu {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    z-index: 20;
    min-width: 290px;
    padding: var(--sp-3);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
  }

  .submenu.is-open {
    display: block;
  }
}

.submenu a {
  display: block;
  padding: var(--sp-3);
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  font-size: var(--step--1);
  font-weight: 600;
  text-decoration: none;
}

.submenu a:hover {
  background: var(--brand-tint);
  color: var(--brand-darker);
}

.submenu a[aria-current="page"] {
  color: var(--brand-dark);
  background: var(--brand-tint);
}

.submenu__overview {
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--sp-2);
  padding-bottom: var(--sp-2);
}

.submenu__overview a {
  color: var(--ink);
}

.nav-cta {
  padding: var(--sp-5) 0 0;
}

@media (min-width: 1024px) {
  .nav-cta {
    display: none;
  }
}

/* ==========================================================================
   Mobile navigation panel
   --------------------------------------------------------------------------
   Below 1024px the navigation is a panel that opens under the header from the
   menu button.

   These rules were originally written for a navy panel and set every border,
   hover and current page state in white. The bar has been white for some time,
   so all of them were painting white on white: the dividers between menu items
   were invisible, the current page was unmarked, and hovering or keyboard
   focusing a submenu link made its text vanish. They now use the same tint and
   rule tokens as the desktop navigation, so the two read as one component.

   Everything is scoped to max-width so that not one declaration in this block
   can reach the desktop layout.
   ========================================================================== */

@media (max-width: 1023.98px) {

  /* The panel can be taller than the screen once a submenu is open, so it
     scrolls on its own rather than pushing the page down forever. */
  .primary-nav.is-open {
    max-height: calc(100vh - 120px);
    max-height: calc(100dvh - 120px);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-block: var(--sp-2) var(--sp-4);
    border-top: 1px solid var(--line);
  }

  .nav-list {
    padding-bottom: 0;
    border-top: 0;
  }

  .nav-item {
    border-bottom: 1px solid var(--line);
  }

  .nav-item:last-child {
    border-bottom: 0;
  }

  /* A comfortable target. 44px is the documented minimum and these are the
     controls a person uses one handed on a phone, so they get more. */
  .primary-nav-bar .nav-link {
    padding-block: var(--sp-4);
    min-height: 48px;
    display: flex;
    align-items: center;
  }

  .primary-nav-bar .nav-link:hover,
  .primary-nav-bar .nav-link:focus-visible {
    background: var(--brand-tint);
    color: var(--brand-darker);
  }

  .primary-nav-bar .nav-link[aria-current="page"] {
    background: var(--brand-tint);
    color: var(--brand-darker);
    box-shadow: inset 3px 0 0 var(--brand);
  }

  .primary-nav-bar .nav-link[aria-current="page"]:focus-visible {
    box-shadow: inset 3px 0 0 var(--brand), var(--ring);
  }

  .primary-nav-bar .submenu {
    padding: 0 0 var(--sp-3) var(--sp-3);
    border-inline-start: 1px solid var(--line);
    margin-inline-start: var(--sp-3);
  }

  .primary-nav-bar .submenu a {
    color: var(--ink-soft);
    min-height: 44px;
    display: flex;
    align-items: center;
  }

  .primary-nav-bar .submenu a:hover,
  .primary-nav-bar .submenu a:focus-visible {
    background: var(--brand-tint);
    color: var(--brand-darker);
  }

  .primary-nav-bar .submenu a[aria-current="page"] {
    background: var(--brand-tint);
    color: var(--brand-darker);
    font-weight: 700;
  }

  .primary-nav-bar .submenu__overview {
    border-bottom: 1px solid var(--line);
  }

  .primary-nav-bar .submenu__overview a {
    color: var(--ink);
    font-weight: 700;
  }

  /* The menu opens in the tier below the trust strip, so the strip is taken
     out of the way while it is open. That puts the panel directly under the
     button that opened it, which is where a person looks for it. */
  .site-header:has(.nav-toggle[aria-expanded="true"]) .mobile-trust-strip {
    display: none;
  }

  /* The open button reads as pressed. */
  .nav-toggle[aria-expanded="true"] {
    background: var(--ink);
    border-color: var(--ink);
    color: #fff;
  }

  .nav-toggle[aria-expanded="true"] .nav-toggle__bars span {
    background: #fff;
  }
}

/* ==========================================================================
   7. Hero
   --------------------------------------------------------------------------
   One hero, used by the home page and by every service page.

   It is light. The photographs the hero was built around are squares with a
   soft white edge baked into the picture file, so they only resolve correctly
   on white: on any darker ground that edge reads as a halo around a pasted
   rectangle. Light is also the right choice for the content, because the
   subject is daylight through glass, and a page that is itself bright is a
   better frame for that than a dark one.

   Depth comes from three quiet layers rather than from a dark fill: a cool
   vertical wash, one soft brand bloom in the top right, and glazing bars at
   very low opacity. None of them carries meaning, so all three are hidden
   from assistive technology in the markup.

   Contrast, measured against the palest point of the wash, #ffffff:
     #0c1b2a  16.6:1   heading
     #33475b   9.0:1   answer paragraph and proof labels
     #5a6b7c   5.4:1   secondary notes
     #084b8e   8.2:1   the typed phrase and every link
     #0b6e7f   5.3:1   proof icons
   And against the deepest point, #eaf1f8:
     #0c1b2a  14.3:1   #33475b 7.7:1   #5a6b7c 4.6:1   #084b8e 7.0:1
   ========================================================================== */

.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  color: var(--ink-soft);
  background:
    linear-gradient(178deg,
      #ffffff 0%,
      #fbfdff 38%,
      #f2f7fc 72%,
      #eaf1f8 100%);
  padding-block:
    clamp(2rem, 1.4rem + 2.6vw, 3.75rem)
    clamp(3.75rem, 2.9rem + 3.2vw, 5.25rem);
}

.hero>.container {
  position: relative;
  z-index: 1;
}

/* --------------------------------------------------------------------------
   Backdrop
   -------------------------------------------------------------------------- */

.hero__backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

.hero__glow {
  position: absolute;
  top: -38%;
  right: -16%;
  width: 72%;
  min-width: 460px;
  aspect-ratio: 1;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%,
      rgba(10, 90, 168, 0.10) 0%,
      rgba(10, 90, 168, 0.05) 44%,
      rgba(10, 90, 168, 0) 70%);
}

.hero__glow::after {
  content: "";
  position: absolute;
  left: -60%;
  bottom: -80%;
  width: 94%;
  aspect-ratio: 1;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%,
      rgba(11, 110, 127, 0.07) 0%,
      rgba(11, 110, 127, 0) 68%);
}

.hero__mullions {
  position: absolute;
  inset: -1px;
  background-image:
    repeating-linear-gradient(90deg,
      rgba(12, 27, 42, 0.026) 0 1px, transparent 1px 148px),
    repeating-linear-gradient(0deg,
      rgba(12, 27, 42, 0.018) 0 1px, transparent 1px 148px);
  -webkit-mask-image: linear-gradient(185deg, rgba(0, 0, 0, 0.85) 6%, rgba(0, 0, 0, 0) 78%);
  mask-image: linear-gradient(185deg, rgba(0, 0, 0, 0.85) 6%, rgba(0, 0, 0, 0) 78%);
}

/* --------------------------------------------------------------------------
   Layout
   -------------------------------------------------------------------------- */

/* Three named areas rather than two blocks in source order.

   On a phone the order is copy, photograph, credentials. Putting the picture
   there means it arrives immediately after the call button instead of after
   three stacked credential rows, so the first thing a visitor sees on their
   first scroll is the work rather than more text. The call button itself
   stays above the fold, which is why the picture is not moved any higher.

   On a wide screen the copy and the credentials return to one column and the
   photograph fills the second, centered against both. */
.hero__grid {
  display: grid;
  gap: var(--sp-6);
  grid-template-areas:
    "body"
    "media"
    "aside";
}

.hero__body {
  grid-area: body;
  max-width: 40rem;
}

.hero__aside {
  grid-area: aside;
  min-width: 0;
  max-width: 40rem;
}

.hero__media {
  grid-area: media;
}

/* Scoped to .hero because /locations/newark/ uses .hero__grid inside a light
   page hero and must keep the plain single column behavior it has now. */
@media (min-width: 1000px) {
  .hero .hero__grid {
    grid-template-areas:
      "body  media"
      "aside media";
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
    grid-template-rows: auto auto;
    align-items: center;
    align-content: center;
    gap: var(--sp-5) var(--sp-7);
  }
}

/* On a phone and a tablet the hero is one column, and a full width square
   photograph would take most of the screen on its own. */
@media (max-width: 999px) {
  .hero__media {
    width: 100%;
    max-width: 24rem;
    margin-inline: auto;
  }
}

/* --------------------------------------------------------------------------
   Copy
   -------------------------------------------------------------------------- */

.hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin: 0 0 var(--sp-4);
  padding: 6px 14px 6px 11px;
  border: 1px solid var(--brand-tint-2);
  border-radius: 999px;
  background: var(--brand-tint);
  color: var(--brand-dark);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.07em;
  line-height: 1.3;
  text-transform: uppercase;
}

.hero__eyebrow::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px rgba(11, 110, 127, 0.16);
}

.hero__title {
  margin: 0 0 var(--sp-4);
  color: var(--ink);
  /* Its own scale rather than --step-5. At the site's largest heading size
     this headline runs to four lines in the hero's left column and pushes the
     call button off a 900px screen, which costs far more than the extra size
     is worth. */
  font-size: clamp(1.95rem, 1.32rem + 2.10vw, 2.95rem);
  line-height: 1.09;
  letter-spacing: -0.026em;
  text-wrap: balance;
}

/* --------------------------------------------------------------------------
   The rotating line under the heading

   The first phrase is in the HTML, so this line is complete and readable with
   the script blocked. assets/js/site.js reserves the height of the tallest
   phrase before it starts, which is why nothing below it moves as the text
   changes.
   -------------------------------------------------------------------------- */

.hero__rotator {
  /* Normal flow, and the lead-in on its own line above the phrase.

     Two earlier versions of this failed. A flex row moved the whole phrase
     down a line the moment it grew too long to sit beside the lead-in, so it
     jumped as it rotated. Inline flow fixed the jump but left the phrase
     wrapping at a different point for every phrase, which made the reserved
     height taller than most phrases needed and opened a gap under the short
     ones. Giving the phrase a line of its own means it wraps the same way
     every time at any width the site supports. */
  display: block;
  margin: 0 0 var(--sp-5);
  font-weight: 600;
  line-height: 1.28;
  letter-spacing: -0.012em;
}

.hero__rotator-lead {
  display: block;
  color: var(--ink-muted);
  font-size: clamp(1.02rem, 0.95rem + 0.30vw, 1.22rem);
  font-weight: 500;
}

.hero__rotator-text {
  display: inline-block;
  /* Keeps a line box while the phrase is empty between two words, so the
     cursor stays visible instead of the line collapsing to nothing. */
  min-height: 1em;
  color: var(--brand-dark);
  font-size: clamp(1.18rem, 0.95rem + 0.98vw, 1.78rem);
}

/* The cursor is the right border of the text itself rather than an element
   beside it. A separate cursor element is a box that moves with every
   character typed, and every one of those moves is a layout shift. A border
   belongs to a box whose left edge never moves, so the same cursor costs
   nothing. It appears only once the script takes over, so a page with no
   JavaScript never shows a cursor blinking beside text that will never be
   typed. */
.hero__rotator.is-typing .hero__rotator-text {
  border-right: 2px solid var(--accent);
  padding-right: 0.08em;
  animation: roxy-caret 1.05s steps(1, end) infinite;
}

@keyframes roxy-caret {
  0%,
  48% {
    border-right-color: var(--accent);
  }

  49%,
  100% {
    border-right-color: transparent;
  }
}

.hero__answer {
  max-width: 54ch;
  margin: 0 0 var(--sp-5);
  color: var(--ink-soft);
  /* Held closer to body size on a phone than --step-1 allows. This paragraph
     is the direct answer, so it has to be inside the first screen along with
     the button underneath it. */
  font-size: clamp(1rem, 0.96rem + 0.30vw, 1.20rem);
  line-height: 1.58;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
}

/* The pair shares one row when there is room and each button fills the row
   on its own when there is not.

   The natural widths do not fit at every breakpoint. Between 1000px and about
   1150px the hero's left column is under 550px, and on the urgent pages the
   first button reads "Call now: (844) 600-3960", so the pair is wider than
   the column no matter how the padding is tuned. Letting them grow means the
   row that does fit looks deliberate rather than ragged, and the row that
   does not wraps into two full width buttons rather than one full width and
   one short one. */
.hero__actions .btn {
  flex: 1 1 12rem;
}

/* The site's large button carries 48px of side padding. The pair then runs a
   few pixels wider than the hero's left column and the second one drops onto
   its own row, and on the urgent pages, where the first button reads "Call
   now" and carries a phone number, it drops at every desktop width. 24px
   keeps the pair together with room to spare and still reads as a large
   control. */
.hero .btn--lg {
  padding-inline: var(--sp-5);
}

.hero .btn .roxy-icon {
  width: 1.15em;
  height: 1.15em;
}

.hero__note {
  margin: var(--sp-5) 0 0;
  color: var(--ink-muted);
  font-size: var(--step--1);
}

/* --------------------------------------------------------------------------
   Proof, inside the first screen

   Three facts that can each be checked. They sit next to the call button
   because that is where a visitor decides whether calling is reasonable.
   -------------------------------------------------------------------------- */

.hero__proof {
  display: grid;
  gap: var(--sp-4);
  margin: 0;
  padding: var(--sp-5) 0 0;
  border-top: 1px solid var(--line);
  list-style: none;
}

@media (min-width: 560px) {
  .hero__proof {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--sp-4);
  }
}

.hero__proof-item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  min-width: 0;
}

.hero__proof-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid rgba(11, 110, 127, 0.22);
  border-radius: 9px;
  background: var(--accent-tint);
  color: var(--accent);
}

.hero__proof-icon svg {
  width: 18px;
  height: 18px;
}

.hero__proof-body {
  min-width: 0;
}

.hero__proof-body strong {
  display: block;
  color: var(--ink);
  font-size: var(--step--1);
  font-weight: 650;
  line-height: 1.35;
}

.hero__proof-note {
  display: block;
  color: var(--ink-muted);
  font-size: var(--step--1);
  line-height: 1.4;
}

.hero__proof-link {
  display: inline-block;
  font-size: var(--step--1);
}

/* --------------------------------------------------------------------------
   The photograph

   No frame, no border, no card. Nine of the twelve hero photographs are
   squares whose outer edge fades into white inside the picture file, and a
   box drawn around one of those fights the picture. The stylesheet feathers
   every hero edge the same way, so the two ordinary photographs in the set
   match the nine, and no hero anywhere ends in a hard rectangle.

   The mask is two gradients intersected. Where mask-composite is not
   supported the mask is simply ignored and the picture keeps its own edge,
   which is correct for nine of the twelve and acceptable for the other two.
   -------------------------------------------------------------------------- */

.hero-figure {
  position: relative;
  margin: 0;
}

.hero-figure__img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border: 1px solid #e3ebe9;
  border-radius: 22px;
  overflow: hidden;
  background: #ffffff;
}

/* The caption is a claim about the work, so it is attached to the picture
   that supports it rather than set as a line of text underneath. It sits
   inside the picture's own edge, clear of the feathered border. */
.hero-figure__tag {
  position: absolute;
  left: 6%;
  right: 6%;
  bottom: 7%;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.92);
  color: var(--ink);
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.35;
  box-shadow: var(--shadow);
  -webkit-backdrop-filter: blur(8px) saturate(1.1);
  backdrop-filter: blur(8px) saturate(1.1);
}

.hero-figure__tag svg {
  flex: none;
  width: 18px;
  height: 18px;
  color: var(--ok);
}

@media (min-width: 1000px) {
  .hero-figure__tag {
    left: auto;
    right: 2%;
    bottom: 9%;
    max-width: 82%;
  }
}

/* --------------------------------------------------------------------------
   Urgent services

   Board ups, storefront glass and window repair are opened by someone with a
   hole in their building. The panel keeps the same structure and changes the
   accent to amber, which is this site's urgency color, and it is always
   paired with the words 'Call now' rather than carrying the meaning alone.
   -------------------------------------------------------------------------- */

.hero--urgent .hero__eyebrow {
  border-color: rgba(180, 83, 10, 0.28);
  background: var(--warn-tint);
  color: #8a3f07;
}

.hero--urgent .hero__eyebrow::before {
  background: #b4530a;
  box-shadow: 0 0 0 3px rgba(180, 83, 10, 0.16);
}

/* Urgency on a light panel. White on #b4530a is 5.1:1, and the fill against
   the palest ground is 4.1:1, which clears both the 4.5:1 that text needs and
   the 3:1 that a control's edge needs. */
.btn--alert {
  --btn-bg: #b4530a;
  --btn-fg: #ffffff;
  --btn-bd: #b4530a;
}

.btn--alert:hover {
  --btn-bg: #96450a;
  --btn-bd: #96450a;
  color: #ffffff;
}

/* --------------------------------------------------------------------------
   The proof strip below the panel

   It is lifted into the bottom of the hero so the credentials read as part of
   the first screen rather than as the start of the next one. The extra bottom
   padding on .hero above is what makes room for it.
   -------------------------------------------------------------------------- */

.hero+.section--tight {
  padding-top: 0;
}

.hero+.section--tight .trust-strip {
  /* Positioned so it paints over the panel: .hero is an isolated stacking
     context, and without a z-index of its own this card would slide
     underneath it and lose its first line. */
  position: relative;
  z-index: 1;
  margin-top: clamp(-2.75rem, -1.9rem - 1.4vw, -1.75rem);
  box-shadow: var(--shadow-lg);
}

/* --------------------------------------------------------------------------
   Reduced motion

   The cursor stops blinking and the script stops retyping, so the line simply
   states the first phrase. Nothing else in the hero moves at any time.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {

  .hero__rotator.is-typing .hero__rotator-text {
    animation: none;
    border-right-color: transparent;
  }
}

/* Compact verified facts panel that sits beside the hero copy. */
.hero-facts {
  padding: var(--sp-5);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}

.hero-facts h2 {
  color: var(--ink);
  font-size: var(--step-0);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-bottom: var(--sp-4);
}

.hero-facts dl {
  display: grid;
  gap: var(--sp-3);
  margin: 0;
}

.hero-facts .fact {
  display: grid;
  gap: 2px;
}

.hero-facts dt {
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 650;
}

.hero-facts dd {
  margin: 0;
  color: var(--ink);
  font-weight: 600;
}

.hero-facts dd a {
  color: var(--brand);
}

/* Page hero for interior pages. Lighter, more compact. */
.page-hero {
  background-color: var(--surface);
  border-bottom: 1px solid var(--line);
  padding-block: clamp(2rem, 1.5rem + 3vw, 3.5rem);
}

.page-hero h1 {
  margin-bottom: var(--sp-4);
  color: var(--ink);
}

.page-hero__grid {
  display: grid;
  gap: var(--sp-6);
}

@media (min-width: 980px) {
  .page-hero__grid {
    grid-template-columns: 1.0fr 0.9fr;
    align-items: top;
  }
}

.page-hero__answer {
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: var(--measure);
}

.page-hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
}

.hero-image-wrapper {
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 4px 20px rgba(0,0,0,0.08);
}

.hero-image-wrapper img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  aspect-ratio: 4/3;
}

/* ==========================================================================
   8. Sections and background rhythm
   --------------------------------------------------------------------------
   Sections alternate between white, a cool tint and the deep ink panel. The
   rhythm marks where one idea ends and the next begins, which is what makes
   a long page readable rather than one continuous wall.
   ========================================================================== */

.section {
  padding-block: var(--section-y);
}

.section--tight {
  padding-block: clamp(2rem, 1.5rem + 3vw, 3.5rem);
}

.section--tint {
  background: var(--surface-2);
}

.section--brand {
  background: var(--brand-tint);
}

.section--line {
  border-top: 1px solid var(--line);
}

.section--ink {
  background:
    radial-gradient(900px 420px at 12% 0%, rgba(10, 90, 168, 0.28), transparent 60%),
    linear-gradient(160deg, #0c1b2a 0%, #0e2b48 100%);
  color: #cddcea;
}

.section--ink h2,
.section--ink h3,
.section--ink h4 {
  color: #fff;
}

.section--ink .eyebrow {
  color: #8fc4f0;
}

/* Not .btn. A button carries its own foreground, and letting this rule reach
   it turned the white label on the blue call button into pale blue on blue,
   which is 2.7:1. That was the one serious contrast failure on the site. */
.section--ink a:not(.btn) {
  color: #a8d0f5;
}

.breadcrumbs {
  padding-block: var(--sp-4);
  font-size: var(--step--1);
}

.breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  list-style: none;
  margin: 15px 18px;
  padding: 0;
}

.breadcrumbs li {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--ink-muted);
}

.breadcrumbs li+li::before {
  content: "";
  width: 0.4em;
  height: 0.4em;
  border-right: 1.5px solid var(--line-strong);
  border-top: 1.5px solid var(--line-strong);
  transform: rotate(45deg);
}

.breadcrumbs a {
  color: var(--ink-muted);
  text-decoration: none;
}

.breadcrumbs a:hover {
  color: var(--brand-dark);
  text-decoration: underline;
}

.breadcrumbs [aria-current="page"] {
  color: var(--ink);
  font-weight: 600;
}

/* ==========================================================================
   9. Cards and grids
   ========================================================================== */

.grid {
  display: grid;
  gap: var(--sp-5);
}

.grid--2 {
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

.grid--3 {
  grid-template-columns: repeat(auto-fit, minmax(288px, 1fr));
}

.grid--4 {
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}

/* A grid of teaser cards is marked up as a list, so that a screen reader
   announces how many items there are. This removes the list appearance
   without removing the list semantics. The explicit list-style on the item
   as well as the container is deliberate: Safari drops list semantics when
   list-style is none, and the role="list" is restored in the markup where
   that matters. */
.card-list,
.card-list > li {
  list-style: none;
}

.card-list {
  padding-left: 0;
  margin-block: 0;
}

.card {
  display: flex;
  flex-direction: column;
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  transition: border-color var(--t-base) var(--ease),
    box-shadow var(--t-base) var(--ease),
    transform var(--t-base) var(--ease);
}

.card h3 {
  font-size: var(--step-1);
  margin-bottom: var(--sp-2);
}

.card p {
  color: var(--ink-soft);
}

.card> :last-child {
  margin-top: 0;
  padding-top: var(--sp-4);
}

.card--link:hover {
  border-color: var(--brand-tint-2);
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}

.card--link:focus-within {
  border-color: var(--brand);
  box-shadow: var(--shadow);
}

/* The whole card is clickable, but only the heading link is in the tab order,
   so keyboard users get one stop per card rather than several. */
.card__title-link {
  text-decoration: none;
  color: inherit;
}

.card__title-link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.card--link {
  position: relative;
}

.card--link:focus-within {
  box-shadow: var(--shadow);
}

.card__title-link:focus-visible {
  box-shadow: none;
}

.card--link:has(.card__title-link:focus-visible) {
  box-shadow: var(--ring), var(--shadow);
}

.card__meta {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--step--1);
  font-weight: 650;
  color: var(--accent);
  margin-bottom: var(--sp-3);
}

.card--urgent {
  border-color: #f0d3b4;
  background: var(--warn-tint);
}

.card--urgent .card__meta {
  color: var(--warn);
}

.icon-tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  margin-bottom: var(--sp-4);
  background: var(--brand-tint);
  border-radius: var(--radius);
  color: var(--brand-dark);
  flex: none;
}

.icon-tile svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.card--urgent .icon-tile {
  background: #f8e3cb;
  color: var(--warn);
}

/* -- Service card with an illustration ----------------------------------
   The drawn service illustrations are a matched set on a white margin, so
   the media band sits on white rather than on a tint. A tinted band would
   show a visible square where the file ends.

   The band declares the illustration's own 3 by 2 ratio, so the space is
   reserved before the file arrives and nothing shifts as the grid loads.
   ---------------------------------------------------------------------- */

.card--service {
  padding: 0;
  overflow: hidden;
}

.card__media {
  display: block;
  aspect-ratio: 3 / 2;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  padding: 25px
}

.card__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-slow) var(--ease);
  padding: 25px
}

/* A slow, small push in on hover. It reads as the card responding rather
   than as an animation, and the reduced motion block below stops it. */
.card--service.card--link:hover .card__media img {
  transform: scale(1.035);
}

/* Fallback band for a service that has no illustration yet. It keeps the
   same height as its neighbors so one missing file cannot make a row of
   cards uneven. */
.card__media--icon {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--brand-tint);
}

.card__media--icon .icon-tile {
  margin-bottom: 0;
  width: 64px;
  height: 64px;
  background: var(--surface);
}

.card__media--icon .icon-tile svg {
  width: 32px;
  height: 32px;
}

.card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 20px;
}

/* .card > :last-child pushes the final element of a plain card to the
   bottom. On a media card the flex column is the body, so the rule has to
   apply one level in, and must be cancelled on the card itself. */
.card--service> :last-child {
  margin-top: 0;
  padding-top: 20px;
}

.card--service .card__body> :last-child {
  margin-top: auto;
  padding-top: 20px;
  margin-bottom: 0;
}

.card--service.card--urgent .card__media {
  border-bottom-color: #066986;
}

/* ==========================================================================
   10. Trust, proof and credentials
   ========================================================================== */

.trust-strip {
  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

@media (min-width: 760px) {
  .trust-strip {
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  }
}

.trust-item {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
}

.trust-item__icon {
  flex: none;
  width: 24px;
  height: 24px;
  color: var(--ok);
}

.trust-item__icon svg {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.trust-item__body {
  min-width: 0;
}

.trust-item strong {
  display: block;
  color: var(--ink);
  font-size: var(--step-0);
  line-height: 1.35;
}

.trust-item span {
  display: block;
  font-size: var(--step--1);
  color: var(--ink-muted);
}

.credential-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-4);
}

.credential {
  display: grid;
  gap: var(--sp-2);
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 4px solid var(--ok);
  border-radius: var(--radius);
}

.credential__label {
  font-weight: 700;
  color: var(--ink);
}

.credential__detail {
  color: var(--ink-soft);
  font-size: var(--step--1);
  margin: 0;
}

.credential__verify {
  font-size: var(--step--1);
  font-weight: 650;
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0.28em 0.7em;
  border-radius: 999px;
  font-size: var(--step--1);
  font-weight: 650;
  line-height: 1.4;
  border: 1px solid transparent;
}

.badge--ok {
  background: var(--ok-tint);
  color: #106431;
  border-color: #bfe3cd;
}

.badge--info {
  background: var(--brand-tint);
  color: var(--brand-darker);
  border-color: var(--brand-tint-2);
}

.badge--urgent {
  background: var(--warn-tint);
  color: #8a3f07;
  border-color: #f0d3b4;
}

.badge--neutral {
  background: var(--surface-3);
  color: var(--ink-soft);
  border-color: var(--line);
}

.badge__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

/* Open or closed status. Never color alone, the text always says which. */
.status-open {
  color: #106431;
  font-weight: 650;
}

.status-closed {
  color: var(--ink-muted);
  font-weight: 650;
}

/* ==========================================================================
   11. Answer blocks, tables and lists
   ========================================================================== */

/* The direct answer that opens a page. Written to stay correct when it is
   quoted on its own by an answer engine. */
.answer-block {
  padding: var(--sp-5) var(--sp-6);
  background: var(--brand-tint);
  border: 1px solid var(--brand-tint-2);
  border-left: 4px solid var(--brand);
  border-radius: var(--radius);
}

.answer-block> :first-child {
  margin-top: 0;
}

.answer-block p {
  color: var(--ink);
  font-size: var(--step-1);
  line-height: 1.6;
}

.callout {
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
}

.callout--warn {
  background: var(--warn-tint);
  border-color: #f0d3b4;
}

.callout--ok {
  background: var(--ok-tint);
  border-color: #bfe3cd;
}

.callout__title {
  display: block;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: var(--sp-2);
}

.checklist {
  list-style: none;
  margin: 0 0 var(--sp-5);
  padding: 0;
  display: grid;
  gap: var(--sp-3);
}

.checklist li {
  position: relative;
  padding-left: 2rem;
  line-height: 1.55;
}

.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.42em;
  width: 1.1rem;
  height: 0.6rem;
  border-left: 2.5px solid var(--ok);
  border-bottom: 2.5px solid var(--ok);
  transform: rotate(-45deg);
}

.numbered {
  counter-reset: step;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-5);
}

.numbered>li {
  counter-increment: step;
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: var(--sp-4);
  align-items: start;
}

.numbered>li::before {
  content: counter(step);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  background: var(--brand);
  color: #fff;
  font-weight: 750;
  border-radius: 50%;
}

.numbered h3 {
  font-size: var(--step-1);
  margin-bottom: var(--sp-2);
}

.numbered p {
  margin: 0;
}

.bullet-list {
  padding-left: 1.2rem;
}

.bullet-list li {
  margin-bottom: var(--sp-2);
}

.bullet-list li::marker {
  color: var(--brand);
}

/* -- Tables ------------------------------------------------------------- */

/* The wrapper is focusable in the markup so a keyboard can scroll it. This
   is the focus ring for it. */
.table-wrap:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--radius-sm);
}

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  -webkit-overflow-scrolling: touch;

  /* Without these the wrapper grows to fit the 520px minimum width of the
     table inside it, instead of holding the table and letting it scroll.
     See the note on layout children below. */
  max-width: 100%;
  min-width: 0;
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
  min-width: 520px;
}

table.data caption {
  padding: var(--sp-4) var(--sp-5);
  text-align: left;
  font-weight: 650;
  color: var(--ink);
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}

table.data th,
table.data td {
  padding: var(--sp-3) var(--sp-5);
  text-align: left;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

table.data thead th {
  background: var(--surface-2);
  color: var(--ink);
  font-weight: 700;
  white-space: nowrap;
}

table.data tbody th {
  font-weight: 650;
  color: var(--ink);
}

table.data tbody tr:last-child th,
table.data tbody tr:last-child td {
  border-bottom: 0;
}

table.data tbody tr:hover {
  background: var(--surface-2);
}

/* ==========================================================================
   12. Accordions and tabs
   ========================================================================== */

.faq {
  display: grid;
  gap: var(--sp-3);
}

.faq details {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color var(--t-base) var(--ease), box-shadow var(--t-base) var(--ease);
}

.faq details[open] {
  border-color: var(--brand-tint-2);
  box-shadow: var(--shadow-sm);
}

.faq summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  cursor: pointer;
  list-style: none;
  font-weight: 650;
  color: var(--ink);
  min-height: 48px;
  border-radius: var(--radius);
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary:hover {
  color: var(--brand-dark);
}

.faq summary::after {
  content: "";
  flex: none;
  margin-top: 0.42em;
  width: 0.6em;
  height: 0.6em;
  border-right: 2px solid var(--brand);
  border-bottom: 2px solid var(--brand);
  transform: rotate(45deg);
  transition: transform var(--t-base) var(--ease);
}

.faq details[open] summary::after {
  transform: rotate(-135deg);
}

.faq__body {
  padding: 0 var(--sp-5) var(--sp-5);
}

.faq__body> :first-child {
  margin-top: 0;
}

/* ==========================================================================
   13. Interactive tools
   ========================================================================== */

.tool {
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

@media (min-width: 720px) {
  .tool {
    padding: var(--sp-6);
  }
}

.tool__head {
  margin-bottom: var(--sp-5);
}

.tool__head h3 {
  margin-bottom: var(--sp-2);
}

.tool__head p {
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--step--1);
}

/* -- Featured tool ------------------------------------------------------
   The glass problem finder is the one place on the home page where a
   visitor does something rather than reads something, and it is easy to
   scroll past a quiet white panel. This variant gives it a brand edge, a
   little more depth, and two numbered steps, so that the page reads as
   "there is a choice to make here" before anybody reads a word of it.
   ---------------------------------------------------------------------- */

.tool--featured {
  position: relative;
  padding-top: calc(var(--sp-5) + 4px);
  border-color: var(--brand-tint-2);
  box-shadow: var(--shadow);
  overflow: hidden;
}

@media (min-width: 720px) {
  .tool--featured {
    padding-top: calc(var(--sp-6) + 4px);
  }
}

/* The brand edge along the top. Drawn as a element border rather than an
   extra div, so the markup stays about meaning. */
.tool--featured::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 4px;
  background: linear-gradient(90deg, var(--brand) 0%, var(--accent) 100%);
}

.tool__step {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-3) var(--sp-4);
  align-items: start;
}

.tool__step + .tool__step {
  margin-top: var(--sp-6);
}

/* The connecting rule between step one and step two. It makes the two
   steps read as one sequence rather than two unrelated panels. */
.tool__step--answer {
  position: relative;
  padding-top: var(--sp-6);
  border-top: 1px solid var(--line);
}

.tool__step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  font-weight: 800;
  font-size: var(--step--1);
  font-variant-numeric: tabular-nums;
  background: var(--brand);
  color: #fff;
  flex: none;
  transition: background-color var(--t-base) var(--ease),
    color var(--t-base) var(--ease),
    box-shadow var(--t-base) var(--ease);
}

.tool__step-title {
  margin: 0 0 var(--sp-4);
  font-size: var(--step-1);
  line-height: 1.25;
  align-self: center;
}

.tool__step-body {
  min-width: 0;
}

/* Step two stays visibly inactive until a choice has been made, so the
   sequence explains itself. It is a color and weight change only. The
   panel underneath is always readable, and its text already says what to
   do, so nothing is hidden from anyone. */
.tool--featured:not(:has(.option input:checked)) .tool__step--answer .tool__step-num {
  background: var(--surface-3);
  color: var(--ink-muted);
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

/* Until something is picked, step one breathes gently to draw the eye. It
   is a slow, low contrast pulse on a 36px circle, not a flash. It stops
   the moment a choice is made, and never runs under reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .tool--featured:not(:has(.option input:checked)) .tool__step--choose .tool__step-num {
    animation: roxy-step-pulse 2.6s var(--ease) infinite;
  }
}

@keyframes roxy-step-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(10, 90, 168, 0.42); }
  50%      { box-shadow: 0 0 0 9px rgba(10, 90, 168, 0); }
}

/* -- Option chips used by the glass problem finder ---------------------- */

.option-grid {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

/* The finder sits inside a step body, which owns the spacing below it.
   Anywhere else the grid keeps its own bottom margin. */
.tool__step-body .option-grid {
  margin-bottom: 0;
}

.option {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease),
    background-color var(--t-fast) var(--ease),
    box-shadow var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease);
}

.option:hover {
  border-color: var(--brand-tint-2);
  background: var(--brand-tint);
  box-shadow: var(--shadow-sm);
  transform: translateY(-1px);
}

/* Pressing gives the chip somewhere to go, so a tap feels answered on a
   touch screen where there is no hover state at all. */
.option:active {
  transform: translateY(0);
}

.option input {
  margin: 0.28em 0 0;
  flex: none;
  width: 20px;
  height: 20px;
  accent-color: var(--brand);
  cursor: pointer;
}

.option__label {
  font-weight: 600;
  color: var(--ink);
  line-height: 1.45;
}

.option:has(input:checked) {
  border-color: var(--brand);
  background: var(--brand-tint);
  box-shadow: inset 0 0 0 1px var(--brand), var(--shadow-sm);
}

/* A checked chip grows a brand colored spine down its leading edge, which
   is a second, non color signal that this is the chosen one. */
.option:has(input:checked)::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block: 12%;
  width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--brand);
}

.option:has(input:focus-visible) {
  box-shadow: var(--ring);
}

/* -- Result panel shared by both tools ---------------------------------- */

.tool__result {
  padding: var(--sp-5);
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface-2);
}

.tool__result[data-state="empty"] {
  color: var(--ink-muted);
}

.tool__result[data-state="match"] {
  background: var(--ok-tint);
  border-color: #bfe3cd;
}

.tool__result[data-state="partial"] {
  background: var(--brand-tint);
  border-color: var(--brand-tint-2);
}

.tool__result[data-state="none"] {
  background: var(--warn-tint);
  border-color: #f0d3b4;
}

/* The panel is rewritten in place each time an answer changes, so it lifts
   in rather than swapping silently. Without it the second answer looks
   identical to the first arriving and is easy to miss. The class is only
   ever added by site.js when the visitor has not asked for reduced motion,
   and the panel is a live region either way, so nothing depends on this. */
.tool__result.is-updated {
  animation: roxy-result-in var(--t-slow) var(--ease);
}

@keyframes roxy-result-in {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.tool__result h4 {
  margin-bottom: var(--sp-3);
}

.tool__result> :last-child {
  margin-bottom: 0;
}

.result-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-4);
}

/* -- City lookup -------------------------------------------------------- */

.lookup {
  display: grid;
  gap: var(--sp-4);
  margin-bottom: var(--sp-5);
}

@media (min-width: 620px) {
  .lookup {
    grid-template-columns: 1fr auto;
    align-items: end;
  }
}

.city-index {
  display: grid;
  gap: var(--sp-6);
}

.city-region h3 {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}

.city-region__phone {
  font-size: var(--step-0);
  font-weight: 700;
}

.city-region__note {
  font-size: var(--step--1);
  color: var(--ink-muted);
  margin-bottom: var(--sp-4);
}

.city-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-2);
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}

.city-list a {
  display: block;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--radius-sm);
  font-size: var(--step--1);
  text-decoration: none;
  color: var(--ink-soft);
  border: 1px solid transparent;
}

.city-list a:hover {
  background: var(--brand-tint);
  color: var(--brand-darker);
  border-color: var(--brand-tint-2);
}

.city-list span {
  display: block;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--step--1);
  color: var(--ink-muted);
}

/* ==========================================================================
   14. Forms
   ========================================================================== */

.form {
  display: grid;
  gap: var(--sp-5);
}

.field {
  display: grid;
  gap: var(--sp-2);
}

.field--row {
  display: grid;
  gap: var(--sp-5);
}

@media (min-width: 620px) {
  .field--row {
    grid-template-columns: 1fr 1fr;
  }
}

.label {
  font-weight: 650;
  color: var(--ink);
  font-size: var(--step--1);
}

.label .req {
  color: var(--danger);
  font-weight: 700;
}

.label .opt {
  color: var(--ink-muted);
  font-weight: 500;
}

.hint {
  font-size: var(--step--1);
  color: var(--ink-muted);
}

.input,
.select,
.textarea {
  width: 100%;
  min-height: 48px;
  padding: var(--sp-3) var(--sp-4);
  font: inherit;
  font-size: var(--step-0);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}

.textarea {
  min-height: 132px;
  resize: vertical;
  line-height: 1.6;
}

.select {
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%),
    linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%);
  background-position: right 1.15rem center, right 0.8rem center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: var(--sp-8);
}

.input:hover,
.select:hover,
.textarea:hover {
  border-color: var(--brand-tint-2);
}

.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(10, 90, 168, 0.16);
}

.input::placeholder,
.textarea::placeholder {
  color: #97a7b6;
}

.input[aria-invalid="true"],
.select[aria-invalid="true"],
.textarea[aria-invalid="true"] {
  border-color: var(--danger);
  background: #fffbfb;
}

.input[aria-invalid="true"]:focus,
.textarea[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px rgba(180, 35, 24, 0.16);
}

.field-error {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--danger);
}

.field-error::before {
  content: "!";
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  margin-top: 0.15em;
  border-radius: 50%;
  background: var(--danger);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 800;
}

.field-error:empty {
  display: none;
}

.form-alert {
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--surface-2);
}

.form-alert h3 {
  font-size: var(--step-1);
  margin-bottom: var(--sp-2);
}

.form-alert> :last-child {
  margin-bottom: 0;
}

.form-alert--error {
  background: var(--danger-tint);
  border-color: #f2c9c5;
}

.form-alert--success {
  background: var(--ok-tint);
  border-color: #bfe3cd;
}

.form-alert--warn {
  background: var(--warn-tint);
  border-color: #f0d3b4;
}

.form-alert ul {
  margin: var(--sp-3) 0 0;
  padding-left: 1.2rem;
}

.form-alert a {
  font-weight: 650;
}

/* Honeypot. Off screen rather than display:none so that a bot filling every
   field it can see still trips it. Hidden from assistive technology too. */
.hp-field {
  position: absolute !important;
  left: -9999px !important;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

.fieldset legend {
  padding: 0;
  font-weight: 650;
  color: var(--ink);
  font-size: var(--step--1);
  margin-bottom: var(--sp-3);
}

.form-footer {
  display: grid;
  gap: var(--sp-4);
  padding-top: var(--sp-2);
}

.form-footer .btn {
  justify-self: start;
}

@media (max-width: 619px) {
  .form-footer .btn {
    justify-self: stretch;
    width: 100%;
  }
}

.btn.is-busy {
  pointer-events: none;
  opacity: 0.7;
}

.btn.is-busy .btn__label::after {
  content: "";
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-left: 0.5em;
  vertical-align: -0.1em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: roxy-spin 0.7s linear infinite;
}

@keyframes roxy-spin {
  to {
    transform: rotate(360deg);
  }
}

/* ==========================================================================
   14b. The estimate request form
   --------------------------------------------------------------------------
   The rules above this point were written against class hooks that the
   estimate form does not emit: .input, .select, .textarea, .label, .hint,
   .field, .field-error and .form-alert. The template emits bare elements
   inside .form-field, .form-hint, .form-error, .form-radio, .form-check and
   .result. Nothing matched, so every control on the only page that takes a
   lead rendered at browser default size, with labels sitting inline beside
   inputs a third of the width of the column.

   This block styles the markup that actually exists. It selects elements
   inside .form rather than requiring a class on each control, so a field
   added later is styled without anybody having to remember a class name,
   which is how the two sets drifted apart in the first place.
   ========================================================================== */

/* -- The panel ----------------------------------------------------------
   The form is the conversion point of the site, so it is given the same
   treatment as the decision tool on the home page: a white card lifted off
   the tinted section with a brand edge along the top. The two most
   important interactive blocks on the site then look like the same family.
   ---------------------------------------------------------------------- */

.form {
  position: relative;
  display: grid;
  gap: var(--sp-5);
  padding: var(--sp-5);
  padding-top: calc(var(--sp-5) + 4px);
  background: var(--surface);
  border: 1px solid var(--brand-tint-2);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
}

@media (min-width: 720px) {
  .form {
    padding: var(--sp-6);
    padding-top: calc(var(--sp-6) + 4px);
  }
}

.form::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 4px;
  background: linear-gradient(90deg, var(--brand) 0%, var(--accent) 100%);
}

/* -- Fields -------------------------------------------------------------- */

.form-field {
  display: grid;
  gap: var(--sp-2);

  /* Rows keep their natural height and stack from the top. Without this a
     field sharing a row with a taller neighbor is stretched to match it,
     and the stretch lands on the input, so the name box grew to twice the
     height of the phone box beside it. */
  align-content: start;
}

/* Two short fields share a row on anything wider than a phone, so the form
   is visibly shorter without dropping a single question. */
.form-row {
  display: grid;
  gap: var(--sp-5);
}

@media (min-width: 620px) {
  .form-row {
    grid-template-columns: 1fr 1fr;
  }
}

.form-field > label,
.form fieldset legend {
  font-weight: 650;
  color: var(--ink);
  font-size: var(--step--1);
  line-height: 1.4;
}

.form fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

.form fieldset legend {
  padding: 0;
  margin-bottom: var(--sp-3);
}

/* "required" is a word, not a symbol, because an asterisk has to be
   explained somewhere and this does not. It is hidden from assistive
   technology in the markup, where the input carries the real required
   attribute instead. */
.form-required {
  display: inline-block;
  margin-inline-start: var(--sp-2);
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--danger-tint);
  color: var(--danger);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  vertical-align: 1px;
}

.form-optional {
  margin-inline-start: var(--sp-2);
  color: var(--ink-muted);
  font-weight: 500;
}

/* -- Controls ------------------------------------------------------------
   48px minimum height because these are tapped with a thumb. The honeypot
   is excluded so the off screen trap is not given a real control's styling.
   ---------------------------------------------------------------------- */

.form input[type="text"],
.form input[type="tel"],
.form input[type="email"],
.form select,
.form textarea {
  width: 100%;
  min-height: 48px;
  padding: var(--sp-3) var(--sp-4);
  font: inherit;
  font-size: var(--step-0);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  transition: border-color var(--t-fast) var(--ease),
    box-shadow var(--t-fast) var(--ease);
}

.form-honeypot input {
  min-height: 0;
}

.form textarea {
  min-height: 140px;
  resize: vertical;
  line-height: 1.6;
}

/* The native arrow is replaced so the control matches the text inputs on
   every browser. Drawn with gradients rather than a background image so
   there is no extra request and nothing to go missing. */
.form select {
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%),
    linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%);
  background-position: right 1.15rem center, right 0.8rem center;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-inline-end: var(--sp-8);
}

.form input[type="text"]:hover,
.form input[type="tel"]:hover,
.form input[type="email"]:hover,
.form select:hover,
.form textarea:hover {
  border-color: var(--brand-tint-2);
}

.form input[type="text"]:focus,
.form input[type="tel"]:focus,
.form input[type="email"]:focus,
.form select:focus,
.form textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-tint);
}

/* -- Choices ------------------------------------------------------------
   Radios and the consent checkbox are given the same chip treatment as the
   glass problem finder, so choosing an answer looks and behaves the same
   way in both places.
   ---------------------------------------------------------------------- */

.form fieldset > .form-radio {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  min-height: 48px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.45;
  transition: border-color var(--t-fast) var(--ease),
    background-color var(--t-fast) var(--ease),
    box-shadow var(--t-fast) var(--ease);
}

.form fieldset > .form-radio + .form-radio {
  margin-top: var(--sp-3);
}

.form-radio:hover {
  border-color: var(--brand-tint-2);
  background: var(--brand-tint);
}

.form-radio:has(input:checked) {
  border-color: var(--brand);
  background: var(--brand-tint);
  box-shadow: inset 0 0 0 1px var(--brand);
}

.form-radio:has(input:focus-visible),
.form-check:has(input:focus-visible) {
  box-shadow: var(--ring);
}

.form-radio input,
.form-check input {
  flex: none;
  margin: 0.15em 0 0;
  width: 20px;
  height: 20px;
  accent-color: var(--brand);
  cursor: pointer;
}

.form-check {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  color: var(--ink);
  line-height: 1.5;
  transition: border-color var(--t-fast) var(--ease),
    background-color var(--t-fast) var(--ease);
}

.form-check:hover {
  border-color: var(--brand-tint-2);
}

.form-check:has(input:checked) {
  border-color: var(--brand);
  background: var(--brand-tint);
}

/* -- Hints and errors ---------------------------------------------------- */

.form-hint {
  margin: 0;
  font-size: var(--step--1);
  color: var(--ink-muted);
  line-height: 1.5;
}

.form-error {
  margin: 0;
  font-size: var(--step--1);
  font-weight: 650;
  color: var(--danger);
  line-height: 1.5;
}

/* The error is announced by color, by weight and by a mark before the
   text, so it does not depend on color alone. */
.form-error::before {
  content: "!";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15em;
  height: 1.15em;
  margin-inline-end: 0.45em;
  border-radius: 50%;
  background: var(--danger);
  color: #fff;
  font-size: 0.8em;
  font-weight: 800;
  vertical-align: -0.15em;
}

.form-field--error input[type="text"],
.form-field--error input[type="tel"],
.form-field--error input[type="email"],
.form-field--error select,
.form-field--error textarea {
  border-color: var(--danger);
  background: var(--danger-tint);
}

.form-field--error > label,
.form-field--error fieldset legend {
  color: var(--danger);
}

/* -- Submit -------------------------------------------------------------- */

.form > .btn {
  justify-self: start;
  width: 100%;
}

@media (min-width: 620px) {
  .form > .btn {
    width: auto;
    min-width: 280px;
  }
}

/* -- Outcome panels -----------------------------------------------------
   Rendered after a submission. The class names here are .result rather than
   .form-alert because that is what the page emits, and site.js looks for
   .result--error to move focus to it.
   ---------------------------------------------------------------------- */

.result {
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-inline-start-width: 4px;
  border-radius: var(--radius);
  background: var(--surface-2);
}

.result > :last-child {
  margin-bottom: 0;
}

.result__title {
  display: block;
  margin-bottom: var(--sp-3);
  font-size: var(--step-1);
  line-height: 1.3;
  color: var(--ink);
}

.result--ok {
  background: var(--ok-tint);
  border-color: #bfe3cd;
  border-inline-start-color: var(--ok);
}

.result--warn {
  background: var(--warn-tint);
  border-color: #f0d3b4;
  border-inline-start-color: #b45309;
}

.result--error {
  background: var(--danger-tint);
  border-color: #f3c9c5;
  border-inline-start-color: var(--danger);
}

.result:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

/* ==========================================================================
   14c. Figures, diagrams and the coverage map
   --------------------------------------------------------------------------
   Same fault as the estimate form: includes/visuals.php emits .figure,
   .figure__svg, .figure__img, .figure__caption and the .map-legend family,
   and the stylesheet defined none of them. An inline SVG with a viewBox and
   no width rule takes its size from the viewBox rather than the column, so
   the Bay Area coverage schematic rendered far wider than its container and
   the region blocks inside it were drawn several times their intended size.
   ========================================================================== */

.figure {
  margin: 0;
  display: grid;
  gap: var(--sp-4);
}

/* The one rule that was actually missing. Without a width the SVG ignores
   the column it sits in. */
.figure__svg {
  display: block;
  width: 100%;
  height: auto;
}

.figure__img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
}

.figure__caption {
  color: var(--ink-soft);
  font-size: var(--step--1);
  line-height: 1.6;
}

.figure__caption > :first-child {
  margin-top: 0;
}

.figure__caption > :last-child {
  margin-bottom: 0;
}

/* The coverage schematic is a panel in its own right, because it answers
   two questions at once: do you come to me, and who answers when I call. */
.figure--map {
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}

@media (min-width: 720px) {
  .figure--map {
    padding: var(--sp-6);
  }
}

/* -- Map legend ----------------------------------------------------------
   The legend is the accessible twin of the diagram. Every region, number
   and city list in the drawing is repeated here as real text, so the answer
   survives with images off, in a screen reader, and in a search result.
   ---------------------------------------------------------------------- */

.map-legend {
  list-style: none;
  margin: var(--sp-5) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-4);
}

@media (min-width: 780px) {
  .map-legend {
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-4) var(--sp-6);
  }
}

.map-legend__item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
}

/* The number in the swatch matches the number drawn on that region, and the
   color matches its fill, so the diagram and the list are readable
   together. The number is what carries the link for anyone who cannot use
   the color. */
.map-legend__swatch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-sm);
  background: var(--ink-soft);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.map-legend__swatch[data-region="sf-marin"]   { background: #0a5aa8; }
.map-legend__swatch[data-region="peninsula"]  { background: #0b6e7f; }
.map-legend__swatch[data-region="south-bay"]  { background: #084b8e; }
.map-legend__swatch[data-region="east-bay"]   { background: #0e7c8a; }
.map-legend__swatch[data-region="tri-valley"] { background: #33475b; }

.map-legend__body {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.map-legend__body strong {
  color: var(--ink);
  font-size: var(--step-0);
  line-height: 1.3;
}

.map-legend__phone {
  font-weight: 700;
  color: var(--brand-dark);
  text-decoration: none;
  justify-self: start;
}

.map-legend__phone:hover {
  text-decoration: underline;
}

.map-legend__cities {
  color: var(--ink-muted);
  font-size: var(--step--1);
  line-height: 1.5;
}

/* ==========================================================================
   14d. Remaining page blocks
   ========================================================================== */

/* A group of regions on the service areas hub. */
.region-block + .region-block {
  margin-top: var(--sp-7);
}

/* The postal address on the Newark location page. */
.location-address {
  font-style: normal;
  line-height: 1.7;
  color: var(--ink-soft);
}

.location-address strong {
  color: var(--ink);
}

/* The license and estimate line printed under a hero's buttons. It is
   supporting detail, so it sits quieter than the paragraph above it while
   staying above the contrast minimum. */
.cta-trust-text {
  color: var(--ink-muted);
  line-height: 1.6;
  max-width: var(--measure);
}

.cta-trust-text a {
  color: var(--brand-dark);
  font-weight: 650;
}

.badge--small {
  padding: 2px 8px;
  font-size: 0.7rem;
}

/* ==========================================================================
   15. Footer
   ========================================================================== */

.site-footer {
  background: linear-gradient(180deg, #0c1b2a 0%, #09131e 100%);
  color: #a9bccd;
  padding-block: var(--sp-8) var(--sp-6);
  font-size: var(--step--1);
}

.site-footer h2,
.site-footer h3 {
  color: #fff;
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: var(--sp-4);
}

.site-footer a {
  color: #c6d7e6;
  text-decoration: none;
}

.site-footer a:hover {
  color: #fff;
  text-decoration: underline;
}

.footer-grid {
  display: grid;
  gap: var(--sp-7);
}

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

@media (min-width: 1024px) {
  .footer-grid {
    grid-template-columns: 1.5fr 1fr 1fr 1.2fr;
  }
}

.footer-logo {
  height: 44px;
  width: auto;
  margin-bottom: var(--sp-4);
}

.footer-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-2);
}

.footer-phones {
  display: grid;
  gap: var(--sp-3);
  margin: 0;
}

.footer-phones div {
  display: grid;
  gap: 1px;
}

.footer-phones dt {
  color: #8ba4ba;
  font-size: 0.78rem;
}

.footer-phones dd {
  margin: 0;
}

.footer-phones dd a {
  color: #fff;
  font-weight: 650;
  font-size: var(--step-0);
}

.footer-bottom {
  margin-top: var(--sp-7);
  padding-top: var(--sp-5);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  display: grid;
  gap: var(--sp-4);
}

@media (min-width: 900px) {
  .footer-bottom {
    grid-template-columns: 1fr auto;
    align-items: center;
  }
}

.footer-legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer-note {
  color: #8ba4ba;
  margin: 0;
  max-width: 70ch;
}

/* -- Mobile sticky action bar -------------------------------------------
   Shown on small screens only, where reaching the header takes a scroll. It
   never covers page content because the body reserves space for it. */

.sticky-actions {
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 90;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4) calc(var(--sp-3) + env(safe-area-inset-bottom, 0px));
  background: rgba(255, 255, 255, 0.97);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--line);
  box-shadow: 0 -4px 16px rgba(12, 27, 42, 0.10);
}

.sticky-actions .btn {
  min-height: 50px;
  padding-inline: var(--sp-3);
}

@media (min-width: 1024px) {
  .sticky-actions {
    display: none;
  }
}

body {
  padding-bottom: 76px;
}

@media (min-width: 1024px) {
  body {
    padding-bottom: 0;
  }
}

/* ==========================================================================
   16. Utilities, states and motion
   ========================================================================== */

.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.center {
  text-align: center;
}

.mt-0 {
  margin-top: 0;
}

.mb-0 {
  margin-bottom: 0;
}

.mb-5 {
  margin-bottom: var(--sp-5);
}

.mt-5 {
  margin-top: var(--sp-5);
}

.mt-6 {
  margin-top: var(--sp-6);
}

.mt-7 {
  margin-top: var(--sp-7);
}

[hidden] {
  display: none !important;
}

/* Reveal on scroll. The content is fully visible by default and is only
   hidden once the script confirms it can reveal it again, so a reader with
   JavaScript disabled never loses content. */
.reveal-ready .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease);
}

.reveal-ready .reveal.is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  .reveal-ready .reveal {
    opacity: 1;
    transform: none;
  }
}

@media print {

  .site-header,
  .sticky-actions,
  .site-footer,
  .nav-toggle,
  .tool {
    display: none !important;
  }

  body {
    padding-bottom: 0;
    color: #000;
  }

  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.85em;
    word-break: break-all;
  }

  .hero,
  .section--ink {
    background: none !important;
    color: #000;
  }

  .hero h1,
  .section--ink h2 {
    color: #000;
  }
}

/* ==========================================================================
   17. Photo Gallery & Visual Showcase
   ========================================================================== */

.photo-gallery {
  margin-top: var(--sp-6);
}

.gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--sp-5);
}

.gallery-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 4px 12px rgba(12, 27, 42, 0.04);
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
  display: flex;
  flex-direction: column;
}

.gallery-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 24px rgba(12, 27, 42, 0.08);
}

/* ==========================================================================
   Work showcase
   --------------------------------------------------------------------------
   Three photographs of the work a page is about, placed in the middle of the
   page rather than in a gallery at the bottom.

   The frame declares a 3 by 2 ratio so the space is reserved before the file
   arrives and the row cannot jump as the page loads. The photograph fills the
   frame with object-fit: cover, so a file of any ratio is cropped to fit
   rather than stretched out of shape.
   ========================================================================== */

.showcase {
  display: grid;
  gap: var(--sp-5);
  grid-template-columns: repeat(auto-fit, minmax(288px, 1fr));
}

.showcase__item {
  display: flex;
  min-width: 0;
}

.showcase__figure {
  display: flex;
  flex-direction: column;
  margin: 0;
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: border-color var(--t-base) var(--ease),
    box-shadow var(--t-base) var(--ease),
    transform var(--t-base) var(--ease);
}

.showcase__figure:hover {
  border-color: var(--brand-tint-2);
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}

.showcase__frame {
  display: block;
  aspect-ratio: 3 / 2;
  background: var(--surface-3);
  overflow: hidden;
}

.showcase__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-slow) var(--ease);
}

.showcase__figure:hover .showcase__frame img {
  transform: scale(1.04);
}

.showcase__body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-5);
}

.showcase__tag {
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--brand-tint);
  color: var(--brand-darker);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.showcase__title {
  font-size: var(--step-1);
  line-height: 1.3;
  color: var(--ink);
}

.showcase__caption {
  color: var(--ink-soft);
  font-size: var(--step--1);
  line-height: 1.6;
}

.gallery-card__img-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--surface-2);
}

.gallery-card__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-normal) var(--ease);
}

.gallery-card:hover .gallery-card__img {
  transform: scale(1.04);
}

.gallery-card__body {
  padding: var(--sp-4) var(--sp-5);
  flex: 1;
  display: flex;
  flex-direction: column;
}

.gallery-card__title {
  font-size: var(--step-0);
  font-weight: 700;
  color: var(--ink);
  margin-bottom: var(--sp-2);
}

.gallery-card__caption {
  font-size: var(--step--1);
  color: var(--ink-soft);
  margin: 0;
  line-height: 1.5;
}

.gallery-slider {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background: var(--surface);
}

.gallery-slider__track:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--radius);
}

.gallery-slider__track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.gallery-slider__track::-webkit-scrollbar {
  display: none;
}

.gallery-slider__slide {
  flex: 0 0 100%;
  min-width: 100%;
  scroll-snap-align: start;
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
}

@media (min-width: 768px) {
  .gallery-slider__slide {
    grid-template-columns: 1.2fr 1fr;
    align-items: center;
  }
}

.gallery-slider__img-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 280px;
  background: var(--surface-2);
}

.gallery-slider__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.gallery-slider__content {
  padding: var(--sp-6);
}

.gallery-slider__tag {
  display: inline-block;
  font-size: var(--step--1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--brand);
  margin-bottom: var(--sp-2);
}
/* ==========================================================================
   Honeypot field

   A field that a person never sees and never fills in. It is moved off screen
   rather than hidden with display:none, because some automated form fillers
   skip fields that are set to display:none and would then walk past the trap.
   It is also removed from the accessibility tree with aria-hidden in the
   markup and from the tab order with tabindex="-1", so a keyboard or screen
   reader user never reaches it either.

   This lives here rather than in a style attribute so that the site can send
   a Content Security Policy that refuses inline styles outright.
   ========================================================================== */

.form-honeypot {
  position: absolute;
  left: -9999px;
  top: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ==========================================================================
   19. Regional phone bar
   --------------------------------------------------------------------------
   Roxy Glass answers five regional lines and has no single company number,
   so all five are printed in the header on every page, in one row between
   the logo and the navigation. Nothing is hidden behind a control: a caller
   sees the number for their market without opening anything.

   Wide screens show the row with a label and hairline dividers. Narrower
   screens wrap it: three across on a tablet, two across on a phone. Nothing
   scrolls out of sight, because a number a caller cannot see is a number
   they will not dial.
   ========================================================================== */

.phone-bar {
  background: var(--brand-tint);
  border-top: 1px solid var(--brand-tint-2);
  border-bottom: 1px solid var(--brand-tint-2);
}

.phone-bar__inner {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: 56px;
  padding-block: var(--sp-2);
}

.phone-bar__label {
  display: none;
  align-items: center;
  gap: var(--sp-2);
  margin: 0;
  color: var(--brand-darker);
  font-size: var(--step--1);
  font-weight: 700;
  white-space: nowrap;
}

.phone-bar__label svg {
  flex: none;
  width: 18px;
  height: 18px;
}

/* The label earns its space only where the five numbers still fit beside it. */
@media (min-width: 1120px) {
  .phone-bar__label {
    display: inline-flex;
  }
}

.phone-bar__list {
  display: grid;
  flex: 1 1 auto;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-1) var(--sp-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 640px) {
  .phone-bar__list {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (min-width: 1024px) {
  .phone-bar__list {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
}

.phone-bar__item {
  min-width: 0;
}

/* Five items in a two or three column grid leave one on its own row. It
   stretches across the remaining columns rather than sitting in a gap. */
.phone-bar__item:last-child:nth-child(odd) {
  grid-column: 1 / -1;
}

/* Three across and five across both place the odd item themselves, so the
   full width rule above applies only to the two column phone layout. */
@media (min-width: 640px) {
  .phone-bar__item:last-child:nth-child(odd) {
    grid-column: auto;
  }
}

.phone-bar__link {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px var(--sp-3);
  border-radius: var(--radius);
  color: var(--ink);
  text-decoration: none;
  transition: background-color var(--t-fast) var(--ease);
}

.phone-bar__link:hover {
  background: #fff;
  color: var(--brand-darker);
}

/* The longest region name, Tri Valley and Diablo Valley, does not fit one
   column at 1180px, so the label wraps and the number never does. */
.phone-bar__region {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px var(--sp-2);
  color: var(--ink-soft);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  line-height: 1.25;
  text-transform: uppercase;
}

.phone-bar__number {
  color: var(--brand-dark);
  font-size: 14px;
  font-weight:600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.phone-bar__badge {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--ok-tint);
  color: var(--ok);
  font-size: 0.62rem;
  letter-spacing: 0.03em;
}

.phone-bar__item.is-current .phone-bar__link {
  background: #fff;
  box-shadow: inset 0 0 0 1px var(--ok);
}

/* Wide screens: one clean row, separated by hairlines rather than gaps. */
@media (min-width: 1024px) {
  .phone-bar__list {
    gap: 0;
  }

  .phone-bar__item + .phone-bar__item {
    border-inline-start: 1px solid var(--brand-tint-2);
  }

  .phone-bar__link {
    padding-inline: var(--sp-4);
  }
}

/* Narrow screens: the row is a wrapped grid, so it needs room to breathe. */
@media (max-width: 1023px) {
  .phone-bar__inner {
    align-items: stretch;
    padding-block: var(--sp-3);
  }

  .phone-bar__link {
    padding: 3px var(--sp-2);
  }

  .phone-bar__number {
    font-size: var(--step--1);
  }

  .phone-bar__region {
    font-size: 0.66rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .phone-bar__link {
    transition: none;
  }
}
/* ==========================================================================
   18. Project photographs
   --------------------------------------------------------------------------
   The project gallery at /projects/, the photograph viewer, and the places
   real project photographs replaced the AI generated pictures: service
   cards, the plain page heroes, showcases and the about page.

   Every photograph frame declares its ratio before the file arrives, so no
   row moves as images load. Every hover effect is a transform or a color,
   never a size change, and the reduced motion block removes all of them.
   ========================================================================== */

/* The drawn service illustrations sat inside 25px of padding. A photograph
   fills the frame edge to edge instead. */
.card__media--photo {
  padding: 0;
  overflow: hidden;
  background: var(--surface-3);
}

.card__media--photo img {
  padding: 0;
}

/* The plain page hero photograph, with its title linking to the gallery. */
.hero-image-wrapper--photo {
  position: relative;
  margin: 0;
  background: var(--surface-3);
}

.hero-image-wrapper__caption {
  position: absolute;
  left: var(--sp-3);
  right: var(--sp-3);
  bottom: var(--sp-3);
  display: flex;
}

.hero-image-wrapper__caption a {
  display: inline-block;
  max-width: 100%;
  padding: 6px 14px;
  border-radius: 999px;
  background: rgba(12, 27, 42, 0.84);
  color: #fff;
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.35;
  text-decoration: none;
}

.hero-image-wrapper__caption a:hover {
  background: var(--ink);
  text-decoration: underline;
}

/* About page photograph. */
.figure--photo {
  margin: var(--sp-6) 0 0;
}

.figure--photo .figure__img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
}

.figure--photo .figure__caption {
  margin-top: var(--sp-3);
  color: var(--ink-soft);
  font-size: var(--step--1);
  line-height: 1.6;
}

/* A showcase can now hold one or two photographs. A single photograph is not
   stretched across the full container. */
@media (min-width: 700px) {
  .showcase--count-1 {
    grid-template-columns: minmax(0, 36rem);
  }

  .showcase--count-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Style examples are never tagged in the brand blue that marks a real job. */
.showcase__tag--style,
.project-card__tag--style {
  background: var(--surface-3);
  color: var(--ink-muted);
}

.gallery-slider__slide {
  margin: 0;
}

/* -- Gallery hero ------------------------------------------------------ */

.projects-mosaic {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--sp-3);
  align-content: start;
}

.projects-mosaic__item {
  grid-column: span 2;
  margin: 0;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--surface-3);
  box-shadow: var(--shadow-sm);
}

.projects-mosaic__item:first-child {
  grid-column: 1 / -1;
  aspect-ratio: 16 / 10;
}

.projects-mosaic__item img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* -- At a glance ------------------------------------------------------- */

.project-stats {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
}

.project-stats__item {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}

.project-stats__value {
  color: var(--brand-dark);
  font-size: var(--step-4);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
}

.project-stats__label {
  color: var(--ink-soft);
  font-size: var(--step--1);
  line-height: 1.5;
}

/* -- Filters ----------------------------------------------------------- */

.gallery-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}

.gallery-toolbar__label {
  margin: 0;
  color: var(--ink);
  font-weight: 700;
}

.filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 8px 10px 8px 16px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease),
    color var(--t-fast) var(--ease);
}

.filter-chip:hover {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand-darker);
}

.filter-chip[aria-pressed="true"] {
  border-color: var(--brand-dark);
  background: var(--brand-dark);
  color: #fff;
}

.filter-chip__count {
  min-width: 1.75rem;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--surface-3);
  color: var(--ink-muted);
  font-size: 0.78rem;
  text-align: center;
}

.filter-chip[aria-pressed="true"] .filter-chip__count {
  background: rgba(255, 255, 255, 0.2);
  color: #fff;
}

.gallery-status {
  min-height: 1.5em;
  margin: 0 0 var(--sp-5);
  color: var(--ink-soft);
  font-size: var(--step--1);
}

/* -- Grid and cards ---------------------------------------------------- */

.project-grid {
  display: grid;
  gap: var(--sp-5);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16.5rem), 1fr));
}

.project-card {
  display: flex;
  min-width: 0;
}

.project-card__figure {
  display: flex;
  flex-direction: column;
  width: 100%;
  margin: 0;
  overflow: hidden;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  transition: border-color var(--t-base) var(--ease),
    box-shadow var(--t-base) var(--ease),
    transform var(--t-base) var(--ease);
}

.project-card__figure:hover {
  border-color: var(--brand-tint-2);
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}

.project-card__link {
  display: block;
  position: relative;
  color: inherit;
  text-decoration: none;
}

.project-card__frame {
  display: block;
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--surface-3);
}

.project-card__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-slow) var(--ease);
}

.project-card__link:hover .project-card__frame img {
  transform: scale(1.04);
}

.project-card__zoom {
  position: absolute;
  right: var(--sp-3);
  bottom: var(--sp-3);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  background: rgba(12, 27, 42, 0.8);
  color: #fff;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}

.project-card__zoom svg {
  width: 20px;
  height: 20px;
}

.project-card__link:hover .project-card__zoom,
.project-card__link:focus-visible .project-card__zoom {
  opacity: 1;
  transform: none;
}

/* On a touch screen there is no hover to reveal it, so it is always shown. */
@media (hover: none) {
  .project-card__zoom {
    opacity: 1;
    transform: none;
  }
}

.project-card__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-5) var(--sp-5);
}

.project-card__tag {
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--brand-tint);
  color: var(--brand-darker);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.project-card__title {
  color: var(--ink);
  font-size: var(--step-0);
  line-height: 1.35;
}

.project-card__caption {
  color: var(--ink-soft);
  font-size: var(--step--1);
  line-height: 1.6;
}

.project-card__next {
  margin-top: auto;
  padding-top: var(--sp-2);
  font-size: var(--step--1);
}

.gallery-more {
  display: flex;
  justify-content: center;
  margin-top: var(--sp-6);
}

.project-note {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  margin: 0 0 var(--sp-6);
  padding: var(--sp-4) var(--sp-5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  color: var(--ink-soft);
}

.project-note svg {
  flex: none;
  width: 22px;
  height: 22px;
  margin-top: 2px;
  color: var(--accent);
}

.project-service__count {
  margin-bottom: var(--sp-2);
  color: var(--accent);
  font-size: var(--step--1);
  font-weight: 700;
}

/* -- Viewer ------------------------------------------------------------ */

.lightbox {
  width: min(calc(100vw - 2rem), 76rem);
  max-width: none;
  max-height: calc(100vh - 2rem);
  max-height: calc(100dvh - 2rem);
  padding: 0;
  overflow: hidden;
  border: 0;
  border-radius: var(--radius-xl);
  background: var(--ink);
  color: var(--on-dark);
  box-shadow: var(--shadow-lg);
}

.lightbox::backdrop {
  background: rgba(6, 14, 22, 0.88);
}

/* The page behind an open viewer does not scroll. */
html:has(.lightbox[open]) {
  overflow: hidden;
}

.lightbox__panel {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  max-height: inherit;
}

.lightbox__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-5);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.lightbox__count {
  margin: 0;
  color: var(--on-dark-soft);
  font-size: var(--step--1);
  font-weight: 600;
}

.lightbox__close,
.lightbox__nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 999px;
  background: rgba(12, 27, 42, 0.74);
  color: #fff;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease);
}

.lightbox__close:hover,
.lightbox__nav:hover {
  background: rgba(255, 255, 255, 0.18);
}

.lightbox__close svg,
.lightbox__nav svg {
  width: 22px;
  height: 22px;
}

.lightbox__stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 14rem;
  padding: var(--sp-4) calc(48px + var(--sp-5));
  background: #060e16;
  touch-action: pan-y;
}

.lightbox__img {
  display: block;
  max-width: 100%;
  max-height: 62vh;
  max-height: 62dvh;
  width: auto;
  height: auto;
  border-radius: var(--radius);
  transition: opacity var(--t-base) var(--ease);
}

.lightbox__stage.is-loading .lightbox__img {
  opacity: 0.35;
}

.lightbox__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

.lightbox__nav--prev {
  left: var(--sp-3);
}

.lightbox__nav--next {
  right: var(--sp-3);
}

.lightbox__error {
  position: absolute;
  left: var(--sp-5);
  right: var(--sp-5);
  bottom: var(--sp-5);
  margin: 0;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius);
  background: var(--danger-tint);
  color: var(--danger);
  font-weight: 600;
}

.lightbox__info {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  min-width: 0;
  padding: var(--sp-5);
  overflow: auto;
}

.lightbox__label {
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: 999px;
  background: rgba(124, 192, 247, 0.16);
  color: var(--brand-light);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.lightbox__label--style {
  background: rgba(255, 255, 255, 0.1);
  color: var(--on-dark-soft);
}

.lightbox__title {
  margin: 0;
  color: #fff;
  font-size: var(--step-2);
  line-height: 1.25;
}

.lightbox__caption {
  margin: 0;
  color: var(--on-dark-soft);
  line-height: 1.6;
}

.lightbox__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3) var(--sp-5);
  margin-top: var(--sp-2);
}

.lightbox__next-page {
  color: var(--brand-light);
  font-weight: 600;
}

.lightbox__next-page:hover {
  color: #fff;
}

@media (min-width: 960px) {
  .lightbox__panel {
    grid-template-columns: minmax(0, 1fr) 22rem;
    grid-template-rows: auto minmax(0, 1fr);
  }

  .lightbox__bar {
    grid-column: 1 / -1;
  }

  .lightbox__img {
    max-height: calc(100vh - 9rem);
    max-height: calc(100dvh - 9rem);
  }

  .lightbox__info {
    justify-content: center;
    border-left: 1px solid rgba(255, 255, 255, 0.08);
  }
}

@media (max-width: 599px) {
  .lightbox {
    width: 100vw;
    max-height: 100vh;
    max-height: 100dvh;
    margin: 0;
    border-radius: 0;
  }

  .lightbox__stage {
    padding-inline: var(--sp-3);
  }

  .lightbox__nav {
    top: auto;
    bottom: var(--sp-3);
    transform: none;
  }
}

/* The global [hidden] rule appears earlier in this file than these
   components, so each component that sets its own display repeats it. */
.project-card[hidden],
.gallery-toolbar[hidden],
.gallery-more[hidden],
.lightbox__nav[hidden],
.lightbox__error[hidden] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .project-card__figure,
  .project-card__frame img,
  .project-card__zoom,
  .filter-chip,
  .lightbox__img,
  .lightbox__close,
  .lightbox__nav {
    transition: none;
  }

  .project-card__figure:hover,
  .project-card__link:hover .project-card__frame img {
    transform: none;
  }
}

/* ==========================================================================
   17. Focus, the last word
   --------------------------------------------------------------------------
   The focus treatment is declared once near the top of this file, as part of
   the reset. It is repeated here, at the end, because the ring is drawn with
   box-shadow and any later rule that also sets box-shadow silently replaces
   it. That is exactly what happened when the hero gave its buttons a drop
   shadow: .hero .btn beats a bare :focus-visible on specificity, and the
   keyboard ring on the main call button disappeared without any checker
   noticing, because a missing focus ring is invisible to automated testing.

   Everything here sets box-shadow and nothing else, so no element's radius,
   color or spacing is affected. Add a selector here whenever a component
   gains its own box-shadow.
   ========================================================================== */

:focus-visible,
.btn:focus-visible,
.btn:hover:focus-visible,
.hero .btn:focus-visible,
.hero .btn:hover:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}
