/* Small additions on top of the original site.css for the one hand-added
   widget that was not part of the original prototype design: the coverage
   map on the Service Areas page. Everything else on the site (header,
   footer, hero, comfort dial, service cards, market tabs, floating call
   button, etc.) is the prototype's own original CSS -- untouched. */

/* Service Areas coverage map -- the real Google Maps (see
   msmech_coverage_map_html()), embedded as a plain iframe pointed at
   maps.google.com. Fixed-ratio box so the page never jumps while the
   embed loads; the background color only ever shows for the instant
   before the iframe paints. */
.coverage-map-google { position: relative; width: 100%; height: 440px; max-height: 60vh; border: 1px solid var(--line); border-radius: 17px; overflow: hidden; background: #eef5f8; }
.coverage-map-iframe { display: block; width: 100%; height: 100%; border: 0; }
.coverage-map-legend { display: inline-flex; align-items: center; gap: 8px; margin-top: 12px; padding: 8px 16px; border: 1px solid var(--line); border-radius: 999px; background: var(--paper); font-family: var(--font-body); font-size: 14px; color: var(--navy); }
.coverage-map-legend-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--sky); opacity: .6; border: 1px solid var(--sky); }
.coverage-map-note { color: var(--muted-ink); font-size: 13px; margin-top: 10px; }
.coverage-map-open-link { color: var(--navy); font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }
.coverage-map-open-link:hover { color: var(--sky); }

/* The two coverage-radius circles drawn on top of the Google Maps embed
   above (positioned by site.js -- see the "[data-coverage-map]" block).
   pointer-events:none on the whole overlay so it never blocks dragging or
   zooming the real map underneath; only the circle/label are visible. */
.coverage-map-overlay { position: absolute; inset: 0; pointer-events: none; z-index: 1; }
.coverage-map-circle {
	position: absolute;
	transform: translate(-50%, -50%);
	border-radius: 50%;
	background: color-mix(in srgb, var(--sky) 20%, transparent);
	border: 2px solid color-mix(in srgb, var(--sky) 80%, var(--navy) 20%);
	display: flex;
	align-items: center;
	justify-content: center;
}
.coverage-map-circle-label {
	background: var(--navy);
	color: #fff;
	font-family: var(--font-body);
	font-weight: 700;
	font-size: 12px;
	line-height: 1;
	padding: 4px 11px;
	border-radius: 999px;
	white-space: nowrap;
	box-shadow: 0 2px 8px color-mix(in srgb, var(--navy) 35%, transparent);
}

/* Locks the map: sits on top of the iframe (above the circle overlay, so
   it catches every click/drag/scroll/touch before the iframe ever sees
   it) and does nothing with them -- no dragging, no zooming, no Street
   View. Fully transparent, so the real map tiles and the two circles
   underneath still show exactly as normal; only interaction is removed.
   cursor:default (not the usual map "grab" hand) is the visual hint that
   this map doesn't move. */
.coverage-map-lock { position: absolute; inset: 0; z-index: 2; cursor: default; background: transparent; }

/* Any element a link can jump to (Get a Quote / Book an Appointment, the
   "Learn more" topic links, and any future anchor) keeps clear of the
   sticky header when the page scrolls there -- otherwise the header
   covers the top of the section the visitor just jumped to. */
*[id] { scroll-margin-top: 120px; }

/* "Get a Quote" / "Book an Appointment" tab buttons on the Contact page --
   show which form is currently open (assets/site.js toggles this class). */
.cb-quote.active, .cb-appointment.active { background: var(--amber); color: var(--navy); border-color: var(--amber); }

/* Contact page: "Call Houston" / "Call Dallas" / "Get a Quote" / "Book an
   Appointment" stack in a single full-width column, one per row -- the
   simplest layout that CANNOT overflow sideways regardless of container
   width, button text length, or a two-column grid's per-column math, and
   regardless of whatever classes/inline style an earlier save of this
   page's content may still carry. Deliberately matches on
   ".contact-aside .call-buttons" alone (not also requiring ".cb-stacked")
   -- ".contact-aside" only ever appears on this one page, so this is safe
   to apply broadly. Uses "flex-direction:column" rather than switching
   "display" to grid/block: if a stale saved copy still has an inline
   "display:flex" on this element, this still takes over completely (flex
   items already stack cleanly in a column with no display change needed),
   so there's nothing left for a leftover inline style to fight over. The
   !important throughout means this never needs the page reverted to take
   effect. */
