/* Belt-and-braces: the badge markup is stripped from index.html and its render
   call is disabled in script_main, so this only matters if the site is
   re-captured and one of those steps is missed. */
#__framer-badge-container,
.__framer-badge { display: none !important; }

/* ── One type scale at every width ───────────────────────────────────
 * Framer ships three type scales, one per breakpoint, and steps every size
 * down twice on the way to a phone: body copy 18 → 16 → 17, card titles
 * 26 → 20 → 17, pull quotes 22 → 18 → 17, dates 16 → 15 → 15. The effect is
 * that the same sentence reads noticeably smaller on a tablet than on a
 * laptop, which is the opposite of what a phone needs.
 *
 * Every text style below is pinned to the largest value it takes on any
 * breakpoint, so the scale is identical everywhere. Nothing is made smaller:
 * where a size was already bumped past the desktop value (the author byline,
 * 14 on desktop but 16 on a phone) the larger number is the one that is kept.
 *
 * Framer sets sizes through a --framer-font-size custom property on rules
 * like `.framer-PQwJi .framer-styles-preset-X:not(.rich-text-wrapper)`, which
 * is three classes to this file's one. !important settles it, and also beats
 * the inline copies Framer writes on some nodes.
 *
 * Display headings are a separate case and are handled further down.
 */
@media (max-width: 1199.98px) {
  /* 18px — body copy, labels, eyebrows, footer headings, form captions */
  .framer-styles-preset-1ijbt30,
  .framer-styles-preset-1y38nyc,
  .framer-styles-preset-m06wgy { --framer-font-size: 18px !important; }

  /* 26px — card and team-member titles */
  .framer-styles-preset-a7el6s { --framer-font-size: 26px !important; }

  /* 22px — article section headings and pull quotes */
  .framer-styles-preset-iv4naq { --framer-font-size: 22px !important; }

  /* 20px — testimonial quotes */
  .framer-styles-preset-1pak7fr { --framer-font-size: 20px !important; }

  /* 16px — project dates and locations, copyright, author role */
  .framer-styles-preset-1mly8nt,
  .framer-styles-preset-sujv38,
  .framer-styles-preset-t6cjmb { --framer-font-size: 16px !important; }
}

/* The byline/role preset is 14px on desktop but 16px on a phone. 16 is the
   one worth keeping, so desktop comes up to meet it rather than the other way
   round — this is the only place the largest layout changes. */
.framer-styles-preset-t6cjmb { --framer-font-size: 16px !important; }

/* Section eyebrows, nav and footer links, and the stat captions take their
 * size from a numeric variable written inline on the element, not from a
 * preset — so they have to be matched on the value as well as the name. Each
 * rule maps one breakpoint's shrunken value back to the desktop one.
 *
 * Framer serialises these with a space after the colon, but a value that is
 * still exactly as captured in the HTML has none, so both forms are matched.
 *
 * --variable-reference-K7DPUMFWg-lU5napzXS is deliberately absent: 14 is one
 * of its real desktop values, so matching 14 would enlarge text that was
 * never shrunk.
 */
@media (min-width: 810px) and (max-width: 1199.98px) {
  [style*="--variable-reference-DsCMXzEUm-UFkM58S0H: 16"],
  [style*="--variable-reference-DsCMXzEUm-UFkM58S0H:16"] { --variable-reference-DsCMXzEUm-UFkM58S0H: 18 !important; }
  [style*="--variable-reference-ncyimL48g-Xo14yi3Wj: 16"],
  [style*="--variable-reference-ncyimL48g-Xo14yi3Wj:16"] { --variable-reference-ncyimL48g-Xo14yi3Wj: 18 !important; }
  [style*="--variable-reference-xvXM8kHcE-jvANqg25u: 16"],
  [style*="--variable-reference-xvXM8kHcE-jvANqg25u:16"] { --variable-reference-xvXM8kHcE-jvANqg25u: 18 !important; }
  [style*="--variable-reference-eBHMLcBwW-A4YlFlJ8h: 16"],
  [style*="--variable-reference-eBHMLcBwW-A4YlFlJ8h:16"] { --variable-reference-eBHMLcBwW-A4YlFlJ8h: 20 !important; }
}

