PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.2.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.2.1
7.2.2 7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 All 37 releases
mlsimport / public / css / mlsimport-listings.css

mlsimport-listings.css in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.2.1, at public/css/mlsimport-listings.css

1,533 lines 46.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * MLSImport standalone (theme_id 990) front-end styles.
3 * Self-contained BEM. Disable with:
4 * add_filter( 'mlsimport_standalone_styles', '__return_false' );
5 */
6
7 /* Design tokens shared by every standalone surface — the listings grid AND the
8 page blocks, which render outside .mlsimport-listings. Defining them on both
9 containers keeps the pager, accents and spacing identical everywhere (the
10 by-id / page-block pager was getting a different accent without this). */
11 .mlsimport-listings,
12 .mlsimport-page-block {
13 --mli-gap: 24px;
14 --mli-radius: 10px;
15 --mli-border: #e3e6ea;
16 --mli-accent: #1a73e8;
17 --mli-text: #1f2733;
18 --mli-muted: #6b7785;
19 }
20
21 /* Taxonomy/CPT archive wrapper (archive-mlsimport-property.php).
22
23 width:100% is load-bearing, not decoration. Themes routinely make the element this
24 wrapper lands in a FLEX container — Astra's .ast-container is display:flex — and a
25 flex item defaults to flex:0 1 auto, i.e. shrink-to-fit its content, not stretch to
26 the column. Without an explicit width the whole archive therefore sized itself to
27 the cards' max-content width, which made the archive's width depend on the "Cards
28 per row" setting: at 4 the cards asked for more than the column so it clamped to the
29 full 1200px and looked correct, but at 3 they only asked for ~955px and the entire
30 archive — search bar, results toolbar AND grid — collapsed to that, leaving ~245px
31 of dead space down the right. Claiming the full column makes 2, 3 and 4 identical.
32 border-box keeps the 30px padding inside that width instead of overflowing it. */
33 .mlsimport-archive {
34 box-sizing: border-box;
35 width: 100%;
36 /* Vertical padding is load-bearing too. The wrapper had `0 30px`, so on a CPT or
37 taxonomy archive the search bar sat flush against the theme header and the last
38 row of cards sat flush against the footer — measured 0px on both edges. Themes
39 do not add that space for us: the archive template emits its own wrapper rather
40 than the theme's entry markup, so nothing else has a say in it. */
41 padding: 48px 30px 64px;
42 }
43
44 .mlsimport-listings {
45 display: grid;
46 /* Default: search form full-width on top, results below. The half-map variant
47 below keeps its left sidebar (the map needs the two-column split). */
48 grid-template-columns: 1fr;
49 gap: var(--mli-gap);
50 color: var(--mli-text);
51 }
52
53 .mlsimport-listings--map {
54 grid-template-columns: 320px 1fr 1fr;
55 }
56
57 /* When the form spans the full width (default layout), lay its fields out as a
58 horizontal search bar rather than a tall vertical stack. The column count is the
59 "Search fields per row" setting (--mlsimport-search-cols, set inline on the form);
60 it falls back to 4-up when unset. The submit gets its own full-width row. */
61 .mlsimport-listings:not(.mlsimport-listings--map) .mlsimport-search {
62 display: grid;
63 grid-template-columns: repeat(var(--mlsimport-search-cols, 4), 1fr);
64 align-items: end;
65 }
66 /* The submit takes ONE grid cell, like every field, so it lands on the same row as
67 the last field and aligns to the column grid. It used to span the whole final row
68 (`grid-column: 1 / -1`) with justify-self:start, which left a ~75px button alone
69 on a full-width row — the only element in the form not aligned to a column, and
70 it read as an afterthought rather than the primary action. */
71 .mlsimport-listings:not(.mlsimport-listings--map) .mlsimport-search__submit {
72 grid-column: auto;
73 justify-self: stretch;
74 }
75
76 /* Narrow screens: cap the column count regardless of the "Search fields per row"
77 setting. TWO steps, the same ladder the half-map pane uses in
78 mlsimport-half-map.css — 2-up on tablets, single column on phones.
79 The 600px step used to stop at `1fr 1fr`, so the bar stayed 2-up all the way
80 down to 320px. That left each cell ~150px on a phone, narrower than the 240px
81 floor the range and beds popups carry, so an open popup ran off the right edge
82 of the screen with its Apply button out of reach. */
83 @media (max-width: 900px) {
84 .mlsimport-listings:not(.mlsimport-listings--map) .mlsimport-search {
85 grid-template-columns: 1fr 1fr;
86 }
87 }
88
89 @media (max-width: 600px) {
90 .mlsimport-listings:not(.mlsimport-listings--map) .mlsimport-search {
91 grid-template-columns: 1fr;
92 }
93 }
94
95 @media (max-width: 900px) {
96 .mlsimport-listings,
97 .mlsimport-listings--map {
98 grid-template-columns: 1fr;
99 }
100 }
101
102 /* Search form */
103 .mlsimport-search {
104 display: flex;
105 flex-direction: column;
106 gap: 12px;
107 padding: 18px;
108 border: 1px solid var(--mli-border);
109 border-radius: var(--mli-radius);
110 background: #fff;
111 align-self: start;
112 }
113
114 .mlsimport-search__field {
115 display: flex;
116 flex-direction: column;
117 gap: 4px;
118 font-size: 13px;
119 color: var(--mli-muted);
120 }
121
122 /* Sized to match .mlsimport-ms__control (mlsimport-multiselect.css), which is the
123 reference for every closed control on the search surface: 13px/300 Manrope in a
124 40px-tall box. These used to be 14px/400 at 37-39px tall, so a row mixing a
125 multiselect with a text or date field showed three different type sizes and
126 three different heights side by side. box-sizing + min-height rather than a
127 fixed height, so a control still grows if its font is scaled up. */
128 .mlsimport-search__field input,
129 .mlsimport-search__field select {
130 box-sizing: border-box;
131 min-height: 40px;
132 padding: 8px 10px;
133 border: 1px solid var(--mli-border);
134 border-radius: 6px;
135 font-family: inherit;
136 font-size: 13px;
137 font-weight: 300;
138 line-height: 22px;
139 color: var(--mli-text);
140 background: #fff;
141 }
142
143 /* The multiselect renders its empty state ("Any") in --mli-muted. Match that for
144 real placeholders, so an untouched form reads as one consistent grey rather than
145 grey multiselects beside near-black inputs. Actual VALUES stay --mli-text — the
146 multiselect switches to that colour once something is chosen, and a typed value
147 in muted grey would look disabled. */
148 .mlsimport-search__field input::placeholder {
149 color: var(--mli-muted);
150 opacity: 1;
151 }
152
153 /* Chrome gives date inputs extra intrinsic height for the picker icon, so
154 min-height alone left this one control 2px taller than its row. Pin it.
155
156 The calendar glyph is the browser's own, recoloured with a filter rather than
157 replaced. Swapping in an SVG background was tried and broke the control: the
158 padding-right needed to clear the icon shrinks the input's CONTENT box, and the
159 real (invisible) indicator lives inside that box — so it ended up ~30px left of
160 the glyph, and clicking the icon hit padding and never opened the picker.
161 Filtering leaves the native indicator exactly where the browser puts it, so it
162 stays clickable and keyboard-reachable; only its colour changes. grayscale drops
163 the glyph to neutral and the opacity lifts near-black to roughly the #8a8a8a of
164 the carets on every other control. */
165 .mlsimport-search__field input[type="date"] {
166 height: 40px;
167 }
168
169 .mlsimport-search__field input[type="date"]::-webkit-calendar-picker-indicator {
170 filter: grayscale( 1 ) opacity( 0.45 );
171 cursor: pointer;
172 }
173
174 /* ═══ THE focus treatment. One rule, every control, no colour. ═══════════════
175 This is the ONLY place focus is styled for the standalone surfaces. Everything
176 else — the multiselect control, the range slider handles, the search fields —
177 deliberately has no :focus rule of its own, so nothing can drift out of sync
178 again. If you need to change how focus looks, change it HERE.
179
180 Colourless by design: the ring must read the same whatever --mli-accent a site
181 sets, and an accent halo just smears the brand colour across the form as you tab.
182 #8a8a8a is a TRUE neutral (R=G=B) — note --mli-muted is #6b7785, a blue-tinted
183 grey that still reads as colour in a ring.
184
185 An `outline` rather than a border+shadow combo, because outline works
186 identically on every element type — inputs, buttons, links, and the slider's
187 plain <div> handles — without shifting layout or fighting each element's own
188 border and shadow.
189
190 `:focus-visible` so the ring appears for keyboard navigation but not on a mouse
191 click. Applied uniformly, every control behaves the same way — which is the
192 point; the old mix of :focus and :focus-visible was why clicking Beds & Baths
193 looked different from clicking Area. */
194 .mlsimport-listings :focus,
195 .mlsimport-page-block :focus {
196 outline: none;
197 }
198
199 /* Focused field: a 2px solid border in the site accent. One rule for every field
200 type — text, number, date, select, the multiselect control, and the range /
201 beds-baths toggles — so they all respond identically to a click.
202
203 Only the COLOUR changes — the border stays 1px, exactly as at rest. A 2px focus
204 border was tried and rejected: these boxes are sized by min-height:40px, and the
205 extra border pushed the content past that minimum, so every field grew to 42px
206 and nudged its grid row as you clicked through the form. Compensating with
207 padding fixed the height but is fragile. Keeping the width constant means nothing
208 can shift, and the accent colour alone is a clear enough signal. */
209 .mlsimport-search__field input:focus,
210 .mlsimport-search__field select:focus,
211 .mlsimport-search-form__field input:focus,
212 .mlsimport-search-form__field select:focus,
213 .mlsimport-contact-form__field input:not([type="checkbox"]):not([type="radio"]):focus,
214 .mlsimport-contact-form__field select:focus,
215 .mlsimport-contact-form__field textarea:focus,
216 .mlsimport-results__sort select:focus,
217 .mlsimport-range__toggle:focus,
218 .mlsimport-bedsbaths__toggle:focus {
219 outline: none;
220 border-color: var(--mli-accent);
221 }
222
223 .mlsimport-listings :focus-visible,
224 .mlsimport-page-block :focus-visible {
225 outline: 2px solid #8a8a8a;
226 outline-offset: 2px;
227 }
228
229 .mlsimport-search__row {
230 display: grid;
231 grid-template-columns: 1fr 1fr;
232 gap: 10px;
233 }
234
235 /* :hover/:focus repeat the resting selector so a theme's pink `button:hover`/
236 `:focus` reset can't repaint the submit; the :hover brightness below still wins. */
237 .mlsimport-search__submit,
238 .mlsimport-search__submit:hover,
239 .mlsimport-search__submit:focus {
240 /* Same 40px box as the fields it sits beside. Keeps its own 14px/600 type —
241 it's the primary action, not another input, so it should read heavier. */
242 box-sizing: border-box;
243 min-height: 40px;
244 padding: 10px 14px;
245 border: 0;
246 border-radius: 6px;
247 background: var(--mli-accent);
248 color: #fff;
249 font-family: inherit;
250 font-size: 14px;
251 line-height: 1.2;
252 font-weight: 600;
253 cursor: pointer;
254 }
255
256 .mlsimport-search__submit:hover {
257 filter: brightness(0.95);
258 }
259
260 /* Results */
261 .mlsimport-results {
262 display: flex;
263 flex-direction: column;
264 gap: var(--mli-gap);
265 }
266
267 /* Count + sort share one row above the grid: count left, sort control right. */
268 .mlsimport-results__toolbar {
269 display: flex;
270 align-items: center;
271 justify-content: space-between;
272 gap: 12px;
273 flex-wrap: wrap;
274 }
275
276 .mlsimport-results__meta {
277 font-size: 14px;
278 color: var(--mli-muted);
279 }
280
281 .mlsimport-results__sort {
282 display: flex;
283 align-items: center;
284 gap: 8px;
285 margin-left: auto;
286 font-size: 13px;
287 color: var(--mli-muted);
288 }
289
290 /* The Sort control is the .mlsimport-interest widget (the same one the property
291 page's "I'm interested in" uses), so the native select below is only the fallback
292 the enhancer hides once it runs — and the styling that matters is the trigger.
293
294 The trigger rules have to live HERE rather than being reused from
295 mlsimport-property-sections.css: that file isn't loaded on listing pages, and its
296 rules are written against --mlsimport-* tokens that don't exist here. The dropdown
297 PANEL does come for free — the enhancer builds it from .mlsimport-location__list,
298 which is defined further down this file.
299
300 Sized 40px/13px to match the search controls it sits above, not the property
301 page's 48px/14px. Same component, sized for its context. */
302 .mlsimport-results__sort-control {
303 position: relative;
304 display: block;
305 }
306
307 .mlsimport-interest.is-enhanced > select {
308 display: none;
309 }
310
311 .mlsimport-results__sort-control .mlsimport-interest__trigger {
312 box-sizing: border-box;
313 display: flex;
314 align-items: center;
315 /* height, not min-height: the label span inside carries its own line-height and
316 pushed the button to 48px. Flex centring keeps the text vertically true. */
317 height: 40px;
318 line-height: 1;
319 /* The button sizes to whatever option is selected, so the toolbar jumped every
320 time the choice changed ("Newest" is ~12px narrower than "Bedrooms"). A floor
321 wide enough for the longest label holds it still. min-width, not width, so a
322 longer translation still fits rather than being clipped. */
323 min-width: 150px;
324 padding: 8px 30px 8px 10px;
325 border: 1px solid var(--mli-border);
326 border-radius: 6px;
327 font-family: inherit;
328 font-size: 13px;
329 font-weight: 300;
330 color: var(--mli-text);
331 background-color: #fff;
332 background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1L5 5L9 1' fill='none' stroke='%238a8a8a' stroke-width='1.5' stroke-linecap='square'/%3E%3C/svg%3E" );
333 background-repeat: no-repeat;
334 background-position: right 12px center;
335 background-size: 8px 5px;
336 text-align: left;
337 cursor: pointer;
338 }
339
340 /* Same focus treatment as every other field — see the focus block above. */
341 .mlsimport-results__sort-control .mlsimport-interest__trigger:focus,
342 .mlsimport-interest.is-open .mlsimport-interest__trigger {
343 outline: none;
344 border-color: var(--mli-accent);
345 }
346
347 /* The panel is .mlsimport-location__list, which deliberately sets no text colour so
348 its options inherit from whatever surface it is dropped onto. In the search bar
349 that is correct; in this toolbar the options inherited the THEME's body colour
350 (a warm brown) instead of the listings ink, and rendered at the autocomplete's
351 14px against the trigger's 13px. A panel that brings its own background has to
352 bring its own foreground — pin both so the open list matches the closed box. */
353 .mlsimport-results__sort-control .mlsimport-interest__list {
354 color: var(--mli-text);
355 }
356
357 .mlsimport-results__sort-control .mlsimport-location__option {
358 font-size: 13px;
359 font-weight: 300;
360 }
361
362 /* Fallback for the un-enhanced native select (no JS): same box, same caret. */
363 .mlsimport-results__sort select {
364 box-sizing: border-box;
365 min-height: 40px;
366 padding: 8px 30px 8px 10px;
367 border: 1px solid var(--mli-border);
368 border-radius: 6px;
369 font-family: inherit;
370 /* 13px/300/40px, same as every other closed control — see the search field rule. */
371 font-size: 13px;
372 font-weight: 300;
373 color: var(--mli-text);
374 appearance: none;
375 -webkit-appearance: none;
376 background-color: #fff;
377 background-image: url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1L5 5L9 1' fill='none' stroke='%238a8a8a' stroke-width='1.5' stroke-linecap='square'/%3E%3C/svg%3E" );
378 background-repeat: no-repeat;
379 background-position: right 12px center;
380 background-size: 8px 5px;
381 }
382
383 .mlsimport-results__grid {
384 display: grid;
385 /* Cards-per-row (2/3/4) is set inline as --mli-cols by render_grid; falls back to
386 3 when unset. Drops to 2 then 1 on narrower screens via the media queries below. */
387 grid-template-columns: repeat(var(--mli-cols, 3), 1fr);
388 /* Fallback so the grid keeps its gutter when reused outside the
389 .mlsimport-listings wrapper (e.g. the agent profile listings), where the
390 --mli-gap token isn't in scope and the gap would otherwise collapse to 0. */
391 gap: var(--mli-gap, 20px);
392 }
393
394 @media (max-width: 900px) {
395 .mlsimport-results__grid {
396 grid-template-columns: repeat(2, 1fr);
397 }
398 }
399
400 @media (max-width: 600px) {
401 .mlsimport-results__grid {
402 grid-template-columns: 1fr;
403 }
404 }
405
406 /* The half-map variant's results column is narrow — keep it auto-fitting, not a
407 forced three-up. */
408 .mlsimport-listings--map .mlsimport-results__grid {
409 grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
410 }
411
412 /* The empty message is rendered INSIDE .mlsimport-results__grid, which is a 3-column
413 grid — so it landed in a single ~431px cell and centred its text within that one
414 column, leaving the sentence floating in the left third instead of lining up with
415 the results count above it. Span every column and align left, so it starts on the
416 same edge as the toolbar and the cards it replaces. */
417 .mlsimport-results__empty {
418 grid-column: 1 / -1;
419 padding: 40px 0;
420 text-align: left;
421 font-size: 14px;
422 color: var(--mli-muted);
423 }
424
425 /* While the grid repaints over AJAX it is filled with skeleton cards (below), so
426 the only thing left to do is freeze interaction — no opacity fade, the shimmer
427 is the loading cue. */
428 .mlsimport-results.is-loading {
429 pointer-events: none;
430 }
431
432 /* Skeleton loading — placeholder cards shown while property cards load over AJAX
433 (search, filter, pagination). Each reuses the .mlsimport-listing-card box so the
434 grid keeps its exact columns/sizing; the media block and text lines shimmer. */
435 .mlsimport-skeleton-card {
436 cursor: default;
437 }
438
439 .mlsimport-skeleton-card:hover {
440 box-shadow: none;
441 transform: none;
442 }
443
444 .mlsimport-skeleton__box {
445 height: 180px; /* matches .mlsimport-listing-card__media */
446 }
447
448 .mlsimport-skeleton__line {
449 display: block;
450 height: 12px;
451 border-radius: 6px;
452 }
453
454 .mlsimport-skeleton__line--price {
455 width: 40%;
456 height: 18px;
457 }
458
459 .mlsimport-skeleton__line--title {
460 width: 85%;
461 }
462
463 .mlsimport-skeleton__line--specs {
464 width: 60%;
465 }
466
467 /* Shimmer fill shared by the media box and the text lines. */
468 .mlsimport-skeleton__box,
469 .mlsimport-skeleton__line {
470 background: linear-gradient( 90deg, #eef1f4 25%, #e2e6eb 37%, #eef1f4 63% );
471 background-size: 400% 100%;
472 animation: mlsimport-skeleton-shimmer 1.4s ease infinite;
473 }
474
475 @keyframes mlsimport-skeleton-shimmer {
476 0% {
477 background-position: 100% 0;
478 }
479 100% {
480 background-position: 0 0;
481 }
482 }
483
484 @media (prefers-reduced-motion: reduce) {
485 .mlsimport-skeleton__box,
486 .mlsimport-skeleton__line {
487 animation: none;
488 }
489 }
490
491 /* Card — compact, price-forward (shared by results grid + similar listings) */
492 .mlsimport-listing-card {
493 display: flex;
494 flex-direction: column;
495 border: 1px solid var(--mlsimport-hairline, var(--mli-border));
496 border-radius: var(--mlsimport-radius-card, var(--mli-radius));
497 overflow: hidden;
498 background: var(--mlsimport-card-bg, #fff);
499 transition: box-shadow 0.2s ease, transform 0.2s ease;
500 }
501
502 .mlsimport-listing-card:hover {
503 box-shadow: var(--mlsimport-shadow-card, 0 6px 22px rgba(20, 30, 50, 0.12));
504 transform: translateY(-3px);
505 }
506
507 .mlsimport-listing-card__link {
508 text-decoration: none;
509 color: inherit;
510 display: flex;
511 flex-direction: column;
512 height: 100%;
513 }
514
515 .mlsimport-listing-card__media {
516 position: relative;
517 height: 180px;
518 background-color: #eef1f4;
519 background-size: cover;
520 background-position: center;
521 background-repeat: no-repeat;
522 }
523
524 .mlsimport-listing-card__badge {
525 position: absolute;
526 top: 11px;
527 left: 11px;
528 padding: 5px 11px;
529 border-radius: var(--mlsimport-radius-chip, 999px);
530 background: rgba(255, 255, 255, 0.92);
531 color: var(--mlsimport-ink, var(--mli-text));
532 font-size: 11px;
533 font-weight: 500;
534 letter-spacing: 0.04em;
535 text-transform: uppercase;
536 line-height: 1;
537 }
538
539 .mlsimport-listing-card__body {
540 display: flex;
541 flex-direction: column;
542 gap: 5px;
543 flex: 1;
544 padding: 14px 16px 15px;
545 }
546
547 .mlsimport-listing-card__price {
548 font-size: 19px;
549 font-weight: 600;
550 letter-spacing: -0.01em;
551 color: var(--mlsimport-ink, var(--mli-text));
552 }
553
554 .mlsimport-listing-card__title {
555 margin: 0;
556 font-size: 14px;
557 font-weight: 400;
558 line-height: 1.35;
559 color: var(--mlsimport-ink-2, var(--mli-muted));
560 display: -webkit-box;
561 -webkit-line-clamp: 2;
562 -webkit-box-orient: vertical;
563 overflow: hidden;
564 }
565
566 .mlsimport-listing-card__specs {
567 margin: 0;
568 font-size: 13px;
569 color: var(--mlsimport-ink-2, var(--mli-muted));
570 }
571
572 /* Agency row — listing office (left) + MLS logo (right), pinned to the bottom */
573 .mlsimport-listing-card__agency {
574 display: flex;
575 align-items: center;
576 justify-content: space-between;
577 gap: 10px;
578 margin-top: auto;
579 padding-top: 12px;
580 border-top: 1px solid var(--mlsimport-hairline, var(--mli-border));
581 }
582
583 .mlsimport-listing-card__office {
584 min-width: 0;
585 font-size: 11.5px;
586 font-weight: 500;
587 color: var(--mlsimport-muted, var(--mli-muted));
588 overflow: hidden;
589 text-overflow: ellipsis;
590 white-space: nowrap;
591 }
592
593 .mlsimport-listing-card__mls {
594 max-height: 35px;
595 width: auto;
596 flex-shrink: 0;
597 }
598
599 /* Pager — one component for the AJAX listings grid and the GET Search Results
600 block (Mlsimport_Pagination). */
601 .mlsimport-pager {
602 display: flex;
603 gap: 8px;
604 justify-content: center;
605 align-items: center;
606 flex-wrap: wrap;
607 margin-top: 22px;
608 }
609
610 /* The grid's pager lives in this wrapper so the AJAX layer can repaint it; hide
611 the wrapper's gap when there is only one page (empty nav). */
612 .mlsimport-results__pager:empty {
613 display: none;
614 }
615
616 .mlsimport-pager__btn {
617 padding: 8px 12px;
618 border: 1px solid var(--mli-border);
619 border-radius: 6px;
620 background: #fff;
621 color: var(--mli-text);
622 font-family: inherit;
623 font-size: 14px;
624 line-height: 1.2;
625 text-decoration: none;
626 cursor: pointer;
627 }
628
629 /* The agent-profile listings pager renders outside .mlsimport-listings /
630 .mlsimport-page-block, where --mli-accent is not defined, so fall back to the
631 root-level --mlsimport-accent (same Main Color) instead of an empty value. */
632 a.mlsimport-pager__btn:hover {
633 border-color: var(--mli-accent, var(--mlsimport-accent));
634 color: var(--mli-accent, var(--mlsimport-accent));
635 }
636
637 .mlsimport-pager__btn.is-active {
638 background: var(--mli-accent, var(--mlsimport-accent));
639 color: #fff;
640 border-color: var(--mli-accent, var(--mlsimport-accent));
641 cursor: default;
642 }
643
644 .mlsimport-pager__gap {
645 padding: 8px 4px;
646 color: var(--mli-muted);
647 }
648
649 /* Content Slider (page block) — a multi-card Splide carousel. Each slide holds one
650 full-width property card (the card keeps its own styling). The arrows reproduce
651 the WpResidence editorial slider: a 38px square pair, hairline border, that invert
652 to dark on hover, grouped at the top-right above the cards. */
653 .mlsimport-content-slider {
654 position: relative;
655 padding-top: 54px;
656 }
657
658 .mlsimport-content-slider .splide__slide {
659 height: auto;
660 }
661
662 .mlsimport-content-slider .splide__slide > * {
663 box-sizing: border-box;
664 width: 100%;
665 height: 100%;
666 /* Zero any padding the theme's editor styles inject onto the card <article>:
667 it would inset the image from the card border and (with content-box) widen the
668 card past its slide, eating the gap and overlapping neighbours. The card's own
669 body keeps its padding; the image stays flush. */
670 padding: 0;
671 margin: 0;
672 }
673
674 /* Fallback before Splide mounts — and the editor preview, where the block-editor
675 iframe does not load Splide's own assets onto the ServerSideRender output. Lay the
676 slides out as a horizontal scroll-snap row (3 / 2 / 1 across) instead of a raw
677 bulleted <ul>. On the front end Splide adds .is-initialized and takes over. */
678 .mlsimport-content-slider:not(.is-initialized) .splide__track {
679 overflow: hidden;
680 }
681
682 .mlsimport-content-slider:not(.is-initialized) .splide__list {
683 display: flex;
684 gap: 1.5rem;
685 margin: 0;
686 padding: 0;
687 list-style: none;
688 overflow-x: auto;
689 scroll-snap-type: x mandatory;
690 }
691
692 .mlsimport-content-slider:not(.is-initialized) .splide__slide {
693 flex: 0 0 calc(33.333% - 1rem);
694 min-width: 0;
695 list-style: none;
696 scroll-snap-align: start;
697 }
698
699 @media (max-width: 980px) {
700 .mlsimport-content-slider:not(.is-initialized) .splide__slide {
701 flex-basis: calc(50% - 0.75rem);
702 }
703 }
704
705 @media (max-width: 640px) {
706 .mlsimport-content-slider:not(.is-initialized) .splide__slide {
707 flex-basis: 85%;
708 }
709 }
710
711 .mlsimport-content-slider .splide__arrows {
712 position: absolute;
713 top: 0;
714 right: 0;
715 display: flex;
716 gap: 0;
717 /* Re-enable interaction: the block-editor preview wraps the SSR output in
718 pointer-events:none (so card links can't hijack the editor), which would
719 otherwise also kill the arrow clicks. The cards stay non-interactive. */
720 pointer-events: auto;
721 }
722
723 .mlsimport-content-slider .splide__arrow {
724 position: static;
725 top: auto;
726 left: auto;
727 right: auto;
728 transform: none;
729 width: 38px;
730 height: 38px;
731 border: 1px solid #e7e7e7;
732 border-radius: 0;
733 background: transparent;
734 opacity: 1;
735 pointer-events: auto;
736 transition: background 0.2s ease, border-color 0.2s ease;
737 }
738
739 .mlsimport-content-slider .splide__arrow--next {
740 border-left: none;
741 }
742
743 .mlsimport-content-slider .splide__arrow svg {
744 width: 14px;
745 height: 14px;
746 fill: #333;
747 transition: fill 0.2s ease;
748 }
749
750 .mlsimport-content-slider .splide__arrow:hover:not(:disabled) {
751 background: #1a1a1a;
752 border-color: #1a1a1a;
753 }
754
755 .mlsimport-content-slider .splide__arrow:hover:not(:disabled) svg {
756 fill: #fff;
757 }
758
759 .mlsimport-content-slider .splide__arrow:disabled {
760 opacity: 0.35;
761 cursor: default;
762 }
763
764 /* Map */
765 .mlsimport-map {
766 min-height: 480px;
767 border-radius: var(--mli-radius);
768 overflow: hidden;
769 border: 1px solid var(--mli-border);
770 }
771
772 /* Agent box */
773 .mlsimport-agent-box {
774 padding: 16px 18px;
775 border: 1px solid var(--mli-border);
776 border-radius: var(--mli-radius);
777 background: #fff;
778 max-width: 320px;
779 }
780
781 .mlsimport-agent-box__name {
782 margin: 0 0 4px;
783 font-size: 18px;
784 }
785
786 .mlsimport-agent-box__office {
787 margin: 0 0 10px;
788 color: var(--mli-muted);
789 font-size: 14px;
790 }
791
792 .mlsimport-agent-box__contact {
793 list-style: none;
794 margin: 0;
795 padding: 0;
796 display: flex;
797 flex-direction: column;
798 gap: 4px;
799 }
800
801 .mlsimport-agent-box__contact a {
802 color: var(--mli-accent);
803 text-decoration: none;
804 }
805
806 /* Agents archive.
807
808 Same shrink-to-fit trap as .mlsimport-archive above: the wrapper is a flex item in
809 the theme's content container, so with no width it sizes to the cards' max-content
810 and the auto-fill grid settles on fewer columns than the column can hold — the
811 directory rendered two cards wide inside a 1200px column with the rest empty. */
812 .mlsimport-agents {
813 box-sizing: border-box;
814 width: 100%;
815 }
816
817 .mlsimport-agents__grid {
818 display: grid;
819 grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
820 gap: var(--mli-gap);
821 }
822
823 .mlsimport-agent-card {
824 border: 1px solid var(--mli-border);
825 border-radius: var(--mli-radius);
826 overflow: hidden;
827 background: #fff;
828 text-align: center;
829 }
830
831 .mlsimport-agent-card__link {
832 text-decoration: none;
833 color: inherit;
834 }
835
836 .mlsimport-agent-card__photo img {
837 width: 100%;
838 height: 200px;
839 object-fit: cover;
840 /* See .mlsimport-agent-card__img — a centred crop decapitates a portrait. */
841 object-position: top center;
842 }
843
844 .mlsimport-agent-card__name {
845 margin: 12px 0 4px;
846 font-size: 16px;
847 }
848
849 .mlsimport-agent-card__phone {
850 margin: 0 0 14px;
851 color: var(--mli-muted);
852 font-size: 14px;
853 }
854
855 /* Team Directory page block — a fixed-column grid of agent tiles, each the whole
856 card linking to that agent's page. Reuses the .mlsimport-agent-card base above. */
857 .mlsimport-agents-directory {
858 display: grid;
859 grid-template-columns: repeat(var(--mlsimport-agents-cols, 4), minmax(0, 1fr));
860 gap: var(--mlsimport-agents-gap, var(--mli-gap));
861 }
862
863 .mlsimport-agent-card--dir {
864 display: block;
865 text-decoration: none;
866 color: inherit;
867 border-radius: var(--mlsimport-agents-radius, var(--mli-radius));
868 transition: box-shadow 0.15s ease, transform 0.15s ease;
869 }
870
871 .mlsimport-agent-card--dir:hover {
872 box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
873 transform: translateY(-2px);
874 }
875
876 /* The photo box owns the height, not the image, so a card with a portrait and a
877 card with the empty placeholder occupy exactly the same space — otherwise a
878 single agent without a photo makes its whole row lose its baseline. */
879 .mlsimport-agent-card--dir .mlsimport-agent-card__photo {
880 display: block;
881 height: 240px;
882 overflow: hidden;
883 line-height: 0;
884 }
885
886 .mlsimport-agent-card__img {
887 display: block;
888 width: 100%;
889 height: 100%;
890 object-fit: cover;
891 /* Anchor the crop to the top. object-fit:cover centres by default, which on a
892 portrait crops through the chin and leaves a headless agent. */
893 object-position: top center;
894 }
895
896 /* `.mlsimport-agent-card__photo img` above is (0,1,1) and outranks the (0,1,0)
897 `__img` rule, so it would pin the tile image back to 200px inside its 240px
898 box and leave a strip of empty placeholder under every photo. Match its weight
899 so the image actually fills the box. */
900 .mlsimport-agent-card--dir .mlsimport-agent-card__photo img {
901 height: 100%;
902 object-position: top center;
903 }
904
905 .mlsimport-agent-card__img--empty {
906 background: linear-gradient(135deg, #eef1f4, #dfe4ea);
907 }
908
909 .mlsimport-agent-card__body {
910 padding: 14px 12px 16px;
911 }
912
913 .mlsimport-agent-card--dir .mlsimport-agent-card__name {
914 display: block;
915 margin: 0 0 4px;
916 font-size: 16px;
917 font-weight: 600;
918 }
919
920 .mlsimport-agent-card__title {
921 display: block;
922 color: var(--mli-accent);
923 font-size: 13px;
924 font-weight: 500;
925 }
926
927 .mlsimport-agent-card__office {
928 display: block;
929 margin-top: 4px;
930 color: var(--mli-muted);
931 font-size: 13px;
932 }
933
934 @media (max-width: 900px) {
935 .mlsimport-agents-directory {
936 grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
937 }
938 }
939
940 @media (max-width: 480px) {
941 .mlsimport-agents-directory {
942 grid-template-columns: repeat(2, minmax(0, 1fr));
943 }
944 }
945
946 /* ── Page blocks (home/landing-page building blocks) ───────────────────────── */
947 .mlsimport-page-block {
948 margin: 0 0 28px;
949 }
950
951 /* Search Results with the refine bar switched off: the form stays in the DOM as
952 the AJAX state carrier (so paging keeps the search), only hidden from view.
953
954 Both classes are on the SAME element on purpose. The layout rule above is
955 `.mlsimport-listings:not(.mlsimport-listings--map) .mlsimport-search { display: grid }`,
956 and a :not() contributes its argument's specificity — so that selector scores (0,3,0)
957 while a plain `.mlsimport-listings--no-form .mlsimport-search` scores only (0,2,0) and
958 LOSES to it no matter where it sits in the file. The bar was therefore never hidden.
959 Doubling up the class here matches (0,3,0) and, being later, wins. */
960 .mlsimport-listings.mlsimport-listings--no-form .mlsimport-search {
961 display: none;
962 }
963
964 /* Search + contact forms. */
965 /* The Contact Form block lays its fields out on a 12-column grid; each field's
966 "Width" setting is how many of those columns it spans, so two halves share a
967 row and three thirds make a three-up row. No width = the full row. */
968 .mlsimport-contact-form {
969 display: grid;
970 grid-template-columns: repeat(12, 1fr);
971 gap: 12px;
972 align-items: end;
973 }
974 /* The Search Form block lays out on the same 12-column grid as the Contact Form,
975 so both blocks share one width vocabulary. Each field — and the submit button —
976 spans the columns its "Width" setting says, which is how the button can sit
977 INLINE as the last cell of a row instead of always taking a row of its own.
978 The other two search surfaces (the listings grid form and the half-map form)
979 keep their own --mlsimport-search-cols grid; they have no per-field widths. */
980 .mlsimport-search-form {
981 display: grid;
982 grid-template-columns: repeat(12, 1fr);
983 gap: 12px;
984 align-items: end;
985 }
986 .mlsimport-search-form__field,
987 .mlsimport-search-form__submit {
988 grid-column: span 12;
989 }
990 /* The optional heading claims a row of its own above the fields. */
991 .mlsimport-search-form__title {
992 grid-column: span 12;
993 margin: 0;
994 font-size: 18px;
995 line-height: 1.3;
996 }
997 .mlsimport-search-form__field--two-thirds,
998 .mlsimport-search-form__submit--two-thirds {
999 grid-column: span 8;
1000 }
1001 .mlsimport-search-form__field--half,
1002 .mlsimport-search-form__submit--half {
1003 grid-column: span 6;
1004 }
1005 .mlsimport-search-form__field--third,
1006 .mlsimport-search-form__submit--third {
1007 grid-column: span 4;
1008 }
1009 .mlsimport-search-form__field--quarter,
1010 .mlsimport-search-form__submit--quarter {
1011 grid-column: span 3;
1012 }
1013 .mlsimport-search-form__field,
1014 .mlsimport-contact-form__field {
1015 display: flex;
1016 flex-direction: column;
1017 gap: 4px;
1018 flex: 1 1 160px;
1019 font-size: 13px;
1020 color: var(--mli-muted, #666);
1021 }
1022 /* A search field never squeezes below a usable width, however narrow its Width
1023 setting makes its grid cell. The 12 tracks are 1fr = minmax(auto, 1fr), so this
1024 floor raises each track's auto minimum and the cells grow to honour it.
1025 Scoped through the form (0,2,0) so it outranks the min-width:0 that
1026 mlsimport-multiselect.css sets on this class and which loads after this file.
1027 That rule exists to stop wide multiselect CHIPS widening a column — a fixed
1028 floor cannot do that (only min-width:auto could), so it stays honoured. */
1029 .mlsimport-search-form .mlsimport-search-form__field {
1030 min-width: 240px;
1031 }
1032
1033 /* Below 600px every cell claims the full row, so the button becomes a full-width
1034 bar under the fields however its Width is configured. */
1035 @media (max-width: 600px) {
1036 .mlsimport-search-form__field,
1037 .mlsimport-search-form__submit {
1038 grid-column: span 12;
1039 }
1040 }
1041 .mlsimport-search-form__field input,
1042 .mlsimport-search-form__field select,
1043 .mlsimport-contact-form__field input:not([type="checkbox"]):not([type="radio"]),
1044 .mlsimport-contact-form__field select,
1045 .mlsimport-contact-form__field textarea {
1046 padding: 9px 11px;
1047 border: 1px solid var(--mli-border, #e2e2e2);
1048 border-radius: 6px;
1049 font-family: inherit;
1050 font-size: 14px;
1051 }
1052 /* Text inputs and selects compute to ~37px from their padding alone, which left
1053 them short of the 40px multiselect beside them. Pin the whole row to 40px so
1054 every control in the search form — button included — is the same height. */
1055 .mlsimport-search-form__field input,
1056 .mlsimport-search-form__field select {
1057 box-sizing: border-box;
1058 min-height: 40px;
1059 }
1060 .mlsimport-contact-form__field {
1061 grid-column: span 12;
1062 }
1063 .mlsimport-contact-form__field--two-thirds {
1064 grid-column: span 8;
1065 }
1066 .mlsimport-contact-form__field--half {
1067 grid-column: span 6;
1068 }
1069 .mlsimport-contact-form__field--third {
1070 grid-column: span 4;
1071 }
1072 .mlsimport-contact-form__field--quarter {
1073 grid-column: span 3;
1074 }
1075 /* A checkbox (GDPR consent) reads as one line: the box, then its label. */
1076 .mlsimport-contact-form__field--checkbox {
1077 flex-direction: row;
1078 align-items: center;
1079 gap: 8px;
1080 }
1081 /* A radio group is a fieldset — a legend is not a flex item, so it stacks. */
1082 .mlsimport-contact-form__field--radio {
1083 display: block;
1084 border: 0;
1085 margin: 0;
1086 padding: 0;
1087 }
1088 .mlsimport-contact-form__field--radio label {
1089 display: flex;
1090 align-items: center;
1091 gap: 6px;
1092 margin-top: 4px;
1093 }
1094 @media (max-width: 600px) {
1095 .mlsimport-contact-form__field {
1096 grid-column: span 12;
1097 }
1098 }
1099 /* :hover/:focus repeat the resting selector so a theme's pink `button:hover`/
1100 `:focus` reset can't repaint the accent submit; the :hover brightness wins below. */
1101 .mlsimport-search-form__submit,
1102 .mlsimport-search-form__submit:hover,
1103 .mlsimport-search-form__submit:focus,
1104 .mlsimport-contact-form__submit,
1105 .mlsimport-contact-form__submit:hover,
1106 .mlsimport-contact-form__submit:focus {
1107 padding: 10px 22px;
1108 border: 0;
1109 border-radius: 6px;
1110 background: var(--mli-accent);
1111 color: #fff;
1112 font-family: inherit;
1113 font-size: 14px;
1114 line-height: 1.2;
1115 cursor: pointer;
1116 }
1117 /* The search button fills its grid cell — the cell's width IS the button's width,
1118 which is what the per-field "Width" setting is for. */
1119 /* 40px is the height of the multiselect control (.mlsimport-ms__control), which
1120 is the tallest field in the form — the button matches it so the row lines up.
1121 min-height, not height, so the --large modifier can still grow past it. */
1122 .mlsimport-search-form__submit {
1123 display: inline-flex;
1124 align-items: center;
1125 justify-content: center;
1126 gap: 8px;
1127 box-sizing: border-box;
1128 width: 100%;
1129 min-height: 40px;
1130 }
1131 /* Same hover as the other accent buttons (.mlsimport-search__submit). */
1132 .mlsimport-search-form__submit {
1133 transition: filter 0.2s ease;
1134 }
1135 .mlsimport-search-form__submit:hover {
1136 filter: brightness(0.95);
1137 }
1138 .mlsimport-search-form__submit--small {
1139 padding: 7px 16px;
1140 font-size: 13px;
1141 }
1142 .mlsimport-search-form__submit--large {
1143 padding: 14px 28px;
1144 font-size: 16px;
1145 }
1146 .mlsimport-search-form__submit-icon {
1147 width: 1em;
1148 height: 1em;
1149 flex: none;
1150 object-fit: contain;
1151 }
1152
1153 /* Location autocomplete: the suggestion list hangs off the input without moving
1154 it, so the form's grid row height never changes as suggestions appear. */
1155 /* A flex column, like the field wrapper itself — so the input STRETCHES to the
1156 cell instead of being sized by width:100%. The inputs carry padding and a
1157 border but the page may be content-box, where width:100% then overflows the
1158 grid cell by 24px and swallows the gutter. Stretch sizing is immune to that. */
1159 .mlsimport-location {
1160 position: relative;
1161 display: flex;
1162 flex-direction: column;
1163 }
1164 .mlsimport-location__list {
1165 position: absolute;
1166 z-index: 20;
1167 top: calc(100% + 4px);
1168 left: 0;
1169 right: 0;
1170 margin: 0;
1171 padding: 4px;
1172 list-style: none;
1173 max-height: 260px;
1174 overflow-y: auto;
1175 background: #fff;
1176 border: 1px solid var(--mli-border, #e2e2e2);
1177 border-radius: 6px;
1178 box-shadow: 0 8px 24px -12px rgba(0, 0, 0, 0.3);
1179 }
1180 .mlsimport-location__list[hidden] {
1181 display: none;
1182 }
1183 .mlsimport-location__option {
1184 display: flex;
1185 align-items: center;
1186 gap: 10px;
1187 padding: 8px 10px;
1188 border-radius: 4px;
1189 font-size: 14px;
1190 cursor: pointer;
1191 }
1192 .mlsimport-location__option:hover,
1193 .mlsimport-location__option.is-active {
1194 background: var(--mli-soft, #f2f4f3);
1195 }
1196 .mlsimport-location__type {
1197 margin-left: auto;
1198 font-size: 11px;
1199 letter-spacing: 0.06em;
1200 text-transform: uppercase;
1201 color: var(--mli-muted, #666);
1202 border: 1px solid var(--mli-border, #e2e2e2);
1203 border-radius: 4px;
1204 padding: 1px 6px;
1205 }
1206 .mlsimport-hp {
1207 position: absolute;
1208 left: -9999px;
1209 }
1210 /* The submit spans the full row unless its "Width" narrows it, so it can sit
1211 INLINE as the last cell of a row — the same vocabulary the fields use. */
1212 .mlsimport-contact-form__submit {
1213 grid-column: span 12;
1214 justify-self: start;
1215 }
1216 .mlsimport-contact-form__submit--two-thirds {
1217 grid-column: span 8;
1218 }
1219 .mlsimport-contact-form__submit--half {
1220 grid-column: span 6;
1221 }
1222 .mlsimport-contact-form__submit--third {
1223 grid-column: span 4;
1224 }
1225 .mlsimport-contact-form__submit--quarter {
1226 grid-column: span 3;
1227 }
1228 /* Where the button sits inside its cell. Justified makes the cell's width the
1229 button's width; the other three leave it at its natural size. */
1230 .mlsimport-contact-form--btn-start .mlsimport-contact-form__submit {
1231 justify-self: start;
1232 }
1233 .mlsimport-contact-form--btn-center .mlsimport-contact-form__submit {
1234 justify-self: center;
1235 }
1236 .mlsimport-contact-form--btn-end .mlsimport-contact-form__submit {
1237 justify-self: end;
1238 }
1239 .mlsimport-contact-form--btn-stretch .mlsimport-contact-form__submit {
1240 justify-self: stretch;
1241 }
1242 .mlsimport-contact-form__submit--small {
1243 padding: 7px 16px;
1244 font-size: 13px;
1245 }
1246 .mlsimport-contact-form__submit--large {
1247 padding: 14px 28px;
1248 font-size: 16px;
1249 }
1250 /* Field size presets — padding and type size move together, so the three steps
1251 read as one scale rather than as separate knobs. Medium is the base rule. */
1252 .mlsimport-contact-form--small .mlsimport-contact-form__field input:not([type="checkbox"]):not([type="radio"]),
1253 .mlsimport-contact-form--small .mlsimport-contact-form__field select,
1254 .mlsimport-contact-form--small .mlsimport-contact-form__field textarea {
1255 padding: 6px 9px;
1256 font-size: 13px;
1257 }
1258 .mlsimport-contact-form--large .mlsimport-contact-form__field input:not([type="checkbox"]):not([type="radio"]),
1259 .mlsimport-contact-form--large .mlsimport-contact-form__field select,
1260 .mlsimport-contact-form--large .mlsimport-contact-form__field textarea {
1261 padding: 13px 15px;
1262 font-size: 16px;
1263 }
1264 /* The optional heading claims a row of its own above the fields. */
1265 .mlsimport-contact-form__title {
1266 grid-column: span 12;
1267 margin: 0;
1268 font-size: 18px;
1269 line-height: 1.3;
1270 }
1271 /* The consent row reads as one line: the box, then its wording and link. */
1272 .mlsimport-contact-form__consent {
1273 grid-column: 1 / -1;
1274 font-size: 13px;
1275 color: var(--mli-muted, #666);
1276 }
1277 .mlsimport-contact-form__consent .mlsimport-property-lead-form__consent {
1278 display: flex;
1279 align-items: flex-start;
1280 gap: 8px;
1281 }
1282 .mlsimport-contact-form__message {
1283 grid-column: 1 / -1;
1284 font-size: 14px;
1285 }
1286 /* Below 600px the button becomes a full-width bar under the fields however its
1287 Width and alignment are configured — matching how the fields collapse. */
1288 @media (max-width: 600px) {
1289 /* Two classes deep so it outranks the --btn-* alignment rules above, which are
1290 themselves (0,2,0). */
1291 .mlsimport-contact-form .mlsimport-contact-form__submit {
1292 grid-column: span 12;
1293 justify-self: stretch;
1294 }
1295 }
1296
1297 /* Map with listings. */
1298 .mlsimport-page-block--map .mlsimport-map {
1299 width: 100%;
1300 height: 420px;
1301 border-radius: 8px;
1302 background: #eef1f4;
1303 }
1304
1305 /* ── Property card v2 — horizontal split (photo left, details right) ─────────── */
1306 .mlsimport-listing-card--v2 .mlsimport-listing-card__link {
1307 display: grid;
1308 grid-template-columns: 40% 1fr;
1309 align-items: stretch;
1310 }
1311 .mlsimport-listing-card--v2 .mlsimport-listing-card__media {
1312 height: 100%;
1313 min-height: 180px;
1314 }
1315 .mlsimport-listing-card--v2 .mlsimport-listing-card__body {
1316 display: flex;
1317 flex-direction: column;
1318 justify-content: center;
1319 gap: 6px;
1320 }
1321 /* The heart lives outside the link, so pull it back over the 40% photo column. */
1322 .mlsimport-listing-card--v2 .mlsimport-fav--heart {
1323 right: calc( 60% + 10px );
1324 }
1325 @media ( max-width: 480px ) {
1326 .mlsimport-listing-card--v2 .mlsimport-listing-card__link {
1327 grid-template-columns: 1fr;
1328 }
1329 .mlsimport-listing-card--v2 .mlsimport-fav--heart {
1330 right: 10px;
1331 }
1332 }
1333
1334 /* ── Property card v3 — full-bleed photo with overlaid details ───────────────── */
1335 .mlsimport-listing-card--v3 .mlsimport-listing-card__media {
1336 position: relative;
1337 height: 320px;
1338 }
1339 .mlsimport-listing-card--v3 .mlsimport-listing-card__overlay {
1340 position: absolute;
1341 left: 0;
1342 right: 0;
1343 bottom: 0;
1344 padding: 28px 16px 14px;
1345 color: #fff;
1346 background: linear-gradient( to top, rgba( 0, 0, 0, 0.72 ) 0%, rgba( 0, 0, 0, 0 ) 100% );
1347 }
1348 .mlsimport-listing-card--v3 .mlsimport-listing-card__title,
1349 .mlsimport-listing-card--v3 .mlsimport-listing-card__price,
1350 .mlsimport-listing-card--v3 .mlsimport-listing-card__specs {
1351 color: #fff;
1352 }
1353 .mlsimport-listing-card--v3 .mlsimport-listing-card__price {
1354 font-size: 20px;
1355 font-weight: 700;
1356 }
1357
1358 /* -------------------------------------------------------------------------
1359 Category widgets — Category Slider + Display Categories.
1360 A tile is the term featured image with the term name (and optional listing
1361 count) overlaid, linking to the term archive. Look controls arrive as the
1362 --mlsimport-cat-* custom properties emitted on the wrapper.
1363 ------------------------------------------------------------------------- */
1364 .mlsimport-cat-tile {
1365 position: relative;
1366 display: block;
1367 overflow: hidden;
1368 height: var(--mlsimport-cat-height, 240px);
1369 border-radius: var(--mlsimport-cat-radius, 8px);
1370 text-decoration: none;
1371 color: #fff;
1372 background: #d7dde3;
1373 }
1374
1375 .mlsimport-cat-tile__img {
1376 position: absolute;
1377 inset: 0;
1378 width: 100%;
1379 height: 100%;
1380 background-size: cover;
1381 background-position: center;
1382 background-repeat: no-repeat;
1383 transition: transform 0.4s ease;
1384 }
1385
1386 .mlsimport-cat-tile__img--empty {
1387 display: block;
1388 background: linear-gradient(135deg, #c9d2da, #aab6c0);
1389 }
1390
1391 .mlsimport-cat-tile:hover .mlsimport-cat-tile__img {
1392 transform: scale(1.06);
1393 }
1394
1395 .mlsimport-cat-tile__overlay {
1396 position: absolute;
1397 inset: 0;
1398 background: linear-gradient(to top, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0) 55%);
1399 }
1400
1401 .mlsimport-cat-tile__body {
1402 position: absolute;
1403 inset: auto 0 0 0;
1404 display: flex;
1405 flex-direction: column;
1406 padding: var(--mlsimport-cat-pad, 16px);
1407 }
1408
1409 .mlsimport-cat-tile__title {
1410 font-size: var(--mlsimport-cat-title-size, 18px);
1411 font-weight: 600;
1412 line-height: 1.2;
1413 color: var(--mlsimport-cat-title-color, #fff);
1414 margin-bottom: var(--mlsimport-cat-title-mb, 4px);
1415 }
1416
1417 .mlsimport-cat-tile__tagline {
1418 font-size: 13px;
1419 opacity: 0.9;
1420 color: var(--mlsimport-cat-title-color, #fff);
1421 margin-bottom: var(--mlsimport-cat-tagline-mb, 0);
1422 }
1423
1424 /* Display Categories — grid (fixed columns) or auto grid (min unit width). */
1425 .mlsimport-cat-list {
1426 display: grid;
1427 grid-template-columns: repeat(var(--mlsimport-cat-cols, 4), 1fr);
1428 gap: var(--mlsimport-cat-gap, 16px);
1429 }
1430
1431 .mlsimport-cat-list--autogrid {
1432 grid-template-columns: repeat(auto-fill, minmax(var(--mlsimport-cat-min-width, 220px), 1fr));
1433 }
1434
1435 @media (max-width: 980px) {
1436 .mlsimport-cat-list:not(.mlsimport-cat-list--autogrid) {
1437 grid-template-columns: repeat(2, 1fr);
1438 }
1439 }
1440
1441 @media (max-width: 600px) {
1442 .mlsimport-cat-list:not(.mlsimport-cat-list--autogrid) {
1443 grid-template-columns: 1fr;
1444 }
1445 }
1446
1447 /* The three designs key on the shared .mlsimport-cat--design-N modifier (on the outer
1448 page-block wrapper), so ONE ruleset styles both the Display Categories grid AND the
1449 Category Slider — the tile markup is identical in both. Simple black/white palette
1450 throughout: a bottom scrim (design 1, on the tile default), a full dim (design 2),
1451 and a solid caption bar (design 3) — all plain black, white text. */
1452
1453 /* Design 2 — full dim overlay, title centred. */
1454 .mlsimport-cat--design-2 .mlsimport-cat-tile__overlay {
1455 background: rgba(0, 0, 0, 0.4);
1456 }
1457
1458 .mlsimport-cat--design-2 .mlsimport-cat-tile__body {
1459 inset: 0;
1460 align-items: center;
1461 justify-content: center;
1462 text-align: center;
1463 }
1464
1465 /* Design 3 — solid caption bar + optional frame border (border vars are Display
1466 Categories only; the slider leaves them unset, so its tiles simply have no border). */
1467 .mlsimport-cat--design-3 .mlsimport-cat-tile {
1468 border: var(--mlsimport-cat-border-w, 0) solid var(--mlsimport-cat-border-c, #e0e0e0);
1469 }
1470
1471 .mlsimport-cat--design-3 .mlsimport-cat-tile__overlay {
1472 display: none;
1473 }
1474
1475 .mlsimport-cat--design-3 .mlsimport-cat-tile__body {
1476 background: rgba(0, 0, 0, 0.72);
1477 }
1478
1479 /* Category Slider — Splide track of tiles. Fallback layout before Splide mounts
1480 (and in the editor iframe, which does not load Splide onto the SSR preview):
1481 a horizontal scroll-snap row instead of a raw bulleted <ul>. */
1482 .mlsimport-category-slider .splide__slide {
1483 height: auto;
1484 list-style: none;
1485 }
1486
1487 /* Navigation arrows — the single-property gallery slider's treatment: a 50×50
1488 semi-transparent white disc centred on the tile edges (Splide's default round
1489 shape, centre position and chevron kept), so the two sliders read the same
1490 instead of the bare grey Splide default. The Elementor Category Slider's Arrow
1491 colour controls layer over this base.
1492
1493 opacity is pinned to 1 (translucency comes from the colour's own alpha) so the
1494 arrow reads as the SAME colour resting and on hover — Splide's default hover
1495 fades opacity 0.7 → 0.9, which made a Style-tab arrow colour look different on
1496 hover. Disabled arrows stay dimmed. */
1497 .mlsimport-category-slider .splide__arrow {
1498 width: 50px;
1499 height: 50px;
1500 background: #ffffff8c;
1501 opacity: 1;
1502 }
1503
1504 .mlsimport-category-slider .splide__arrow:disabled {
1505 opacity: 0.35;
1506 }
1507
1508 .mlsimport-category-slider:not(.is-initialized) .splide__track {
1509 overflow: hidden;
1510 }
1511
1512 .mlsimport-category-slider:not(.is-initialized) .splide__list {
1513 display: flex;
1514 gap: var(--mlsimport-cat-gap, 16px);
1515 margin: 0;
1516 padding: 0;
1517 list-style: none;
1518 overflow-x: auto;
1519 scroll-snap-type: x mandatory;
1520 }
1521
1522 .mlsimport-category-slider:not(.is-initialized) .splide__slide {
1523 flex: 0 0 calc(33.333% - 1rem);
1524 min-width: 0;
1525 scroll-snap-align: start;
1526 }
1527
1528 @media (max-width: 640px) {
1529 .mlsimport-category-slider:not(.is-initialized) .splide__slide {
1530 flex-basis: 80%;
1531 }
1532 }
1533