.contact-aside .call-buttons {
	display: flex !important;
	flex-direction: column !important;
	flex-wrap: nowrap !important;
	align-items: stretch !important;
	gap: 10px !important;
	width: 100% !important;
}
.contact-aside .call-buttons > .cb {
	width: 100% !important;
	max-width: 100% !important;
	min-width: 0;
	box-sizing: border-box !important;
	justify-content: center;
	white-space: normal !important;
}
/* Extra safety net for this same row: an old saved copy of the Contact
   page might have "Get a Quote"/"Book an Appointment" split off into a
   separate wrapper of their own (e.g. a leftover ".stack" div copied from
   the header at some point, which is never width- or position-
   constrained on its own) rather than sharing the ".call-buttons" div
   above with "Call Houston"/"Call Dallas". Whatever wrapper any of these
   four buttons are actually sitting in, and whatever "position" a much
   older, unrelated rule might have left on any of them, this forces every
   one of them back into normal, full-width, block-level flow -- nothing
   in ".contact-aside" can float free of the card or sit off to the side
   of it after this. */
.contact-aside .stack {
	display: flex !important;
	flex-direction: column !important;
	gap: 10px !important;
	width: 100% !important;
	position: static !important;
}
.contact-aside .cb,
.contact-aside .stack > * {
	position: static !important;
	width: 100% !important;
	max-width: 100% !important;
	box-sizing: border-box !important;
}

/* Custom-styled dropdowns: every <select> in a form field (Service area /
   What do you need? / Space type on the Contact page, plus any other form
   field that uses one) -- swaps the browser's plain, inconsistent-looking
   native arrow icon for one that matches the rest of the site's icon
   style, and adds a proper hover/focus state that matches the text
   inputs next to it. Still a real, plain <select> underneath (appearance
   is the only thing changed) -- keyboard and screen-reader behavior are
   completely untouched. */
.field select {
	appearance: none;
	-webkit-appearance: none;
	-moz-appearance: none;
	background-color: var(--field-bg);
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%235e7077' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 12px center;
	background-size: 18px;
	padding-right: 38px;
	cursor: pointer;
	transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
}
.field select:hover { border-color: color-mix(in srgb, var(--sky) 55%, var(--line)); background-color: color-mix(in srgb, var(--sky) 4%, var(--field-bg)); }
.field select:focus { border-color: var(--sky); box-shadow: 0 0 0 3px color-mix(in srgb, var(--sky) 18%, transparent); }
.field select:invalid { color: var(--muted-ink); }
.field select option { color: var(--navy); }
@media (max-width: 640px) {
	.field select { background-size: 16px; padding-right: 34px; }
}

/* Floating images added from Edit Mode's "+ Add Image" -- free-standing
   photos placed anywhere on a page, independent of the normal text flow.
   Position, width, opacity and layering (front of/behind the text) are
   all set per-image as inline style, saved with the page; this is only
   the structural default every visitor's browser needs. */
.msmech-floating-img { position: absolute; }
.msmech-floating-img > img { width: 100%; height: auto; display: block; -webkit-user-drag: none; user-select: none; }

/* Reviews carousel (Home page) -- unlimited reviews (wp-admin > Reviews),
   sliding left/right, with admin-controlled auto-scroll speed/direction
   plus manual arrows that work either direction at any time. */
.reviews-carousel { position: relative; margin-top: 20px; }
.reviews-track {
	display: flex; gap: 20px; overflow-x: auto; scroll-snap-type: x mandatory;
	scroll-behavior: smooth; padding: 4px 2px 14px; -webkit-overflow-scrolling: touch;
	scrollbar-width: none;
}
.reviews-track::-webkit-scrollbar { display: none; }
.review-card {
	flex: 0 0 auto; scroll-snap-align: start; width: min(var(--reviews-card-width, 360px), 84vw);
	border: 1px solid var(--line); background: var(--reviews-card-bg, var(--paper)); border-radius: 17px;
	padding: 28px; font-family: var(--font-body);
}
.review-card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.review-card-head strong { color: var(--reviews-name, var(--navy)); font-family: var(--font-heading); }
/* Round avatar spot next to the reviewer's name -- colored initials by
   default (msmech_review_initials()), or the review's own Featured Image
   (wp-admin > Reviews > [a review] > Featured Image) when one is set. */