@media (max-width: 809.98px) {
  [style*="--variable-reference-DsCMXzEUm-UFkM58S0H: 14"],
  [style*="--variable-reference-DsCMXzEUm-UFkM58S0H:14"] { --variable-reference-DsCMXzEUm-UFkM58S0H: 18 !important; }
  [style*="--variable-reference-ncyimL48g-Xo14yi3Wj: 14"],
  [style*="--variable-reference-ncyimL48g-Xo14yi3Wj:14"] { --variable-reference-ncyimL48g-Xo14yi3Wj: 18 !important; }
  [style*="--variable-reference-xvXM8kHcE-jvANqg25u: 15"],
  [style*="--variable-reference-xvXM8kHcE-jvANqg25u:15"] { --variable-reference-xvXM8kHcE-jvANqg25u: 18 !important; }
  [style*="--variable-reference-eBHMLcBwW-A4YlFlJ8h: 14"],
  [style*="--variable-reference-eBHMLcBwW-A4YlFlJ8h:14"] { --variable-reference-eBHMLcBwW-A4YlFlJ8h: 20 !important; }
  [style*="--variable-reference-OrAWu8jrO-DISjflWx9: 18"],
  [style*="--variable-reference-OrAWu8jrO-DISjflWx9:18"] { --variable-reference-OrAWu8jrO-DISjflWx9: 20 !important; }

  /* A handful of nodes carry --framer-font-size inline rather than through a
     preset. Only two sizes actually shrink on a phone.

     Testimonial quotes: 22px everywhere else, 18px here. */
  [style*="--framer-font-size: 18px"],
  [style*="--framer-font-size:18px"] { --framer-font-size: 22px !important; }

  /* The About hero eyebrow: 20px everywhere else, 14px here. The home page's
     "Trusted by 750+ Happy Customers." is also 14px inline but is 14px on
     every breakpoint, so this is scoped to the About hero rather than
     matching 14px site-wide. That hero is the only one holding a wordmark
     image, which gives a hook that survives a client-side route change. */
  header:has([data-framer-name="Logo"] img) [style*="--framer-font-size: 14px"],
  header:has([data-framer-name="Logo"] img) [style*="--framer-font-size:14px"] { --framer-font-size: 20px !important; }
}

/* ── Display headings ─────────────────────────────────────────────────
 * The three display sizes run 64/60/40 on desktop, 48/44/32 on a tablet and
 * 36/28/26 on a phone. A tablet has the width to carry the desktop sizes, so
 * it gets them.
 *
 * A phone does not: "Experienced" alone is 11 characters, and at 64px it is
 * wider than a 390px screen, so the word would hang off the side rather than
 * wrap. The phone column keeps its own scale — the one place where the sizes
 * still differ, and for the reason the breakpoint exists.
 */
@media (min-width: 810px) and (max-width: 1199.98px) {
  .framer-styles-preset-15yigb9 { --framer-font-size: 64px !important; }
  .framer-styles-preset-n4tnyp  { --framer-font-size: 60px !important; }
  .framer-styles-preset-15xje0k { --framer-font-size: 40px !important; }
}

/* ── Blog index ───────────────────────────────────────────────────────
 * Scoped by PAGE CONTENT, not by a class on <html>. Framer's router navigates
 * client-side: it swaps the body and keeps the original document's <html>, so
 * anything scoped there silently stops applying when you arrive by clicking a
 * link rather than loading the URL. The blog index is the only page carrying a
 * "Tabs+Border" section, so :has() gives a hook that travels with the content
 * and needs no JavaScript.
 */

/* the divider under the filter chips (template design; delete to restore it) */
[data-framer-name="Tabs+Border"] [data-framer-name="line"] { display: none !important; }

/* "Insights" — the template runs it large and wide, tight under the nav.
   Framer caps the title row at 1040px and its line box fits two lines for a
   single word, which is where the empty space came from. */
body:has([data-framer-name="Tabs+Border"]) h1.framer-text {
  --framer-font-size: 224px !important;
  line-height: 0.92 !important;
  white-space: nowrap;
}

@media (min-width: 1200px) {
  body:has([data-framer-name="Tabs+Border"]) [data-framer-name="Title"] {
    max-width: none !important;
    width: 100% !important;
  }
  /* glyphs run ~3.27x the font size in a column ~64% of the viewport */
  body:has([data-framer-name="Tabs+Border"]) h1.framer-text { --framer-font-size: min(300px, 17.5vw) !important; }
  body:has([data-framer-name="Tabs+Border"]) header[class*="framer-"] { padding-top: 40px !important; padding-bottom: 8px !important; }
}

@media (min-width: 810px) and (max-width: 1199.98px) {
  body:has([data-framer-name="Tabs+Border"]) h1.framer-text { --framer-font-size: min(170px, 15vw) !important; }
  body:has([data-framer-name="Tabs+Border"]) header[class*="framer-"] { padding-top: 40px !important; padding-bottom: 8px !important; }
}