.review-avatar {
	display: inline-flex; align-items: center; justify-content: center;
	width: 40px; height: 40px; min-width: 40px; border-radius: 50%;
	overflow: hidden; flex-shrink: 0;
}
.review-initials {
	color: #fff; font-size: 15px; font-weight: 700; letter-spacing: .02em;
	font-family: var(--font-heading);
}
.review-avatar-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.review-stars { color: var(--reviews-stars, var(--amber)); font-size: 22px; margin: 0 0 10px; letter-spacing: 3px; }
.review-text { color: var(--reviews-text, var(--muted-ink)); font-size: var(--reviews-fontsize, 14px); line-height: 1.6; margin: 0; }
.reviews-arrow {
	position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
	width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--line);
	background: var(--paper); color: var(--navy); font-size: 20px; line-height: 1;
	cursor: pointer; display: grid; place-items: center;
	box-shadow: 0 8px 20px color-mix(in srgb, var(--navy) 10%, transparent);
}
.reviews-arrow:hover { background: var(--ivory); }
.reviews-arrow-prev { left: -14px; }
.reviews-arrow-next { right: -14px; }
@media (max-width: 640px) {
	.reviews-arrow-prev { left: -6px; }
	.reviews-arrow-next { right: -6px; }
}

/* Footer social icon row (Appearance > Customize > Footer). Icons that have
   no URL set are skipped entirely by msmech_social_links_html(), so this
   row just quietly doesn't render at all until at least one link is filled
   in -- no empty gap is left behind. */
.footer-social-links {
	display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.footer-social-link {
	display: inline-flex; align-items: center; justify-content: center;
	width: 34px; height: 34px; min-width: 34px; border-radius: 50%;
	background: color-mix(in srgb, var(--navy) 8%, transparent);
	color: var(--navy); transition: background .15s ease, color .15s ease, transform .15s ease;
}
.footer-social-link:hover {
	background: var(--sky); color: #fff; transform: translateY(-1px);
}
@media (max-width: 640px) {
	.footer-bottom { align-items: flex-start; }
	.footer-social-links { order: 3; width: 100%; margin-top: 6px; }
}

/* Persistent "back" button (top-left, every page). Deliberately small and
   translucent -- it sits over whatever content happens to scroll under it,
   so it stays a light, blurred chip rather than a solid block that would
   blank out the text underneath. Its exact top offset is set by JS (see
   site.js) to sit just below the sticky header, whatever that header's
   real height turns out to be on this page/breakpoint -- never overlapping
   the logo. */
.msmech-back-btn {
	position: fixed; left: 12px; top: 74px; z-index: 50;
	display: inline-flex; align-items: center; justify-content: center;
	width: 32px; height: 32px; border-radius: 50%; border: 1px solid color-mix(in srgb, var(--navy) 20%, transparent);
	background: color-mix(in srgb, var(--paper) 46%, transparent);
	color: var(--navy); cursor: pointer; padding: 0;
	box-shadow: 0 3px 8px color-mix(in srgb, var(--navy) 10%, transparent);
	transition: background .15s ease, transform .15s ease;
}
.msmech-back-btn:hover { background: color-mix(in srgb, var(--paper) 85%, transparent); transform: translateY(-1px); }
.msmech-back-btn[hidden] { display: none; }

/* A photo that was manually resized/zoomed/repositioned from Edit Mode
   (see assets/edit-mode.js -- imageFrameFor()) gets wrapped in one of
   these: a fixed-box frame the photo fills via object-fit:cover, so a
   custom size and a custom zoom/crop never fight each other. Needed by
   every visitor, not just while editing, since it's part of the saved
   page content once an admin has used Size/Zoom on a plain (previously
   unwrapped) image. */
/* The frame's own width/height above are captured as literal inline
   pixels from whatever screen the editor was using at the time (usually a
   desktop) -- e.g. style="width:790px". Nothing capped that, so the SAME
   frame stayed exactly that many pixels wide on a phone too, pushing it
   (and everything sharing its row, like the header logo's text) past the
   edge of the screen instead of shrinking to fit -- a real bug reported
   live (text and photos "cut off," only readable by rotating to
   landscape). A plain max-width:100% turned out not to be enough on its
   own: percentage widths only resolve against a container that has a
   definite size, and a frame's nearest container here (a flex row like
   the header's brand link, or a CSS grid cell) is often sized to fit its
   *content* instead -- which, with an oversized frame inside it, becomes
   circular and the percentage effectively does nothing. Overriding the
   captured pixel width back to "auto" (with !important, since inline
   style otherwise always wins) sidesteps that: in the normal block/grid
   layout most photos on this site use, "auto" naturally means "fill the
   available width," which is exactly what should happen once the
   original captured size no longer fits. */
.msmech-img-frame { display: block; position: relative; overflow: hidden; width: auto !important; max-width: 100%; }
/* The one place "auto" doesn't mean "fill available width" is a flex
   row -- the header/footer brand link is `display:inline-flex`, where an
   auto-sized flex item instead shrinks to its own content's size. Since
   the photo inside a frame is always pulled out of flow (position:
   absolute, to make object-fit:cover work), that content size is ~0, so
   the header logo would otherwise just vanish. A resized logo should
   never be anything other than the theme's own standard logo size anyway
   (36px normally, 54px in the tall/desktop header), so those are pinned
   explicitly here regardless of whatever pixel size Edit Mode's Size tool
   last captured for it. */
.brand .msmech-img-frame { width: 36px !important; height: 36px !important; max-width: 36px !important; }
.site-header-lg .brand .msmech-img-frame { width: 54px !important; height: 54px !important; max-width: 54px !important; }

/* Rotating photo set ("Make this a rotating photo set" in Edit Mode --
   see assets/edit-mode.js / the .msmech-imgset block in assets/site.js).
   site.js re-applies the photo-sizing part of this itself as inline
   styles on every photo the moment it runs, on whichever container a set
   happens to live in (a hero photo, a plain in-text photo, a grid
   layout, ...) -- the img rule below is a static backstop so a set still
   looks right (one photo, filling its box) even for the very first
   paint before that script has run.

   Deliberately NOT position:relative on .msmech-imgset itself (a real
   bug this comment is here to stop someone reintroducing): every photo
   this can wrap is already guaranteed a definite-size positioned
   ancestor by the time it can become a set -- either the theme's own
   existing "stacked photo" container (.trust-photo, .hero-visual, ...,
   which already carries its own min-height + position:relative), or the
   .msmech-img-frame span every OTHER photo gets wrapped in the moment
   Size/Zoom is first touched (see imageFrameFor() in edit-mode.js --
   always runs before a photo can be converted to a set at all), which
   captures an explicit pixel width/height of its own. If .msmech-imgset
   itself were also made a positioned ancestor, its own height (nothing
   in it is in normal flow -- every photo inside is position:absolute)
   would resolve to 0, and it would become the containing block instead
   of that outer, correctly-sized box -- collapsing every photo inside
   to a 0-height, invisible box regardless of how large its true
   container is. Leaving it position:static (the default) lets the
   percentage heights below skip straight past it to that outer box, the
   same way they always have. */
.msmech-imgset > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* Get a Quote / Book an Appointment forms (inc/lead-forms.php handles the
   actual submit). A honeypot field a real visitor never sees or reaches
   by tab -- off-screen, not display:none (some bots skip filling in a
   display:none field specifically, off-screen doesn't fool them the same
   way) -- and the small "Sending..."/error line shown above the submit
   button while the form itself stays visible (a full failure never wipes
   out what someone already typed). .form-success (see site.css) is the
   separate, bigger "thanks" card that REPLACES the form on an actual
   successful submit. */
.msmech-hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.form-status { margin: -4px 0 6px; font-size: var(--fs-12); display: block; }
.form-status.is-sending { color: var(--muted-ink); }
.form-status.is-error { color: var(--amber-deep); font-weight: 700; }
/* The "Send another request" button on the .form-success "thanks" card
   (site.css) -- a <button>, not an <a>, since it resets/re-shows the form
   instead of navigating anywhere, so it needs its native button chrome
   stripped to read identically to the "Call Houston/Dallas" .text-link
   right above it. */
.form-success-reset { background: none; border: none; padding: 0; margin-top: 14px; cursor: pointer; font: inherit; }