@media (max-width: 809.98px) {
  body:has([data-framer-name="Tabs+Border"]) h1.framer-text { --framer-font-size: 56px !important; }
}

/* ── Footer navigation ────────────────────────────────────────────────
 * The Navigation list is flex-wrap with a 36px gap. When the column is wide
 * enough for four items, "Blog" lands at the end of the first row hard against
 * the Socials column, which reads as uneven even though every gap is identical.
 * Capping the list keeps it at three per row (About / Works / Services, then
 * Blog / Contact), so the rhythm matches and nothing crowds Socials.
 */
@media (min-width: 810px) {
  footer ul:has([data-framer-name="footer_link"]) { max-width: 300px; }
}

/* ── The leftover template teal ───────────────────────────────────────
 * Framer's hero puts a photo layer inside a clipped box and parallaxes it.
 * Behind that layer the box is painted `--token-c7703b05…: #047e9f`, a colour
 * from the Resido template that this site never uses anywhere else — the one
 * and only rule that reads the token is the hero container's background.
 *
 * Any frame where the layer does not quite cover its box — a subpixel seam at
 * a rounded corner, a resize before the parallax re-clamps, a breakpoint whose
 * layer is exactly as tall as the box — paints that teal as a bright blue line
 * against the photo. Chasing every such frame is a losing race against a
 * compositor that repaints on its own schedule.
 *
 * So the colour itself is retired. #2e2921 is sampled from the hero photo
 * (its average is #554c3f, the lower third nearly black), so a seam that does
 * slip through now reads as shadow in the image rather than a blue bar.
 * tune.js additionally paints the photo itself onto the box; this is the
 * fallback for the frame before that lands, and for any layout it misses.
 */
body { --token-c7703b05-bdf7-4806-9e9d-7eef7826d8f6: #2e2921; }

/* ── Contact details: phone, email, WhatsApp ──────────────────────────
 * The footer column is a clone of the Socials column, so it already carries
 * Framer's typography and hover states and needs no rules here. These cover
 * only the pieces built from scratch: the header phone/WhatsApp pair, the
 * contact-page lines, and the form's status message.
 *
 * Colours come from Framer's own tokens rather than literals, so the site can
 * be re-themed without these drifting out of step. WhatsApp green is used only
 * for the glyph, never for text, so contrast stays with the page's own palette.
 */

.afs-wa { display: inline-flex; align-items: center; gap: 0.45em; }
.afs-wa svg { color: #25D366; }

/* Header ----------------------------------------------------------------- */
.afs-header-contact {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-left: auto;
  padding-right: 14px;
}
.afs-header-phone,
.afs-header-wa {
  font-size: 15px;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  color: var(--token-36310b23-4971-4589-8264-ec5b880126b6, #000);
  opacity: 0.85;
  transition: opacity 0.18s ease;
}
.afs-header-phone:hover,
.afs-header-wa:hover { opacity: 1; }

/* The header is tight on phones and the hamburger already sits at the right.
   Keep the number — it is the point — and drop the WhatsApp label, which the
   footer and contact page both still carry. */
@media (max-width: 809.98px) {
  .afs-header-contact { gap: 12px; padding-right: 10px; }
  .afs-header-phone { font-size: 14px; }
  .afs-header-wa span { display: none; }
  .afs-header-wa svg { width: 1.35em; height: 1.35em; }
}

/* Contact page ------------------------------------------------------------ */
.afs-contact-extra {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  margin-top: 22px;
}
/* The email above these sits in a very large display heading. At 17px these
   read as a footnote to it rather than as contact details in their own right,
   so they take the body size and the page's full-strength text colour. */
.afs-contact-line {
  font-size: 22px;
  line-height: 1.25;
  text-decoration: none;
  color: var(--token-36310b23-4971-4589-8264-ec5b880126b6, #000);
  opacity: 0.78;
  transition: opacity 0.18s ease;
}
.afs-contact-line:hover { opacity: 1; }

@media (max-width: 809.98px) {
  .afs-contact-line { font-size: 18px; }
  .afs-contact-extra { gap: 10px; margin-top: 18px; }
}

/* Form status ------------------------------------------------------------- */
.afs-form-status {
  margin: 14px 0 0;
  font-size: 15px;
  line-height: 1.45;
  color: var(--token-065a1007-efe3-447e-8f7e-bab1f7402153, #707070);
}
.afs-form-status:empty { display: none; }
.afs-form-status.is-ok    { color: #1c7c3f; }
.afs-form-status.is-error { color: #b3261e; }
.afs-form-status a { color: inherit; text-decoration: underline; }

/* The footer columns are flex siblings at `flex: 1 0 0`, i.e. equal thirds of
   the leftover space (~191px at 1280). An email address needs about 220px, so
   this column is allowed to take the width it needs while the others keep
   theirs, and its links wrap rather than clip. */
.afs-contact-col { flex: 1 1 auto !important; min-width: 232px; }
.afs-contact-col li p.afs-nowrap { white-space: nowrap !important; }
/* Below the desktop breakpoint the three columns no longer fit side by side;
   forcing 232px there squeezes Navigation and Socials instead. Let it take a
   row of its own. */
@media (max-width: 1199.98px) {
  .afs-contact-col { flex-basis: 100% !important; min-width: 0; margin-top: 4px; }
}
.afs-contact-col ul { white-space: normal; }
.afs-contact-col li a { overflow: visible !important; }
.afs-contact-col li p { white-space: normal !important; }

/* ── Hero buttons on mobile ───────────────────────────────────────────
 * Framer stacks the hero's two calls to action in a flex *column* below
 * 810px and lets each fill the container, so both run the full width of the
 * screen. Side by side at their natural width reads as a pair of buttons
 * rather than two banners.
 *
 * `flex-wrap: wrap` is the safety net: on a very narrow screen — or if the
 * labels are ever made longer — they drop back to stacking instead of
 * overflowing the viewport. Scoped to the hero's own btn-group so the
 * identically-named groups elsewhere on the site are untouched.
 */
@media (max-width: 809.98px) {
  header [data-framer-name="btn-group"] {
    flex-direction: row !important;
    flex-wrap: wrap;
    justify-content: flex-start;
    align-items: center;
    gap: 10px !important;
    /* Without this the group keeps its own intrinsic width, the buttons never
       exceed it, wrap never triggers, and the second button runs off the side
       of a narrow phone — the hero clips it, so it just disappears rather than
       scrolling. Measured at 320px: right edge 376px on a 320px screen. */
    max-width: 100%;
  }

  /* the buttons sit in per-button container divs that are themselves 100% */
  header [data-framer-name="btn-group"] > * {
    width: auto !important;
    max-width: 100%;
    flex: 0 1 auto !important;
    min-width: 0;
  }

  header [data-framer-name="btn-group"] a {
    width: auto !important;
    white-space: nowrap;
    /* trimmed from 20px so both fit on a 360px screen; below that they wrap */
    padding-left: 14px !important;
    padding-right: 14px !important;
  }
}

/* ── Footer layout on narrow and mid-width screens ────────────────────
 * On a phone the footer had two collisions: the wordmark painted across the
 * Navigation list, and the email address ran through the Socials column and
 * printed over "Dribbble".
 *
 * Root cause of both is the same — the footer keeps a two-column grid down to
 * the smallest screens, and its contents have fixed widths that the columns
 * cannot honour. A 368px wordmark and a 218px email simply do not fit in a
 * ~190px track, and neither one clips or wraps, so they overlap a neighbour.
 *
 * Fixing the symptoms alone made it worse: constraining the wordmark cropped
 * "Sanding" off (the image is object-fit: cover, so squeezing the box crops
 * rather than scales), and letting the email wrap broke it one character per
 * line in the narrow track. So the layout is fixed instead — one column per
 * row below 700px, where there is room for both at their natural size.
 */

/* The wordmark is a cover-fitted background image: narrowing its box crops
   the right-hand side instead of scaling it down. Contain scales it. */
footer a[href="./"] img {
  object-fit: contain !important;
  object-position: left center;
}
footer a[href="./"] {
  max-width: 100%;
}

@media (max-width: 699.98px) {
  /* One column per row, so nothing has to overlap or hyphenate to fit.
   *
   * `grid-template-columns: 1fr` alone is not enough, and it took a while to
   * see why: one of the footer's children carries `grid-column: span 2`, which
   * creates an implicit second column however many explicit tracks there are.
   * Every child is therefore forced onto the single track explicitly.
   *
   * `min-width: 0` matters too — a grid item's default `min-width: auto`
   * refuses to shrink below its content, and the 368px wordmark was setting
   * the track's floor, pushing the whole grid wider than the screen. */
  footer [data-framer-name="grid"] {
    grid-template-columns: 1fr !important;
  }
  footer [data-framer-name="grid"] > * {
    grid-column: 1 / -1 !important;
    min-width: 0 !important;
  }
  /* let the wordmark scale with the column instead of fixing its width */
  footer a[href="./"] {
    width: 100% !important;
    max-width: 320px;
    height: auto !important;
    aspect-ratio: 368 / 241;
  }
  .afs-contact-col {
    min-width: 0 !important;
    max-width: 100%;
  }
}

/* Between 700px and the desktop layout the columns are wide enough for the
   address, but not by much — let it wrap at a sensible point if a longer one
   is ever used, rather than overflowing into Socials. */
@media (min-width: 700px) and (max-width: 1199.98px) {
  .afs-contact-col {
    min-width: 0 !important;
    max-width: 100%;
  }
  .afs-contact-col li p,
  .afs-contact-col li a {
    white-space: normal !important;
    overflow-wrap: break-word;
  }
  .afs-contact-col li p.afs-nowrap {
    white-space: nowrap !important;
  }
}

/* ── Hero on mobile ───────────────────────────────────────────────────
 * Three things, all only below the desktop breakpoint.
 *
 * 1. The wordmark looked "cut off". It is not cropped — the asset's ink runs
 *    to row 84 of 111 and object-fit shows rows 21–90, so nothing is lost.
 *    The hero section carries `z-index: 1` and the nav does not, so the photo
 *    paints *over* the bottom 9px of the logo. Clearing the header fixes it.
 *
 * 2. The content sat high with a large empty band beneath: 88px above, 254px
 *    below. Centring it splits that evenly.
 *
 * 3. An empty 174px spacer row sits at the bottom of the hero. It holds
 *    nothing, but it drags the centred group upward, so it comes out.
 */
@media (max-width: 809.98px) {
  header[data-framer-name="Hero"] {
    margin-top: 16px;
  }

  header[data-framer-name="Hero"] > [data-framer-name="Container"] {
    /* The 638px the hero used to stand at came entirely from its 88px top
       padding plus the empty spacer below. Removing both collapsed it to the
       height of the text, so the height is now stated deliberately. 70vh
       leaves the photo a real presence without the dead band underneath, and
       a shorter box also means the landscape photo is scaled up less — which
       is the other half of the sharpness fix. */
    min-height: 70vh !important;
    justify-content: center !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
  }

  /* the empty spacer; it has no text and one empty child */
  header[data-framer-name="Hero"] > [data-framer-name="Container"] > [data-framer-name="row"] {
    display: none !important;
  }
}

/* ── About page: the hero wordmark ────────────────────────────────────
 * The hero title is not text — it is an 880x880 PNG of the wordmark shown
 * through a letterbox crop: `object-fit: cover` in a box 880 wide and 138
 * tall, i.e. 6.377:1.
 *
 * The box gets its height from `min-height`, because `height: min-content`
 * measures to zero over an absolutely-positioned image. Framer wrote that
 * min-height as 138px on desktop and **7px** on both smaller breakpoints, so
 * below 1200px the crop collapsed to a 7px band and the heading rendered as a
 * horizontal slice through the middle of the letters.
 *
 * Restating the height in pixels would only move the problem to the next
 * screen width, since the box is 80% of its container. Giving it the desktop
 * aspect ratio instead makes the height follow the width, so every screen
 * gets the identical crop: 281px wide on a 390px phone comes out 44px tall.
 *
 * `height: auto` is required as well — `min-content` would otherwise still
 * resolve to zero and win over the ratio.
 */
@media (max-width: 1199.98px) {
  header [data-framer-name="Logo"]:has(> div > img) {
    min-height: 0 !important;
    height: auto !important;
    aspect-ratio: 880 / 138;
  }
}

/* ── Contact page: the email headline ─────────────────────────────────
 * The address is set in the hero display style, 36px on a phone. At that
 * size "ablefloorsanding@gmail.com" is wider than a 390px screen, and since
 * it holds no spaces the browser breaks it wherever it runs out of room —
 * "ablefloorsanding@gm" on one line and "ail.com" on the next.
 *
 * Inserting a break opportunity after the "@" would put an invisible
 * character inside the address, which then travels with it when a visitor
 * copies it. Scaling the line to the screen instead keeps the text intact:
 * the glyphs average 0.51em, so 26 characters need about 13.3em of width,
 * and 6.4vw lands inside the content column from 320px upward while never
 * exceeding the 36px the design asks for.
 */
@media (max-width: 809.98px) {
  h2.framer-text:has(> a[href^="mailto:"]) {
    --framer-font-size: min(36px, 6.4vw) !important;
  }
}
