Make WordPress Themes

Changeset 334420


Ignore:
Timestamp:
06/16/2026 09:12:49 PM (5 months ago)
Author:
themedropbox
Message:

New version of Havenlytics Realty - 1.15.5

Location:
havenlytics-realty/1.15.5
Files:
19 added
5 deleted
27 edited
1 copied

Legend:

Unmodified
Added
Removed
  • havenlytics-realty/1.15.5/assets/css/havenlytics-compat.css

    r334339 r334420  
    9797   
    9898}
     99body.wp-theme-havenlytics-realty
     100.hvnly-property-single__carousel-slide
     101.hvnly-property-single__similar-content
     102.hvnly-property-single__btn {
     103
     104        color: var(--hvnly-color-white)!important;
     105   
     106}
     107
     108
    99109/* -------------------------------------------------------------------------
    100110   Property archive & grid
    … …  
    220230}
    221231
     232/* Homepage hero map — Customizer vh height (overrides plugin 550px fixed heights). */
     233.hvn-realty-section--hero .hvn-realty-hero__map,
     234.hvn-realty-section--hero .hvn-realty-home-map,
     235.hvn-realty-section--hero .hvn-realty-home-map__canvas,
     236.hvn-realty-section--hero #hvnly-map-placeholder,
     237.hvn-realty-section--hero .hvnly-map-placeholder,
     238.hvn-realty-section--hero .hvnly-map-view,
     239.hvn-realty-section--hero .hvnly-map-container,
     240.hvn-realty-section--hero .hvnly-properties-map,
     241.hvn-realty-section--hero #hvnly-properties-map,
     242.hvn-realty-section--hero .leaflet-container {
     243        height: var(--hvn-realty-hero-height, 70vh) !important;
     244        min-height: var(--hvn-realty-hero-height, 70vh) !important;
     245        max-height: none;
     246        border-radius: 0;
     247}
     248
     249@media (max-width: 640px) {
     250        .hvn-realty-section--hero .hvn-realty-hero__map,
     251        .hvn-realty-section--hero .hvn-realty-home-map,
     252        .hvn-realty-section--hero .hvn-realty-home-map__canvas,
     253        .hvn-realty-section--hero #hvnly-map-placeholder,
     254        .hvn-realty-section--hero .hvnly-map-placeholder,
     255        .hvn-realty-section--hero .hvnly-map-view,
     256        .hvn-realty-section--hero .hvnly-map-container,
     257        .hvn-realty-section--hero .hvnly-properties-map,
     258        .hvn-realty-section--hero #hvnly-properties-map,
     259        .hvn-realty-section--hero .leaflet-container {
     260                height: var(--hvn-realty-hero-height-mobile, 50vh) !important;
     261                min-height: var(--hvn-realty-hero-height-mobile, 50vh) !important;
     262        }
     263}
     264
    222265.hvn-realty-home .hvnly-property-card,
    223266.hvn-realty-home .hvnly-agent-card,
    … …  
    254297
    255298@media (max-width: 768px) {
     299        body.wp-theme-havenlytics-realty
    256300        .hvn-realty-havenlytics-view .hvnly-container,
     301        body.wp-theme-havenlytics-realty
    257302        .hvn-realty-havenlytics-view .hvnly-property-single__container,
     303        body.wp-theme-havenlytics-realty
    258304        .hvn-realty-havenlytics-view .hvnly-agent-single__container {
    259305                padding-inline: var(--hvn-theme-space-sm, 0.75rem);
    … …  
    264310        }
    265311}
     312
     313
     314@media (max-width: 767px) {
     315        body.wp-theme-havenlytics-realty
     316        .hvnly-container{
     317                padding:0 ;
     318    }
     319}
     320
  • havenlytics-realty/1.15.5/assets/css/polish.css

    r334339 r334420  
    6363        flex-wrap: wrap;
    6464        align-items: center;
    65         gap: 0.25rem 1.5rem;
    6665        list-style: none;
    6766        margin: 0;
    … …  
    626625}
    627626
    628 /* Blog pagination — see layouts.css (.hvn-theme-pagination) */
    629 
    630627/* ==========================================================================
    631628   PHASE 6 — Responsive layout grid
    … …  
    703700        position: absolute;
    704701        inset: 0;
    705         background: rgba(15, 23, 42, 0.55);
     702        background: rgba(15, 23, 42, 0.58);
     703        backdrop-filter: blur(4px);
     704        -webkit-backdrop-filter: blur(4px);
    706705        opacity: 0;
    707         transition: opacity 0.2s ease;
     706        transition: opacity 0.32s cubic-bezier(0.22, 1, 0.36, 1);
    708707}
    709708
    … …  
    714713.hvn-theme-property-search-panel__dialog {
    715714        position: relative;
    716         width: min(100%, 640px);
     715        width: min(100%, 720px);
     716        max-height: calc(100vh - 2rem);
    717717        margin-top: clamp(2rem, 8vh, 5rem);
    718718        padding: 1.75rem 1.5rem 1.5rem;
    … …  
    720720        background: #fff;
    721721        box-shadow: 0 24px 60px rgba(15, 23, 42, 0.18);
    722         transform: translateY(12px) scale(0.98);
     722        transform: translateY(18px) scale(0.96);
    723723        opacity: 0;
    724         transition: transform 0.22s ease, opacity 0.22s ease;
     724        overflow-y: auto;
     725        overscroll-behavior: contain;
     726        -webkit-overflow-scrolling: touch;
     727        transition:
     728                transform 0.32s cubic-bezier(0.22, 1, 0.36, 1),
     729                opacity 0.32s cubic-bezier(0.22, 1, 0.36, 1);
    725730}
    726731
    … …  
    774779}
    775780
     781.hvn-theme-property-search-panel__field:last-child {
     782        margin-bottom: 0;
     783}
     784
     785.hvn-theme-property-search-panel__row .hvn-theme-property-search-panel__field {
     786        margin-bottom: 0;
     787}
     788
     789.hvn-theme-property-search-panel__row {
     790        display: grid;
     791        grid-template-columns: repeat(2, minmax(0, 1fr));
     792        gap: 1rem;
     793        margin-bottom: 1rem;
     794}
     795
     796.hvn-theme-property-search-panel__actions {
     797        margin-top: 0.5rem;
     798}
     799
    776800.hvn-theme-property-search-panel__field label {
    777801        display: block;
    … …  
    782806}
    783807
    784 .hvn-theme-property-search-panel__row {
    785         display: grid;
    786         grid-template-columns: repeat(2, minmax(0, 1fr));
    787         gap: 1rem;
    788 }
    789 
    790 .hvn-theme-property-search-panel__actions {
    791         margin-top: 0.5rem;
    792 }
    793 
    794808.hvn-theme-property-search-panel__submit {
    795809        width: 100%;
    … …  
    809823        .hvn-theme-property-search-panel__dialog {
    810824                width: 100%;
     825                max-height: none;
    811826                min-height: 100%;
    812827                margin-top: 0;
    813828                border-radius: 0;
    814829                padding: 1.25rem 1rem 1.5rem;
     830                padding-top: max(1.25rem, env(safe-area-inset-top, 0px));
     831                padding-bottom: max(1.5rem, env(safe-area-inset-bottom, 0px));
     832                transform: translateY(100%);
     833        }
     834
     835        .hvn-theme-property-search-panel.is-open .hvn-theme-property-search-panel__dialog {
     836                transform: translateY(0) scale(1);
    815837        }
    816838
    … …  
    857879}
    858880
     881/* Desktop dropdown — premium panel, fade + translateY reveal */
     882.hvn-theme-header-row,
     883.hvn-theme-nav,
     884#site-navigation,
     885.hvn-theme-main-navigation {
     886        overflow: visible;
     887}
     888
     889.hvn-theme-nav-menu .menu-item-has-children {
     890        position: relative;
     891}
     892
     893.hvn-theme-nav-menu .sub-menu {
     894        position: absolute;
     895        top: 100%;
     896        left: 0;
     897        margin: 0;
     898        padding: 0.5rem;
     899        min-width: 240px;
     900        list-style: none;
     901        background: var(--hvn-theme-color-white, #fff);
     902        border: 1px solid rgba(15, 23, 42, 0.08);
     903        border-radius: 12px;
     904        box-shadow:
     905                0 4px 6px -2px rgba(15, 23, 42, 0.06),
     906                0 16px 32px -8px rgba(15, 23, 42, 0.14);
     907        opacity: 0;
     908        visibility: hidden;
     909        transform: translateY(8px);
     910        pointer-events: none;
     911        transition:
     912                opacity 0.2s cubic-bezier(0.22, 1, 0.36, 1),
     913                transform 0.2s cubic-bezier(0.22, 1, 0.36, 1),
     914                visibility 0.2s;
     915        z-index: 100001;
     916}
     917
     918.hvn-theme-nav-menu .menu-item-has-children > .sub-menu::before {
     919        content: "";
     920        position: absolute;
     921        top: -14px;
     922        left: 0;
     923        right: 0;
     924        height: 14px;
     925        background: transparent;
     926}
     927
     928.hvn-theme-nav-menu .menu-item-has-children:hover > .sub-menu,
     929.hvn-theme-nav-menu .menu-item-has-children.is-dropdown-open > .sub-menu,
     930.hvn-theme-nav-menu .menu-item-has-children.focus > .sub-menu,
     931.hvn-theme-nav-menu .menu-item-has-children:focus-within > .sub-menu {
     932        opacity: 1;
     933        visibility: visible;
     934        transform: translateY(0);
     935        pointer-events: auto;
     936}
     937
     938.hvn-theme-nav-menu .menu-item-has-children > a::after {
     939        transition: transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
     940}
     941
     942.hvn-theme-nav-menu .menu-item-has-children:hover > a::after,
     943.hvn-theme-nav-menu .menu-item-has-children.is-dropdown-open > a::after,
     944.hvn-theme-nav-menu .menu-item-has-children.focus > a::after,
     945.hvn-theme-nav-menu .menu-item-has-children:focus-within > a::after {
     946        transform: translateY(-50%) rotate(180deg);
     947}
     948
    859949.hvn-theme-nav-menu .sub-menu a {
    860         border-left: 2px solid transparent;
    861         transition: border-color 0.2s ease, color 0.2s ease, background-color 0.2s ease;
     950        display: flex;
     951        align-items: center;
     952        min-height: 42px;
     953        padding: 0.5rem 0.75rem !important;
     954        font-size: 0.875rem;
     955        font-weight: 500;
     956        line-height: 1.35;
     957        color: var(--hvn-theme-text-secondary, #50575e);
     958        border-radius: 8px;
     959        border-left: none;
     960        transition:
     961                background-color 0.18s ease,
     962                color 0.18s ease;
    862963}
    863964
    864965.hvn-theme-nav-menu .sub-menu a:hover,
    865966.hvn-theme-nav-menu .sub-menu a:focus-visible {
    866         border-left-color: var(--hvn-theme-brand-primary, #6c60fe);
     967        background-color: rgba(108, 96, 254, 0.08);
     968        color: var(--hvn-theme-brand-primary, #6c60fe);
     969}
     970
     971.hvn-theme-nav-menu .sub-menu .menu-item-has-children > .sub-menu {
     972        top: -0.5rem;
     973        left: 100%;
     974        margin: 0;
     975        padding: 0.5rem;
     976}
     977
     978.hvn-theme-nav-menu .sub-menu .menu-item-has-children > .sub-menu::before {
     979        top: 0;
     980        left: -14px;
     981        width: 14px;
     982        height: 100%;
     983        right: auto;
     984}
     985
     986.hvn-theme-nav-menu .sub-menu .current-menu-item > a,
     987.hvn-theme-nav-menu .sub-menu .current-page-ancestor > a {
     988        color: var(--hvn-theme-brand-primary, #6c60fe);
     989        font-weight: 600;
     990        background-color: rgba(108, 96, 254, 0.06);
     991}
     992
     993/* Site logo — shared Customizer source */
     994.hvn-theme-custom-logo img,
     995img.custom-logo {
     996        display: block;
     997        max-height: 52px;
     998        width: auto;
     999        height: auto;
     1000}
     1001
     1002.hvn-theme-sticky-header .hvn-theme-custom-logo img,
     1003.hvn-theme-sticky-header img.custom-logo {
     1004        max-height: 48px;
     1005}
     1006
     1007/* Mobile drawer branding (same Site Identity logo) */
     1008.hvn-theme-mobile-menu-branding {
     1009        flex: 1;
     1010        min-width: 0;
     1011        padding-right: 0.75rem;
     1012}
     1013
     1014.hvn-theme-mobile-menu-branding .hvn-theme-logo {
     1015        margin: 0;
     1016}
     1017
     1018.hvn-theme-mobile-menu-branding .hvn-theme-site-description {
     1019        display: none;
     1020}
     1021
     1022.hvn-theme-mobile-menu-branding .hvn-theme-custom-logo img,
     1023.hvn-theme-mobile-menu-branding img.custom-logo {
     1024        max-height: 42px;
     1025}
     1026
     1027.hvn-theme-mobile-menu-branding .hvn-theme-site-title {
     1028        margin: 0;
     1029        font-size: 1.125rem;
     1030        line-height: 1.3;
     1031}
     1032
     1033.hvn-theme-mobile-menu-header {
     1034        gap: 0.75rem;
     1035        align-items: center;
     1036}
     1037
     1038/* ============================================
     1039   MOBILE MENU - FIXED: No left-to-right jump
     1040   ============================================ */
     1041
     1042/* Mobile Menu Panel */
     1043.hvn-theme-mobile-menu {
     1044        position: fixed;
     1045        top: 0;
     1046        left: -100%;
     1047        width: 85%;
     1048        max-width: 320px;
     1049        height: 100vh;
     1050        background: var(--hvn-theme-color-white);
     1051        box-shadow: 5px 0 25px rgba(0, 0, 0, 0.15);
     1052        z-index: 10000;
     1053        display: flex;
     1054        flex-direction: column;
     1055        overflow-y: auto;
     1056        transition: left 0.35s cubic-bezier(0.4, 0, 0.2, 1);
     1057        will-change: transform, left;
     1058}
     1059
     1060.hvn-theme-mobile-menu.active {
     1061        left: 0;
     1062}
     1063
     1064.hvn-theme-mobile-menu-content {
     1065        flex: 1;
     1066        padding: 0;
     1067        overflow-y: auto;
     1068        -webkit-overflow-scrolling: touch;
     1069}
     1070
     1071/* Mobile Navigation Menu */
     1072.hvn-theme-mobile-nav-menu {
     1073        list-style: none;
     1074        margin: 0;
     1075        padding: 0;
     1076}
     1077
     1078.hvn-theme-mobile-nav-menu > li {
     1079        border-bottom: 1px solid var(--hvn-theme-border-color-light);
     1080        position: relative;
     1081}
     1082
     1083.hvn-theme-mobile-nav-menu > li:last-child {
     1084        border-bottom: none;
     1085}
     1086
     1087/* ============================================
     1088   MOBILE MENU LINKS - NO JUMPING
     1089   ============================================ */
     1090
     1091.hvn-theme-mobile-nav-menu > li > a {
     1092        display: flex;
     1093        align-items: center;
     1094        min-height: 48px;
     1095        padding: 0.75rem 1.25rem;
     1096        font-size: 1rem;
     1097        font-weight: 500;
     1098        line-height: 1.4;
     1099        color: var(--hvn-theme-text-primary);
     1100        text-decoration: none;
     1101        position: relative;
     1102        box-sizing: border-box;
     1103        width: 100%;
     1104       
     1105        /* Smooth transitions - only color and background */
     1106        transition:
     1107                color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
     1108                background-color 0.3s cubic-bezier(0.4, 0, 0.2, 1);
     1109       
     1110        /* Left border indicator - no shift */
     1111        border-left: 3px solid transparent;
     1112}
     1113
     1114/* FIXED: Override theme.css padding-left change on hover */
     1115.hvn-theme-mobile-nav-menu > li > a:hover,
     1116.hvn-theme-mobile-nav-menu > li > a:focus-visible {
     1117        background-color: rgba(108, 96, 254, 0.08);
     1118        color: var(--hvn-theme-brand-primary);
     1119       
     1120        /* CRITICAL: NO padding-left change - prevents jumping */
     1121        padding-left: 1.25rem !important;
     1122       
     1123        /* NO transform - prevents jumping */
     1124        transform: none !important;
     1125       
     1126        /* Left border indicator */
     1127        border-left-color: var(--hvn-theme-brand-primary);
     1128}
     1129
     1130/* Current menu item */
     1131.hvn-theme-mobile-nav-menu .current-menu-item > a,
     1132.hvn-theme-mobile-nav-menu .current-page-ancestor > a {
     1133        color: var(--hvn-theme-brand-primary);
     1134        font-weight: 600;
     1135        background-color: rgba(108, 96, 254, 0.08);
     1136        border-left-color: var(--hvn-theme-brand-primary);
     1137}
     1138
     1139/* ============================================
     1140   MOBILE DROPDOWN TOGGLE
     1141   ============================================ */
     1142
     1143.hvn-theme-mobile-nav-menu .menu-item-has-children {
     1144        position: relative;
     1145}
     1146
     1147.hvn-theme-mobile-nav-menu .menu-item-has-children > a {
     1148        padding-right: 3.75rem;
     1149}
     1150
     1151.hvn-theme-mobile-dropdown-toggle {
     1152        position: absolute;
     1153        top: 2px;
     1154        right: 0.75rem;
     1155        display: inline-flex;
     1156        align-items: center;
     1157        justify-content: center;
     1158        width: 44px;
     1159        height: 44px;
     1160        border: 1px solid var(--hvn-theme-border-color);
     1161        border-radius: 8px;
     1162        background: var(--hvn-theme-color-gray-100);
     1163        color: var(--hvn-theme-text-primary);
     1164        font-size: 0;
     1165        line-height: 0;
     1166        cursor: pointer;
     1167        z-index: 2;
     1168        transform: none;
     1169        transition:
     1170                background-color 0.2s ease,
     1171                color 0.2s ease,
     1172                border-color 0.2s ease;
     1173}
     1174
     1175.hvn-theme-mobile-dropdown-toggle::before {
     1176        content: "";
     1177        display: block;
     1178        width: 8px;
     1179        height: 8px;
     1180        border-right: 2px solid currentColor;
     1181        border-bottom: 2px solid currentColor;
     1182        transform: rotate(45deg);
     1183        transform-origin: center center;
     1184        transition: transform 0.24s cubic-bezier(0.22, 1, 0.36, 1);
     1185}
     1186
     1187.hvn-theme-mobile-dropdown-toggle:hover,
     1188.hvn-theme-mobile-dropdown-toggle:focus-visible {
     1189        background: var(--hvn-theme-brand-primary);
     1190        color: #fff;
     1191        border-color: var(--hvn-theme-brand-primary);
     1192        outline: 2px solid var(--hvn-theme-brand-primary);
     1193        outline-offset: 2px;
     1194}
     1195
     1196.hvn-theme-mobile-dropdown-toggle.active {
     1197        background: rgba(108, 96, 254, 0.12);
     1198        color: var(--hvn-theme-brand-primary);
     1199        border-color: var(--hvn-theme-brand-primary);
     1200}
     1201
     1202.hvn-theme-mobile-dropdown-toggle.active::before {
     1203        transform: rotate(-135deg);
     1204}
     1205
     1206/* ============================================
     1207   SUBMENU - max-height expand (no layout shift)
     1208   ============================================ */
     1209
     1210.hvn-theme-mobile-nav-menu .sub-menu {
     1211        display: block;
     1212        max-height: 0;
     1213        opacity: 0;
     1214        visibility: hidden;
     1215        margin: 0;
     1216        padding: 0;
     1217        background: var(--hvn-theme-color-gray-50, #f9fafb);
     1218        border-top: 0 solid var(--hvn-theme-border-color);
     1219        overflow: hidden;
     1220        transition:
     1221                max-height 0.28s cubic-bezier(0.22, 1, 0.36, 1),
     1222                opacity 0.22s cubic-bezier(0.22, 1, 0.36, 1),
     1223                visibility 0.22s,
     1224                padding 0.28s cubic-bezier(0.22, 1, 0.36, 1);
     1225}
     1226
     1227.hvn-theme-mobile-nav-menu .sub-menu.toggled {
     1228        max-height: 1200px;
     1229        opacity: 1;
     1230        visibility: visible;
     1231        padding: 0.125rem 0 0.625rem;
     1232        border-top-width: 1px;
     1233}
     1234
     1235/* ============================================
     1236   SUBMENU LINKS - NO JUMPING
     1237   ============================================ */
     1238
     1239.hvn-theme-mobile-nav-menu .sub-menu a {
     1240        display: flex;
     1241        align-items: center;
     1242        min-height: 44px;
     1243        padding: 0.625rem 1.25rem 0.625rem 1.75rem !important;
     1244        font-size: 0.9375rem;
     1245        font-weight: 400;
     1246        line-height: 1.4;
     1247        color: var(--hvn-theme-text-secondary, #50575e);
     1248        text-decoration: none;
     1249        box-sizing: border-box;
     1250        width: 100%;
     1251        border-left: none;
     1252        transition:
     1253                color 0.2s ease,
     1254                background-color 0.2s ease;
     1255}
     1256
     1257.hvn-theme-mobile-nav-menu .sub-menu a:hover,
     1258.hvn-theme-mobile-nav-menu .sub-menu a:focus-visible {
     1259        background-color: rgba(108, 96, 254, 0.06);
     1260        color: var(--hvn-theme-brand-primary);
     1261}
     1262
     1263.hvn-theme-mobile-nav-menu .sub-menu .current-menu-item > a {
     1264        color: var(--hvn-theme-brand-primary);
     1265        font-weight: 600;
     1266        background-color: rgba(108, 96, 254, 0.08);
     1267        border-left-color: var(--hvn-theme-brand-primary);
     1268}
     1269
     1270/* ============================================
     1271   MOBILE OVERLAY
     1272   ============================================ */
     1273
     1274.hvn-theme-mobile-overlay {
     1275        position: fixed;
     1276        top: 0;
     1277        left: 0;
     1278        width: 100%;
     1279        height: 100%;
     1280        background: rgba(0, 0, 0, 0.5);
     1281        z-index: 9999;
     1282        opacity: 0;
     1283        visibility: hidden;
     1284        transition: opacity 0.35s cubic-bezier(0.4, 0, 0.2, 1),
     1285                    visibility 0.35s cubic-bezier(0.4, 0, 0.2, 1);
     1286}
     1287
     1288.hvn-theme-mobile-overlay.active {
     1289        opacity: 1;
     1290        visibility: visible;
     1291}
     1292
     1293/* Mobile Menu CTA Section */
     1294.hvn-theme-mobile-menu-cta {
     1295        padding: 1rem 1.25rem 1.5rem;
     1296        border-top: 1px solid var(--hvn-theme-border-color);
     1297        margin-top: auto;
     1298        flex-shrink: 0;
     1299}
     1300
     1301.hvn-theme-mobile-header-cta {
     1302        width: 100%;
     1303        justify-content: center;
     1304        transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
     1305                    box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
     1306}
     1307
     1308.hvn-theme-mobile-header-cta:hover {
     1309        transform: translateY(-2px);
     1310        box-shadow: 0 4px 16px rgba(108, 96, 254, 0.3);
     1311}
     1312
     1313/* ============================================
     1314   MOBILE CLOSE BUTTON
     1315   ============================================ */
     1316
     1317.hvn-theme-mobile-menu-close {
     1318        flex-shrink: 0;
     1319        width: 44px;
     1320        height: 44px;
     1321        display: inline-flex;
     1322        align-items: center;
     1323        justify-content: center;
     1324        background: none;
     1325        border: none;
     1326        font-size: 2rem;
     1327        line-height: 1;
     1328        cursor: pointer;
     1329        color: var(--hvn-theme-text-muted);
     1330        padding: 5px 10px;
     1331        transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
     1332                    transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
     1333}
     1334
     1335.hvn-theme-mobile-menu-close:hover {
     1336        color: var(--hvn-theme-brand-error);
     1337        transform: scale(1.1);
     1338}
     1339
     1340.hvn-theme-mobile-menu-close:focus-visible {
     1341        outline: 2px solid var(--hvn-theme-brand-primary);
     1342        outline-offset: 2px;
     1343}
     1344
     1345/* ============================================
     1346   RESPONSIVE FIXES
     1347   ============================================ */
     1348
     1349@media (max-width: 480px) {
     1350        .hvn-theme-mobile-menu {
     1351                width: 100%;
     1352                max-width: none;
     1353                left: -100%;
     1354        }
     1355       
     1356        .hvn-theme-mobile-menu.active {
     1357                left: 0;
     1358        }
     1359       
     1360        .hvn-theme-mobile-menu-header {
     1361                padding: 12px 16px;
     1362        }
     1363       
     1364        .hvn-theme-mobile-nav-menu > li > a {
     1365                padding: 0.625rem 1rem;
     1366                min-height: 44px;
     1367                font-size: 0.9375rem;
     1368        }
     1369       
     1370        /* FIXED: Keep padding consistent on hover */
     1371        .hvn-theme-mobile-nav-menu > li > a:hover,
     1372        .hvn-theme-mobile-nav-menu > li > a:focus-visible {
     1373                padding-left: 1rem !important;
     1374        }
     1375       
     1376        .hvn-theme-mobile-nav-menu .sub-menu a {
     1377                padding: 0.5rem 1rem 0.5rem 1.5rem !important;
     1378                min-height: 40px;
     1379                font-size: 0.875rem;
     1380        }
     1381
     1382        .hvn-theme-mobile-nav-menu .sub-menu a:hover,
     1383        .hvn-theme-mobile-nav-menu .sub-menu a:focus-visible {
     1384                padding: 0.5rem 1rem 0.5rem 1.5rem !important;
     1385        }
     1386       
     1387        .hvn-theme-mobile-dropdown-toggle {
     1388                width: 38px;
     1389                height: 38px;
     1390                top: 3px;
     1391                right: 0.5rem;
     1392        }
     1393}
     1394
     1395@media (max-width: 375px) {
     1396        .hvn-theme-mobile-menu {
     1397                width: 100%;
     1398                max-width: none;
     1399        }
     1400}
     1401
     1402/* ============================================
     1403   REDUCED MOTION PREFERENCE
     1404   ============================================ */
     1405
     1406@media (prefers-reduced-motion: reduce) {
     1407        .hvn-theme-mobile-menu,
     1408        .hvn-theme-mobile-menu-close,
     1409        .hvn-theme-mobile-nav-menu > li > a,
     1410        .hvn-theme-mobile-nav-menu .sub-menu a,
     1411        .hvn-theme-mobile-dropdown-toggle,
     1412        .hvn-theme-mobile-overlay,
     1413        .hvn-theme-mobile-header-cta {
     1414                transition: none !important;
     1415        }
     1416       
     1417        .hvn-theme-mobile-nav-menu > li > a:hover,
     1418        .hvn-theme-mobile-nav-menu > li > a:focus-visible {
     1419                padding-left: 1.25rem !important;
     1420                transform: none !important;
     1421        }
     1422       
     1423        .hvn-theme-mobile-nav-menu .sub-menu a:hover,
     1424        .hvn-theme-mobile-nav-menu .sub-menu a:focus-visible {
     1425                padding: 0.625rem 1.25rem 0.625rem 1.75rem !important;
     1426                transform: none !important;
     1427        }
     1428       
     1429        .hvn-theme-mobile-menu-close:hover {
     1430                transform: none !important;
     1431        }
     1432       
     1433        .hvn-theme-mobile-header-cta:hover {
     1434                transform: none !important;
     1435        }
     1436
     1437        .hvn-theme-mobile-nav-menu .sub-menu {
     1438                transition: none !important;
     1439        }
    8671440}
    8681441
    … …  
    9631536
    9641537        .hvn-theme-nav-menu > li > a:hover::before,
     1538        .hvn-theme-nav-menu > li > a:focus-visible::before,
    9651539        .hvn-theme-nav-menu > li.current-menu-item > a::before {
    9661540                opacity: 1;
     1541        }
     1542
     1543        .hvn-theme-nav-menu .sub-menu,
     1544        .hvn-theme-nav-menu .menu-item-has-children > a::after {
     1545                transition: none;
     1546        }
     1547
     1548        .hvn-theme-nav-menu .sub-menu {
     1549                transform: none;
    9671550        }
    9681551}
    … …  
    9711554   Responsive audit — targeted fixes (320px–1440px)
    9721555   ========================================================================== */
     1556
     1557@media (max-width: 320px) {
     1558        .hvn-theme-header-row {
     1559                gap: 0.35rem;
     1560        }
     1561
     1562        .hvn-theme-custom-logo img,
     1563        img.custom-logo {
     1564                max-height: 36px;
     1565        }
     1566
     1567        .hvn-theme-mobile-menu-branding .hvn-theme-custom-logo img,
     1568        .hvn-theme-mobile-menu-branding img.custom-logo {
     1569                max-height: 36px;
     1570        }
     1571
     1572        .hvn-theme-mobile-nav-menu > li > a {
     1573                padding-left: 1rem;
     1574                padding-right: 1rem;
     1575        }
     1576}
    9731577
    9741578@media (max-width: 480px) {
    … …  
    10071611                padding: 1rem 0.75rem 1.25rem;
    10081612        }
    1009 
    1010         .hvn-theme-mobile-menu {
    1011                 width: 100%;
    1012                 max-width: none;
    1013         }
    10141613}
    10151614
    10161615@media (max-width: 767px) {
    10171616        .hvn-theme-mobile-nav-menu .menu-item-has-children > a {
    1018                 padding-right: 3rem;
     1617                padding-right: 3.5rem;
    10191618        }
    10201619
    … …  
    10261625                text-align: left;
    10271626        }
     1627
     1628        .hvn-realty-home .hvnly-property-card,
     1629        .hvn-realty-home .hvnly-agent-card,
     1630        .hvn-realty-home .hvnly-agency-card {
     1631                min-width: 0;
     1632        }
     1633
     1634        .hvn-realty-section__body {
     1635                overflow-x: clip;
     1636        }
    10281637}
    10291638
    10301639@media (min-width: 768px) and (max-width: 1024px) {
     1640        .hvn-theme-header-row {
     1641                gap: 1rem;
     1642        }
     1643
     1644        .hvn-theme-nav-menu > li > a {
     1645                padding-left: 0.75rem;
     1646                padding-right: 0.75rem;
     1647        }
     1648
    10311649        .hvn-theme-footer-widgets.hvn-cols-3,
    10321650        .hvn-theme-footer-widgets.hvn-cols-4 {
    10331651                grid-template-columns: repeat(2, minmax(0, 1fr));
    10341652        }
    1035 }
    1036 
     1653
     1654        .hvn-realty-dept-tabs__nav {
     1655                overflow-x: auto;
     1656                -webkit-overflow-scrolling: touch;
     1657                scrollbar-width: thin;
     1658        }
     1659}
     1660
     1661@media (min-width: 1025px) and (max-width: 1440px) {
     1662        .hvn-theme-nav-menu > li > a {
     1663                padding-left: 0.875rem;
     1664                padding-right: 0.875rem;
     1665        }
     1666}
  • havenlytics-realty/1.15.5/assets/css/theme.css

    r334339 r334420  
    19411941.hvn-theme-mobile-nav-menu a:focus {
    19421942    color: var(--hvn-theme-brand-primary);
    1943     padding-left: 5px;
     1943    padding-left: 5px;transition: all 0.4s ease;
    19441944}
    19451945
  • havenlytics-realty/1.15.5/assets/js/customizer.js

    r334339 r334420  
    2626                if ( node ) {
    2727                        node.textContent = value;
     28                }
     29        }
     30
     31        function resolveThemeLink( value, fallback ) {
     32                var link = ( value || '' ).trim();
     33                var homeUrl = ( config.homeUrl || '/' ).replace( /\/$/, '' );
     34
     35                if ( ! link ) {
     36                        return fallback || homeUrl + '/';
     37                }
     38
     39                if ( /^https?:\/\//i.test( link ) ) {
     40                        return link;
     41                }
     42
     43                if ( link.charAt( 0 ) === '/' ) {
     44                        return homeUrl + link;
     45                }
     46
     47                return homeUrl + '/' + link.replace( /^\/+|\/+$/g, '' ) + '/';
     48        }
     49
     50        function setLink( selector, href, text ) {
     51                var node = document.querySelector( selector );
     52                if ( ! node ) {
     53                        return;
     54                }
     55                if ( typeof href === 'string' && href ) {
     56                        node.setAttribute( 'href', href );
     57                }
     58                if ( typeof text === 'string' ) {
     59                        node.textContent = text;
    2860                }
    2961        }
    … …  
    149181        }
    150182
     183        function bindHomeDepartmentButton() {
     184                if ( ! wp.customize( 'hvn_realty_home_department_button_text' ) || ! wp.customize( 'hvn_realty_home_department_button_url' ) ) {
     185                        return;
     186                }
     187
     188                var fallback = config.propertySearchUrl || config.homeUrl || '/';
     189
     190                function refreshDepartmentButton() {
     191                        var text = wp.customize( 'hvn_realty_home_department_button_text' ).get();
     192                        var urlValue = wp.customize( 'hvn_realty_home_department_button_url' ).get();
     193                        setLink( '#hvn-realty-dept-view-all', resolveThemeLink( urlValue, fallback ), text );
     194                }
     195
     196                wp.customize( 'hvn_realty_home_department_button_text', function ( value ) {
     197                        value.bind( refreshDepartmentButton );
     198                } );
     199
     200                wp.customize( 'hvn_realty_home_department_button_url', function ( value ) {
     201                        value.bind( refreshDepartmentButton );
     202                } );
     203        }
     204
     205        function bindCustomLogoPreview() {
     206                if ( ! wp.customize( 'custom_logo' ) ) {
     207                        return;
     208                }
     209
     210                wp.customize( 'custom_logo', function ( value ) {
     211                        value.bind( function ( to ) {
     212                                var logoId = parseInt( to, 10 );
     213
     214                                if ( ! logoId ) {
     215                                        if ( wp.customize.preview ) {
     216                                                wp.customize.preview.send( 'refresh' );
     217                                        }
     218                                        return;
     219                                }
     220
     221                                var attachment = wp.media.attachment( logoId );
     222                                attachment.fetch().then( function () {
     223                                        var url = attachment.get( 'url' );
     224                                        if ( ! url ) {
     225                                                return;
     226                                        }
     227
     228                                        var alt = attachment.get( 'alt' ) || '';
     229                                        var width = attachment.get( 'width' );
     230                                        var height = attachment.get( 'height' );
     231
     232                                        document.querySelectorAll( '.hvn-theme-custom-logo img, img.custom-logo' ).forEach( function ( img ) {
     233                                                img.src = url;
     234                                                img.alt = alt;
     235                                                if ( width ) {
     236                                                        img.width = width;
     237                                                }
     238                                                if ( height ) {
     239                                                        img.height = height;
     240                                                }
     241                                        } );
     242                                } );
     243                        } );
     244                } );
     245        }
     246
    151247        function bindSiteIconPreview() {
    152248                if ( ! wp.customize( 'site_icon' ) ) {
    … …  
    356452
    357453                bindHomeTextFields();
     454                bindHomeDepartmentButton();
     455                bindCustomLogoPreview();
    358456                bindSiteIconPreview();
    359457
    … …  
    439537                                        return;
    440538                                }
     539                                height = Math.max( 40, Math.min( 100, height ) );
    441540                                setStyle(
    442541                                        'hvn-realty-hero-height',
    … …  
    444543                                );
    445544                                if ( typeof window.hvnRealtyInvalidateHomeMap === 'function' ) {
    446                                         window.hvnRealtyInvalidateHomeMap();
     545                                        window.setTimeout( window.hvnRealtyInvalidateHomeMap, 50 );
     546                                        window.setTimeout( window.hvnRealtyInvalidateHomeMap, 350 );
    447547                                }
    448548                        } );
    … …  
    455555                                        return;
    456556                                }
     557                                height = Math.max( 40, Math.min( 100, height ) );
    457558                                setStyle(
    458559                                        'hvn-realty-hero-height-mobile',
    … …  
    460561                                );
    461562                                if ( typeof window.hvnRealtyInvalidateHomeMap === 'function' ) {
    462                                         window.hvnRealtyInvalidateHomeMap();
     563                                        window.setTimeout( window.hvnRealtyInvalidateHomeMap, 50 );
     564                                        window.setTimeout( window.hvnRealtyInvalidateHomeMap, 350 );
    463565                                }
    464566                        } );
  • havenlytics-realty/1.15.5/assets/js/havenlytics-home.js

    r334339 r334420  
    273273        }
    274274
     275        function getHomeMapDepartmentSlugs() {
     276                var $shell = $( '#hvn-realty-home-map-shell' );
     277                if ( ! $shell.length ) {
     278                        return [];
     279                }
     280
     281                var raw = $shell.attr( 'data-map-departments' );
     282                if ( raw ) {
     283                        try {
     284                                var parsed = JSON.parse( raw );
     285                                if ( Array.isArray( parsed ) && parsed.length ) {
     286                                        return parsed.map( String );
     287                                }
     288                        } catch ( error ) {
     289                                // Fall through to DOM lookup.
     290                        }
     291                }
     292
     293                var slugs = [];
     294                $shell.find( '.hvn-realty-home-map-filters input[name="hvnly_prop_depts[]"]:checked' ).each( function () {
     295                        slugs.push( String( $( this ).val() ) );
     296                } );
     297
     298                if ( ! slugs.length ) {
     299                        var single = $shell.find( '.hvn-realty-home-map-filters input[name="department"]' ).val();
     300                        if ( single ) {
     301                                slugs.push( String( single ) );
     302                        }
     303                }
     304
     305                return slugs;
     306        }
     307
     308        /**
     309         * Merge Customizer department filters into map AJAX requests.
     310         *
     311         * The plugin map loader prefers havenlyticsAJAX.getFormData(), which only
     312         * reads the property-search form and #hvnly-filter-sidebar — not the theme
     313         * hero map filter inputs.
     314         */
     315        function ensureHomeMapDepartmentFilters() {
     316                var slugs = getHomeMapDepartmentSlugs();
     317                if ( ! slugs.length ) {
     318                        return;
     319                }
     320
     321                if ( window.havenlyticsAJAX && typeof window.havenlyticsAJAX.getFormData === 'function' ) {
     322                        if ( ! window.havenlyticsAJAX._hvnRealtyHomeMapFormDataPatched ) {
     323                                var originalGetFormData = window.havenlyticsAJAX.getFormData.bind( window.havenlyticsAJAX );
     324
     325                                window.havenlyticsAJAX.getFormData = function () {
     326                                        var data = originalGetFormData();
     327
     328                                        if ( ! $( '#hvn-realty-home-map-shell' ).length ) {
     329                                                return data;
     330                                        }
     331
     332                                        var homeSlugs = window.havenlyticsAJAX._hvnRealtyHomeMapDepartments || getHomeMapDepartmentSlugs();
     333                                        if ( ! homeSlugs.length ) {
     334                                                return data;
     335                                        }
     336
     337                                        data.hvnly_prop_depts = homeSlugs.slice();
     338                                        if ( homeSlugs.length === 1 ) {
     339                                                data.department = homeSlugs[ 0 ];
     340                                        } else {
     341                                                delete data.department;
     342                                        }
     343
     344                                        return data;
     345                                };
     346
     347                                window.havenlyticsAJAX._hvnRealtyHomeMapFormDataPatched = true;
     348                        }
     349
     350                        window.havenlyticsAJAX._hvnRealtyHomeMapDepartments = slugs;
     351                }
     352        }
     353
    275354        function getMapInstance() {
    276355                return window.HavenlyticsPropertyMap || window.HvnlyPropertyMap || null;
    … …  
    305384                if ( ! mapInstance || typeof mapInstance.loadPropertyMap !== 'function' ) {
    306385                        return false;
     386                }
     387
     388                var slugs = getHomeMapDepartmentSlugs();
     389                if ( slugs.length ) {
     390                        ensureHomeMapDepartmentFilters();
     391
     392                        if ( ! window.havenlyticsAJAX || typeof window.havenlyticsAJAX.getFormData !== 'function' ) {
     393                                return false;
     394                        }
    307395                }
    308396
  • havenlytics-realty/1.15.5/assets/js/theme.js

    r334339 r334420  
    22 * Havenlytics Realty theme interactions (vanilla JS).
    33 */
    4 ( function () {
     4(function () {
    55        'use strict';
    66
    77        var keyboardTimeout;
    88
    9         function qs( selector, context ) {
    10                 return ( context || document ).querySelector( selector );
    11         }
    12 
    13         function qsa( selector, context ) {
    14                 return Array.prototype.slice.call( ( context || document ).querySelectorAll( selector ) );
    15         }
    16 
    17         function on( el, event, handler ) {
    18                 if ( el ) {
    19                         el.addEventListener( event, handler );
    20                 }
    21         }
    22 
    23         function trapFocus( container, event ) {
    24                 if ( event.key !== 'Tab' || ! container ) {
    25                         return;
    26                 }
    27 
    28                 var focusable = qsa( 'a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])', container )
    29                         .filter( function ( node ) {
    30                                 return ! node.disabled && node.offsetParent !== null;
    31                         } );
    32 
    33                 if ( ! focusable.length ) {
     9        function qs(selector, context) {
     10                return (context || document).querySelector(selector);
     11        }
     12
     13        function qsa(selector, context) {
     14                return Array.prototype.slice.call((context || document).querySelectorAll(selector));
     15        }
     16
     17        function on(el, event, handler) {
     18                if (el) {
     19                        el.addEventListener(event, handler);
     20                }
     21        }
     22
     23        function trapFocus(container, event) {
     24                if (event.key !== 'Tab' || !container) {
     25                        return;
     26                }
     27
     28                var focusable = qsa('a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])', container)
     29                        .filter(function (node) {
     30                                return !node.disabled && node.offsetParent !== null;
     31                        });
     32
     33                if (!focusable.length) {
    3434                        return;
    3535                }
    3636
    3737                var first = focusable[0];
    38                 var last = focusable[ focusable.length - 1 ];
    39 
    40                 if ( event.shiftKey && document.activeElement === first ) {
     38                var last = focusable[focusable.length - 1];
     39
     40                if (event.shiftKey && document.activeElement === first) {
    4141                        event.preventDefault();
    4242                        last.focus();
    43                 } else if ( ! event.shiftKey && document.activeElement === last ) {
     43                } else if (!event.shiftKey && document.activeElement === last) {
    4444                        event.preventDefault();
    4545                        first.focus();
    … …  
    4747        }
    4848
    49         function handleActivation( event ) {
    50                 if ( event.key === 'Enter' || event.key === ' ' ) {
     49        function handleActivation(event) {
     50                if (event.key === 'Enter' || event.key === ' ') {
    5151                        event.preventDefault();
    5252                        event.currentTarget.click();
    … …  
    5454        }
    5555
    56         function handleFirstTab( event ) {
    57                 if ( event.key === 'Tab' ) {
    58                         document.body.classList.remove( 'mouse-user' );
    59                         document.body.classList.add( 'keyboard-user' );
    60                         clearTimeout( keyboardTimeout );
    61                         keyboardTimeout = setTimeout( function () {
    62                                 document.body.classList.remove( 'keyboard-user' );
    63                                 document.body.classList.add( 'mouse-user' );
    64                         }, 1000 );
     56        function handleFirstTab(event) {
     57                if (event.key === 'Tab') {
     58                        document.body.classList.remove('mouse-user');
     59                        document.body.classList.add('keyboard-user');
     60                        clearTimeout(keyboardTimeout);
     61                        keyboardTimeout = setTimeout(function () {
     62                                document.body.classList.remove('keyboard-user');
     63                                document.body.classList.add('mouse-user');
     64                        }, 1000);
    6565                }
    6666        }
    6767
    6868        function initKeyboardMode() {
    69                 window.addEventListener( 'keydown', handleFirstTab );
    70                 document.body.classList.add( 'mouse-user' );
     69                window.addEventListener('keydown', handleFirstTab);
     70                document.body.classList.add('mouse-user');
    7171        }
    7272
    7373        function initDesktopNavigation() {
    74                 var desktopMenu = qs( '#site-navigation' );
    75                 if ( ! desktopMenu ) {
    76                         return;
    77                 }
    78 
    79                 var subMenus = qsa( '.menu-item-has-children', desktopMenu );
    80 
    81                 subMenus.forEach( function ( item ) {
    82                         item.setAttribute( 'aria-haspopup', 'true' );
    83 
    84                         var parentLink = qs( 'a', item );
     74                var desktopMenu = qs('#site-navigation');
     75                if (!desktopMenu) {
     76                        return;
     77                }
     78
     79                var subMenus = qsa('.menu-item-has-children', desktopMenu);
     80                var closeTimers = new WeakMap();
     81
     82                function setDropdownExpanded( item, expanded ) {
     83                        var parentLink = qs( ':scope > a', item );
     84
    8585                        if ( parentLink ) {
    86                                 on( parentLink, 'keydown', function ( event ) {
    87                                         if ( event.key !== 'ArrowDown' && event.key !== 'ArrowUp' ) {
     86                                parentLink.setAttribute( 'aria-expanded', expanded ? 'true' : 'false' );
     87                        }
     88
     89                        item.classList.toggle( 'is-dropdown-open', !! expanded );
     90                }
     91
     92                subMenus.forEach(function (item) {
     93                        item.setAttribute('aria-haspopup', 'true');
     94
     95                        var parentLink = qs(':scope > a', item);
     96                        if (parentLink) {
     97                                parentLink.setAttribute('aria-expanded', 'false');
     98
     99                                on(item, 'mouseenter', function () {
     100                                        var timer = closeTimers.get(item);
     101                                        if (timer) {
     102                                                clearTimeout(timer);
     103                                                closeTimers.delete(item);
     104                                        }
     105                                        setDropdownExpanded(item, true);
     106                                });
     107
     108                                on(item, 'mouseleave', function () {
     109                                        var timer = setTimeout(function () {
     110                                                if (!item.contains(document.activeElement)) {
     111                                                        setDropdownExpanded(item, false);
     112                                                }
     113                                        }, 160);
     114                                        closeTimers.set(item, timer);
     115                                });
     116
     117                                on(parentLink, 'keydown', function (event) {
     118                                        if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp') {
     119                                                if (event.key === 'Escape') {
     120                                                        setDropdownExpanded(item, false);
     121                                                        item.classList.remove('focus');
     122                                                }
    88123                                                return;
    89124                                        }
    90125                                        event.preventDefault();
    91                                         var subMenu = qs( '.sub-menu', item );
    92                                         if ( subMenu && subMenu.children.length ) {
    93                                                 item.classList.add( 'focus' );
    94                                                 var firstLink = qs( 'a', subMenu );
    95                                                 if ( firstLink ) {
     126                                        var subMenu = qs('.sub-menu', item);
     127                                        if (subMenu && subMenu.children.length) {
     128                                                item.classList.add('focus');
     129                                                setDropdownExpanded(item, true);
     130                                                var firstLink = qs('a', subMenu);
     131                                                if (firstLink) {
    96132                                                        firstLink.focus();
    97133                                                }
    98134                                        }
    99                                 } );
    100                         }
    101                 } );
    102 
    103                 qsa( '.sub-menu a', desktopMenu ).forEach( function ( link ) {
    104                         on( link, 'keydown', function ( event ) {
    105                                 if ( event.key === 'ArrowLeft' ) {
    106                                         var parent = link.closest( '.sub-menu' );
    107                                         var parentItem = parent ? parent.closest( '.menu-item-has-children' ) : null;
    108                                         if ( parentItem ) {
    109                                                 var parentLink = qs( 'a', parentItem );
    110                                                 parentItem.classList.remove( 'focus' );
    111                                                 if ( parentLink ) {
     135                                });
     136                        }
     137                });
     138
     139                qsa('.sub-menu a', desktopMenu).forEach(function (link) {
     140                        on(link, 'keydown', function (event) {
     141                                if (event.key === 'ArrowLeft') {
     142                                        var parent = link.closest('.sub-menu');
     143                                        var parentItem = parent ? parent.closest('.menu-item-has-children') : null;
     144                                        if (parentItem) {
     145                                                var parentLink = qs(':scope > a', parentItem);
     146                                                parentItem.classList.remove('focus');
     147                                                setDropdownExpanded(parentItem, false);
     148                                                if (parentLink) {
    112149                                                        parentLink.focus();
    113150                                                }
    … …  
    115152                                }
    116153
    117                                 if ( event.key === 'ArrowRight' ) {
    118                                         var currentItem = link.closest( '.menu-item-has-children' );
    119                                         if ( currentItem ) {
    120                                                 var childSubMenu = qs( '.sub-menu', currentItem );
    121                                                 if ( childSubMenu && childSubMenu.children.length ) {
    122                                                         currentItem.classList.add( 'focus' );
    123                                                         var firstChildLink = qs( 'a', childSubMenu );
    124                                                         if ( firstChildLink ) {
     154                                if (event.key === 'ArrowRight') {
     155                                        var currentItem = link.closest('.menu-item-has-children');
     156                                        if (currentItem) {
     157                                                var childSubMenu = qs('.sub-menu', currentItem);
     158                                                if (childSubMenu && childSubMenu.children.length) {
     159                                                        currentItem.classList.add('focus');
     160                                                        setDropdownExpanded(currentItem, true);
     161                                                        var firstChildLink = qs('a', childSubMenu);
     162                                                        if (firstChildLink) {
    125163                                                                firstChildLink.focus();
    126164                                                        }
    … …  
    129167                                }
    130168
    131                                 if ( event.key === 'Escape' ) {
    132                                         var closestMenu = link.closest( '.hvn-theme-nav-menu' );
    133                                         if ( closestMenu ) {
    134                                                 qsa( '.menu-item-has-children.focus', closestMenu ).forEach( function ( openItem ) {
    135                                                         openItem.classList.remove( 'focus' );
    136                                                 } );
    137                                                 var mainLinks = qsa( 'a', closestMenu );
    138                                                 if ( mainLinks[0] ) {
     169                                if (event.key === 'Escape') {
     170                                        var closestMenu = link.closest('.hvn-theme-nav-menu');
     171                                        if (closestMenu) {
     172                                                qsa('.menu-item-has-children.focus, .menu-item-has-children.is-dropdown-open', closestMenu).forEach(function (openItem) {
     173                                                        openItem.classList.remove('focus');
     174                                                        setDropdownExpanded(openItem, false);
     175                                                });
     176                                                var mainLinks = qsa('a', closestMenu);
     177                                                if (mainLinks[0]) {
    139178                                                        mainLinks[0].focus();
    140179                                                }
    141180                                        }
    142181                                }
    143                         } );
    144                 } );
    145 
    146                 qsa( 'a', desktopMenu ).forEach( function ( link ) {
    147                         on( link, 'focus', function () {
     182                        });
     183                });
     184
     185                qsa('a', desktopMenu).forEach(function (link) {
     186                        on(link, 'focus', function () {
    148187                                var node = link.parentNode;
    149                                 while ( node && node !== desktopMenu ) {
    150                                         if ( node.tagName === 'LI' && node.classList.contains( 'menu-item-has-children' ) ) {
    151                                                 node.classList.add( 'focus' );
     188                                while (node && node !== desktopMenu) {
     189                                        if (node.tagName === 'LI' && node.classList.contains('menu-item-has-children')) {
     190                                                node.classList.add('focus');
     191                                                setDropdownExpanded(node, true);
    152192                                        }
    153193                                        node = node.parentNode;
    154194                                }
    155                         } );
    156 
    157                         on( link, 'blur', function () {
     195                        });
     196
     197                        on(link, 'blur', function () {
    158198                                var parent = link.parentNode;
    159                                 setTimeout( function () {
    160                                         if ( parent && parent.classList && parent.classList.contains( 'menu-item-has-children' ) ) {
    161                                                 if ( ! parent.contains( document.activeElement ) ) {
    162                                                         parent.classList.remove( 'focus' );
    163                                                 }
    164                                         }
    165                                 }, 10 );
    166                         } );
    167                 } );
     199                                setTimeout(function () {
     200                                        if (parent && parent.classList && parent.classList.contains('menu-item-has-children')) {
     201                                                if (!parent.contains(document.activeElement)) {
     202                                                        parent.classList.remove('focus');
     203                                                        setDropdownExpanded(parent, false);
     204                                                }
     205                                        }
     206                                }, 10);
     207                        });
     208                });
    168209        }
    169210
    170211        function initMobileMenu() {
    171                 var menuToggle = qs( '.hvn-theme-menu-toggle' );
    172                 var mobileMenu = qs( '#hvn-mobile-menu' );
    173                 var mobileOverlay = qs( '.hvn-theme-mobile-overlay' );
    174                 var mobileClose = qs( '.hvn-theme-mobile-menu-close' );
    175 
    176                 if ( ! menuToggle || ! mobileMenu ) {
     212                var menuToggle = qs('.hvn-theme-menu-toggle');
     213                var mobileMenu = qs('#hvn-mobile-menu');
     214                var mobileOverlay = qs('.hvn-theme-mobile-overlay');
     215                var mobileClose = qs('.hvn-theme-mobile-menu-close');
     216
     217                if (!menuToggle || !mobileMenu) {
    177218                        return;
    178219                }
    179220
    180221                function openMobileMenu() {
    181                         mobileMenu.classList.add( 'active' );
    182                         mobileMenu.setAttribute( 'aria-hidden', 'false' );
    183                         if ( mobileOverlay ) {
    184                                 mobileOverlay.classList.add( 'active' );
    185                                 mobileOverlay.setAttribute( 'aria-hidden', 'false' );
    186                         }
    187                         menuToggle.classList.add( 'active' );
    188                         menuToggle.setAttribute( 'aria-expanded', 'true' );
     222                        mobileMenu.classList.add('active');
     223                        mobileMenu.setAttribute('aria-hidden', 'false');
     224                        if (mobileOverlay) {
     225                                mobileOverlay.classList.add('active');
     226                                mobileOverlay.setAttribute('aria-hidden', 'false');
     227                        }
     228                        menuToggle.classList.add('active');
     229                        menuToggle.setAttribute('aria-expanded', 'true');
    189230                        document.body.style.overflow = 'hidden';
    190                         setTimeout( function () {
    191                                 if ( mobileClose ) {
     231                        setTimeout(function () {
     232                                if (mobileClose) {
    192233                                        mobileClose.focus();
    193234                                }
    194                         }, 100 );
     235                        }, 100);
    195236                }
    196237
    197238                function closeMobileMenu() {
    198                         mobileMenu.classList.remove( 'active' );
    199                         mobileMenu.setAttribute( 'aria-hidden', 'true' );
    200                         if ( mobileOverlay ) {
    201                                 mobileOverlay.classList.remove( 'active' );
    202                                 mobileOverlay.setAttribute( 'aria-hidden', 'true' );
    203                         }
    204                         menuToggle.classList.remove( 'active' );
    205                         menuToggle.setAttribute( 'aria-expanded', 'false' );
     239                        mobileMenu.classList.remove('active');
     240                        mobileMenu.setAttribute('aria-hidden', 'true');
     241                        if (mobileOverlay) {
     242                                mobileOverlay.classList.remove('active');
     243                                mobileOverlay.setAttribute('aria-hidden', 'true');
     244                        }
     245                        menuToggle.classList.remove('active');
     246                        menuToggle.setAttribute('aria-expanded', 'false');
    206247                        document.body.style.overflow = '';
     248                        qsa('.hvn-theme-mobile-nav-menu .sub-menu.toggled').forEach(function (subMenu) {
     249                                subMenu.classList.remove('toggled');
     250                                subMenu.setAttribute('aria-hidden', 'true');
     251                        });
     252                        qsa('.hvn-theme-mobile-dropdown-toggle.active').forEach(function (btn) {
     253                                btn.classList.remove('active');
     254                                btn.setAttribute('aria-expanded', 'false');
     255                        });
     256                        qsa('.hvn-theme-mobile-nav-menu .menu-item-has-children > a[aria-expanded="true"]').forEach(function (link) {
     257                                link.setAttribute('aria-expanded', 'false');
     258                        });
    207259                        menuToggle.focus();
    208260                }
    209261
    210                 on( menuToggle, 'click', function ( event ) {
     262                on(menuToggle, 'click', function (event) {
    211263                        event.preventDefault();
    212                         if ( mobileMenu.classList.contains( 'active' ) ) {
     264                        if (mobileMenu.classList.contains('active')) {
    213265                                closeMobileMenu();
    214266                        } else {
    215267                                openMobileMenu();
    216268                        }
    217                 } );
    218                 on( menuToggle, 'keydown', handleActivation );
    219 
    220                 if ( mobileClose ) {
    221                         on( mobileClose, 'click', function ( event ) {
     269                });
     270                on(menuToggle, 'keydown', handleActivation);
     271
     272                if (mobileClose) {
     273                        on(mobileClose, 'click', function (event) {
    222274                                event.preventDefault();
    223275                                closeMobileMenu();
    224                         } );
    225                         on( mobileClose, 'keydown', handleActivation );
    226                 }
    227 
    228                 if ( mobileOverlay ) {
    229                         on( mobileOverlay, 'click', closeMobileMenu );
    230                 }
    231 
    232                 on( document, 'keydown', function ( event ) {
    233                         if ( event.key === 'Escape' && mobileMenu.classList.contains( 'active' ) ) {
     276                        });
     277                        on(mobileClose, 'keydown', handleActivation);
     278                }
     279
     280                if (mobileOverlay) {
     281                        on(mobileOverlay, 'click', closeMobileMenu);
     282                }
     283
     284                on(document, 'keydown', function (event) {
     285                        if (event.key === 'Escape' && mobileMenu.classList.contains('active')) {
    234286                                closeMobileMenu();
    235287                        }
    236                 } );
    237 
    238                 on( window, 'resize', function () {
    239                         if ( window.innerWidth > 991 ) {
     288                });
     289
     290                on(window, 'resize', function () {
     291                        if (window.innerWidth > 991) {
    240292                                closeMobileMenu();
    241293                        }
    242                 } );
    243 
    244                 on( mobileMenu, 'keydown', function ( event ) {
    245                         trapFocus( mobileMenu, event );
    246                 } );
    247 
    248                 qsa( '.hvn-theme-mobile-nav-menu .menu-item-has-children' ).forEach( function ( item ) {
    249                         var subMenu = qs( ':scope > .sub-menu', item );
    250                         if ( ! subMenu || qs( '.hvn-theme-mobile-dropdown-toggle', item ) ) {
     294                });
     295
     296                on(mobileMenu, 'keydown', function (event) {
     297                        trapFocus(mobileMenu, event);
     298                });
     299
     300                // Initialize mobile dropdown toggles
     301                qsa('.hvn-theme-mobile-nav-menu .menu-item-has-children').forEach(function (item, index) {
     302                        var subMenu = qs(':scope > .sub-menu', item);
     303                        if (!subMenu || qs('.hvn-theme-mobile-dropdown-toggle', item)) {
    251304                                return;
    252305                        }
    253306
    254                         var toggle = document.createElement( 'button' );
     307                        var subMenuId = subMenu.id || ('hvn-mobile-submenu-' + index);
     308                        subMenu.id = subMenuId;
     309                        subMenu.setAttribute('aria-hidden', 'true');
     310
     311                        var toggle = document.createElement('button');
    255312                        toggle.className = 'hvn-theme-mobile-dropdown-toggle';
    256313                        toggle.type = 'button';
    257                         toggle.setAttribute( 'aria-expanded', 'false' );
    258                         toggle.innerHTML = '▼';
    259                         item.appendChild( toggle );
    260 
    261                         on( toggle, 'click', function ( event ) {
     314                        toggle.setAttribute('aria-expanded', 'false');
     315                        toggle.setAttribute('aria-controls', subMenuId);
     316                        toggle.setAttribute('aria-label', 'Toggle submenu');
     317                        item.appendChild(toggle);
     318
     319                        on(toggle, 'click', function (event) {
    262320                                event.preventDefault();
    263321                                event.stopPropagation();
    264                                 var expanded = toggle.getAttribute( 'aria-expanded' ) === 'true';
    265                                 toggle.setAttribute( 'aria-expanded', expanded ? 'false' : 'true' );
    266                                 toggle.classList.toggle( 'active', ! expanded );
    267                                 subMenu.classList.toggle( 'toggled', ! expanded );
    268                                 subMenu.style.display = expanded ? 'none' : 'block';
    269                         } );
    270 
    271                         on( toggle, 'keydown', handleActivation );
    272                 } );
     322                                var expanded = toggle.getAttribute('aria-expanded') === 'true';
     323                                var isExpanded = !expanded;
     324                                toggle.setAttribute('aria-expanded', isExpanded ? 'true' : 'false');
     325                                toggle.classList.toggle('active', isExpanded);
     326
     327                                var parentLink = qs(':scope > a', item);
     328                                if (parentLink) {
     329                                        parentLink.setAttribute('aria-expanded', isExpanded ? 'true' : 'false');
     330                                }
     331
     332                                subMenu.setAttribute('aria-hidden', isExpanded ? 'false' : 'true');
     333                                subMenu.classList.toggle('toggled', isExpanded);
     334                        });
     335
     336                        on(toggle, 'keydown', handleActivation);
     337                });
     338
     339                // Handle submenu toggle from parent link
     340                qsa('.hvn-theme-mobile-nav-menu .menu-item-has-children > a').forEach(function (link) {
     341                        var item = link.parentNode;
     342                        var subMenu = qs(':scope > .sub-menu', item);
     343                        var toggle = qs('.hvn-theme-mobile-dropdown-toggle', item);
     344
     345                        // Allow enter/space on the parent link to toggle if submenu exists
     346                        on(link, 'keydown', function (event) {
     347                                if ((event.key === 'Enter' || event.key === ' ') && subMenu && toggle) {
     348                                        event.preventDefault();
     349                                        toggle.click();
     350                                }
     351                        });
     352                });
    273353        }
    274354
    275355        function initSkipLink() {
    276                 var skipLink = qs( '.skip-link' );
    277                 if ( ! skipLink ) {
    278                         return;
    279                 }
    280 
    281                 on( skipLink, 'keydown', function ( event ) {
    282                         if ( event.key === 'Enter' ) {
    283                                 var target = skipLink.getAttribute( 'href' );
    284                                 if ( target ) {
    285                                         var node = qs( target );
    286                                         if ( node ) {
    287                                                 node.setAttribute( 'tabindex', '-1' );
     356                var skipLink = qs('.skip-link');
     357                if (!skipLink) {
     358                        return;
     359                }
     360
     361                on(skipLink, 'keydown', function (event) {
     362                        if (event.key === 'Enter') {
     363                                var target = skipLink.getAttribute('href');
     364                                if (target) {
     365                                        var node = qs(target);
     366                                        if (node) {
     367                                                node.setAttribute('tabindex', '-1');
    288368                                                node.focus();
    289369                                        }
    290370                                }
    291371                        }
    292                 } );
     372                });
    293373        }
    294374
    295375        function initScrollTop() {
    296                 var scrollTopBtn = document.getElementById( 'hvn-scroll-top' ) || document.querySelector( '.hvn-theme-back-to-top' );
    297                 if ( ! scrollTopBtn ) {
    298                         return;
    299                 }
    300 
    301                 on( window, 'scroll', function () {
    302                         if ( window.scrollY > 300 ) {
    303                                 scrollTopBtn.classList.add( 'show' );
     376                var scrollTopBtn = document.getElementById('hvn-scroll-top') || document.querySelector('.hvn-theme-back-to-top');
     377                if (!scrollTopBtn) {
     378                        return;
     379                }
     380
     381                on(window, 'scroll', function () {
     382                        if (window.scrollY > 300) {
     383                                scrollTopBtn.classList.add('show');
    304384                        } else {
    305                                 scrollTopBtn.classList.remove( 'show' );
    306                         }
    307                 } );
    308 
    309                 function scrollToTop( event ) {
    310                         if ( event ) {
     385                                scrollTopBtn.classList.remove('show');
     386                        }
     387                });
     388
     389                function scrollToTop(event) {
     390                        if (event) {
    311391                                event.preventDefault();
    312392                        }
    313                         window.scrollTo( { top: 0, behavior: 'smooth' } );
    314                 }
    315 
    316                 on( scrollTopBtn, 'click', scrollToTop );
    317                 on( scrollTopBtn, 'keydown', function ( event ) {
    318                         if ( event.key === 'Enter' || event.key === ' ' ) {
    319                                 scrollToTop( event );
    320                         }
    321                 } );
     393                        window.scrollTo({ top: 0, behavior: 'smooth' });
     394                }
     395
     396                on(scrollTopBtn, 'click', scrollToTop);
     397                on(scrollTopBtn, 'keydown', function (event) {
     398                        if (event.key === 'Enter' || event.key === ' ') {
     399                                scrollToTop(event);
     400                        }
     401                });
    322402        }
    323403
    324404        function initHeaderPropertySearch() {
    325                 var panel = qs( '#hvn-property-search-panel' );
    326                 var openBtn = qs( '[data-hvn-search-open]' );
    327 
    328                 if ( ! panel || ! openBtn ) {
    329                         return;
    330                 }
    331 
    332                 var dialog = qs( '.hvn-theme-property-search-panel__dialog', panel );
    333                 var closeTriggers = qsa( '[data-hvn-search-close]', panel );
    334                 var keywordField = qs( '#hvn-header-address-keyword', panel );
     405                var panel = qs('#hvn-property-search-panel');
     406                var openBtn = qs('[data-hvn-search-open]');
     407
     408                if (!panel || !openBtn) {
     409                        return;
     410                }
     411
     412                var dialog = qs('.hvn-theme-property-search-panel__dialog', panel);
     413                var closeTriggers = qsa('[data-hvn-search-close]', panel);
     414                var keywordField = qs('#hvn-header-address-keyword', panel);
    335415
    336416                function openPanel() {
    337417                        panel.hidden = false;
    338                         panel.setAttribute( 'aria-hidden', 'false' );
    339                         panel.classList.add( 'is-open' );
    340                         openBtn.setAttribute( 'aria-expanded', 'true' );
    341                         document.body.classList.add( 'hvn-search-panel-open' );
     418                        panel.setAttribute('aria-hidden', 'false');
     419                        openBtn.setAttribute('aria-expanded', 'true');
     420                        document.body.classList.add('hvn-search-panel-open');
    342421                        document.body.style.overflow = 'hidden';
    343422
    344                         setTimeout( function () {
    345                                 if ( keywordField ) {
     423                        requestAnimationFrame(function () {
     424                                requestAnimationFrame(function () {
     425                                        panel.classList.add('is-open');
     426                                });
     427                        });
     428
     429                        window.setTimeout(function () {
     430                                if (keywordField) {
    346431                                        keywordField.focus();
    347432                                }
    348                         }, 100 );
     433                        }, 320);
    349434                }
    350435
    351436                function closePanel() {
    352                         panel.classList.remove( 'is-open' );
    353                         panel.setAttribute( 'aria-hidden', 'true' );
    354                         openBtn.setAttribute( 'aria-expanded', 'false' );
    355                         document.body.classList.remove( 'hvn-search-panel-open' );
     437                        panel.classList.remove('is-open');
     438                        panel.setAttribute('aria-hidden', 'true');
     439                        openBtn.setAttribute('aria-expanded', 'false');
     440                        document.body.classList.remove('hvn-search-panel-open');
    356441                        document.body.style.overflow = '';
    357442                        openBtn.focus();
    358443
    359                         setTimeout( function () {
     444                        window.setTimeout(function () {
    360445                                panel.hidden = true;
    361                         }, 200 );
    362                 }
    363 
    364                 on( openBtn, 'click', function ( event ) {
     446                        }, 320);
     447                }
     448
     449                on(openBtn, 'click', function (event) {
    365450                        event.preventDefault();
    366                         if ( panel.classList.contains( 'is-open' ) ) {
     451                        if (panel.classList.contains('is-open')) {
    367452                                closePanel();
    368453                        } else {
    369454                                openPanel();
    370455                        }
    371                 } );
    372 
    373                 closeTriggers.forEach( function ( trigger ) {
    374                         on( trigger, 'click', function ( event ) {
     456                });
     457
     458                closeTriggers.forEach(function (trigger) {
     459                        on(trigger, 'click', function (event) {
    375460                                event.preventDefault();
    376461                                closePanel();
    377                         } );
    378                 } );
    379 
    380                 on( document, 'keydown', function ( event ) {
    381                         if ( event.key === 'Escape' && panel.classList.contains( 'is-open' ) ) {
     462                        });
     463                });
     464
     465                on(document, 'keydown', function (event) {
     466                        if (event.key === 'Escape' && panel.classList.contains('is-open')) {
    382467                                closePanel();
    383468                        }
    384                 } );
    385 
    386                 if ( dialog ) {
    387                         on( dialog, 'keydown', function ( event ) {
    388                                 trapFocus( dialog, event );
    389                         } );
     469                });
     470
     471                if (dialog) {
     472                        on(dialog, 'keydown', function (event) {
     473                                trapFocus(dialog, event);
     474                        });
    390475                }
    391476        }
    392477
    393478        function initPreloader() {
    394                 var preloader = qs( '#hvn-theme-preloader' );
    395                 if ( ! preloader ) {
     479                var preloader = qs('#hvn-theme-preloader');
     480                if (!preloader) {
    396481                        return;
    397482                }
    398483
    399484                function hidePreloader() {
    400                         preloader.classList.add( 'is-hidden' );
    401                         preloader.setAttribute( 'aria-hidden', 'true' );
    402                         document.body.classList.remove( 'hvn-theme-is-loading' );
    403                         document.body.classList.add( 'hvn-theme-is-loaded' );
    404 
    405                         setTimeout( function () {
    406                                 if ( preloader.parentNode ) {
    407                                         preloader.parentNode.removeChild( preloader );
    408                                 }
    409                         }, 450 );
    410                 }
    411 
    412                 if ( document.readyState === 'complete' ) {
     485                        preloader.classList.add('is-hidden');
     486                        preloader.setAttribute('aria-hidden', 'true');
     487                        document.body.classList.remove('hvn-theme-is-loading');
     488                        document.body.classList.add('hvn-theme-is-loaded');
     489
     490                        setTimeout(function () {
     491                                if (preloader.parentNode) {
     492                                        preloader.parentNode.removeChild(preloader);
     493                                }
     494                        }, 450);
     495                }
     496
     497                if (document.readyState === 'complete') {
    413498                        hidePreloader();
    414499                } else {
    415                         on( window, 'load', hidePreloader );
     500                        on(window, 'load', hidePreloader);
    416501                }
    417502        }
    … …  
    427512        }
    428513
    429         if ( document.readyState === 'loading' ) {
    430                 document.addEventListener( 'DOMContentLoaded', init );
     514        if (document.readyState === 'loading') {
     515                document.addEventListener('DOMContentLoaded', init);
    431516        } else {
    432517                init();
    433518        }
    434 } )();
     519})();
  • havenlytics-realty/1.15.5/functions.php

    r334339 r334420  
    77
    88if ( ! defined( 'HVN_REALTY_VERSION' ) ) {
    9     define( 'HVN_REALTY_VERSION', '1.15.0' );
     9    define( 'HVN_REALTY_VERSION', '1.15.5' );
    1010}
    1111
    … …  
    2929    add_image_size( 'hvn-realty-blog', 800, 450, true );
    3030    add_image_size( 'hvn-realty-featured', 1200, 600, true );
    31     add_post_type_support( 'hvnly_property', 'thumbnail' );
     31
     32    if ( post_type_exists( 'hvnly_property' ) ) {
     33        add_post_type_support( 'hvnly_property', 'thumbnail' );
     34    }
    3235
    3336
    … …  
    121124
    122125function hvn_realty_font_preconnect() {
    123     if ( is_admin() ) {
     126    if ( is_admin() || ! function_exists( 'hvn_realty_get_google_font_families' ) ) {
    124127        return;
    125128    }
    … …  
    154157function hvn_realty_scripts() {
    155158    $font_deps = array();
    156     $font_handle = hvn_realty_enqueue_google_fonts();
    157     if ( $font_handle ) {
    158         $font_deps[] = $font_handle;
    159     }
    160 
    161     wp_enqueue_style(
    162         'hvn-realty-theme',
    163         HVN_REALTY_TEMPLATE_URL . '/assets/css/theme.css',
    164         $font_deps,
    165         HVN_REALTY_VERSION
    166     );
    167 
    168     wp_enqueue_style(
    169         'hvn-realty-polish',
    170         HVN_REALTY_TEMPLATE_URL . '/assets/css/polish.css',
    171         array( 'hvn-realty-theme' ),
    172         HVN_REALTY_VERSION
    173     );
    174 
    175     wp_enqueue_style(
    176         'hvn-realty-layouts',
    177         HVN_REALTY_TEMPLATE_URL . '/assets/css/layouts.css',
    178         array( 'hvn-realty-polish' ),
    179         HVN_REALTY_VERSION
    180     );
    181 
    182     wp_enqueue_style(
    183         'hvn-realty-style',
    184         get_stylesheet_uri(),
    185         array( 'hvn-realty-layouts' ),
    186         HVN_REALTY_VERSION
    187     );
    188 
    189     wp_enqueue_script(
    190         'hvn-realty-theme-js',
    191         HVN_REALTY_TEMPLATE_URL . '/assets/js/theme.js',
    192         array(),
    193         HVN_REALTY_VERSION,
    194         true
    195     );
     159
     160    if ( function_exists( 'hvn_realty_enqueue_google_fonts' ) ) {
     161        $font_handle = hvn_realty_enqueue_google_fonts();
     162        if ( $font_handle ) {
     163            $font_deps[] = $font_handle;
     164        }
     165    }
     166
     167    if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) {
     168        hvn_realty_enqueue_theme_style( 'hvn-realty-theme', 'assets/css/theme.css', $font_deps );
     169        hvn_realty_enqueue_theme_style( 'hvn-realty-polish', 'assets/css/polish.css', array( 'hvn-realty-theme' ) );
     170        hvn_realty_enqueue_theme_style( 'hvn-realty-layouts', 'assets/css/layouts.css', array( 'hvn-realty-polish' ) );
     171        hvn_realty_enqueue_theme_style( 'hvn-realty-style', 'style.css', array( 'hvn-realty-layouts' ) );
     172    } else {
     173        wp_enqueue_style( 'hvn-realty-theme', HVN_REALTY_TEMPLATE_URL . '/assets/css/theme.css', $font_deps, HVN_REALTY_VERSION );
     174        wp_enqueue_style( 'hvn-realty-polish', HVN_REALTY_TEMPLATE_URL . '/assets/css/polish.css', array( 'hvn-realty-theme' ), HVN_REALTY_VERSION );
     175        wp_enqueue_style( 'hvn-realty-layouts', HVN_REALTY_TEMPLATE_URL . '/assets/css/layouts.css', array( 'hvn-realty-polish' ), HVN_REALTY_VERSION );
     176        wp_enqueue_style( 'hvn-realty-style', get_stylesheet_uri(), array( 'hvn-realty-layouts' ), HVN_REALTY_VERSION );
     177    }
     178
     179    if ( function_exists( 'hvn_realty_enqueue_theme_script' ) ) {
     180        hvn_realty_enqueue_theme_script( 'hvn-realty-theme-js', 'assets/js/theme.js' );
     181    } else {
     182        wp_enqueue_script( 'hvn-realty-theme-js', HVN_REALTY_TEMPLATE_URL . '/assets/js/theme.js', array(), HVN_REALTY_VERSION, true );
     183    }
    196184
    197185    wp_localize_script(
    … …  
    224212
    225213        if ( is_page() || is_404() ) {
    226                 wp_enqueue_style(
    227                         'hvn-realty-page',
    228                         HVN_REALTY_TEMPLATE_URL . '/assets/css/page.css',
    229                         array( 'hvn-realty-layouts' ),
    230                         HVN_REALTY_VERSION
    231                 );
     214                if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) {
     215                        hvn_realty_enqueue_theme_style( 'hvn-realty-page', 'assets/css/page.css', array( 'hvn-realty-layouts' ) );
     216                } else {
     217                        wp_enqueue_style( 'hvn-realty-page', HVN_REALTY_TEMPLATE_URL . '/assets/css/page.css', array( 'hvn-realty-layouts' ), HVN_REALTY_VERSION );
     218                }
    232219        }
    233220
    234221        if ( is_home() || is_archive() || is_search() ) {
    235                 wp_enqueue_style(
    236                         'hvn-realty-blog',
    237                         HVN_REALTY_TEMPLATE_URL . '/assets/css/blog.css',
    238                         array( 'hvn-realty-layouts' ),
    239                         HVN_REALTY_VERSION
    240                 );
     222                if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) {
     223                        hvn_realty_enqueue_theme_style( 'hvn-realty-blog', 'assets/css/blog.css', array( 'hvn-realty-layouts' ) );
     224                } else {
     225                        wp_enqueue_style( 'hvn-realty-blog', HVN_REALTY_TEMPLATE_URL . '/assets/css/blog.css', array( 'hvn-realty-layouts' ), HVN_REALTY_VERSION );
     226                }
    241227        }
    242228
    243229        if ( is_single() ) {
    244                 wp_enqueue_style(
    245                         'hvn-realty-single',
    246                         HVN_REALTY_TEMPLATE_URL . '/assets/css/single.css',
    247                         array( 'hvn-realty-layouts' ),
    248                         HVN_REALTY_VERSION
    249                 );
     230                if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) {
     231                        hvn_realty_enqueue_theme_style( 'hvn-realty-single', 'assets/css/single.css', array( 'hvn-realty-layouts' ) );
     232                } else {
     233                        wp_enqueue_style( 'hvn-realty-single', HVN_REALTY_TEMPLATE_URL . '/assets/css/single.css', array( 'hvn-realty-layouts' ), HVN_REALTY_VERSION );
     234                }
    250235        }
    251236}
    … …  
    256241 */
    257242function hvn_realty_editor_fonts() {
    258         hvn_realty_enqueue_google_fonts();
     243        if ( function_exists( 'hvn_realty_enqueue_google_fonts' ) ) {
     244                hvn_realty_enqueue_google_fonts();
     245        }
    259246}
    260247add_action( 'enqueue_block_editor_assets', 'hvn_realty_editor_fonts', 5 );
    … …  
    487474 * Load required files.
    488475 */
    489 require get_template_directory() . '/inc/customizer/loader.php';
    490 require get_template_directory() . '/inc/plugin-hooks.php';
    491 require get_template_directory() . '/inc/integrations/havenlytics/bootstrap.php';
    492 require get_template_directory() . '/inc/setup/theme-launch.php';
    493 require get_template_directory() . '/inc/setup/theme-menus.php';
    494 require get_template_directory() . '/inc/setup/theme-footer-widgets.php';
    495 require get_template_directory() . '/inc/elementor.php';
    496 require get_template_directory() . '/inc/custom-header.php';
    497 require get_template_directory() . '/inc/template-tags.php';
    498 require get_template_directory() . '/inc/layout.php';
    499 require get_template_directory() . '/inc/template-functions.php';
    500 require get_template_directory() . '/inc/customizer.php';
    501 require get_template_directory() . '/inc/breadcrumbs.php';
    502 require get_template_directory() . '/inc/admin/realty-admin.php';
    503 require get_template_directory() . '/inc/admin/theme-welcome-notice.php';
     476$theme_loader = get_template_directory() . '/inc/theme-loader.php';
     477if ( file_exists( $theme_loader ) ) {
     478        require_once $theme_loader;
     479} else {
     480        /**
     481         * Minimal fallback when theme-loader.php is missing from the package.
     482         *
     483         * @param string $relative_path Theme-relative file path.
     484         * @param bool   $optional      Unused in fallback.
     485         * @return bool
     486         */
     487        function hvn_realty_load_theme_file( $relative_path, $optional = true ) {
     488                $file = get_template_directory() . '/' . ltrim( $relative_path, '/' );
     489                if ( file_exists( $file ) ) {
     490                        require_once $file;
     491                        return true;
     492                }
     493                return false;
     494        }
     495}
     496
     497hvn_realty_load_theme_file( 'inc/customizer/loader.php', false );
     498hvn_realty_load_theme_file( 'inc/plugin-hooks.php', false );
     499
     500if ( ! hvn_realty_load_theme_file( 'inc/integrations/havenlytics/bootstrap.php', true ) ) {
     501        if ( function_exists( 'hvn_realty_missing_integration_notice' ) ) {
     502                add_action( 'admin_notices', 'hvn_realty_missing_integration_notice' );
     503        }
     504
     505        $integration_files = array(
     506                'helpers.php',
     507                'hero-map.php',
     508                'carousel.php',
     509                'homepage-settings.php',
     510                'homepage.php',
     511                'homepage-assets.php',
     512                'breadcrumbs.php',
     513                'body-classes.php',
     514                'plugin-shell.php',
     515                'assets.php',
     516        );
     517
     518        foreach ( $integration_files as $integration_file ) {
     519                hvn_realty_load_theme_file( 'inc/integrations/havenlytics/' . $integration_file, true );
     520        }
     521}
     522
     523hvn_realty_load_theme_file( 'inc/integration-fallbacks.php', true );
     524hvn_realty_load_theme_file( 'inc/setup/theme-launch.php', false );
     525hvn_realty_load_theme_file( 'inc/setup/theme-menus.php', false );
     526hvn_realty_load_theme_file( 'inc/setup/theme-footer-widgets.php', false );
     527hvn_realty_load_theme_file( 'inc/elementor.php', true );
     528hvn_realty_load_theme_file( 'inc/custom-header.php', false );
     529hvn_realty_load_theme_file( 'inc/template-tags.php', false );
     530hvn_realty_load_theme_file( 'inc/layout.php', false );
     531hvn_realty_load_theme_file( 'inc/template-functions.php', false );
     532hvn_realty_load_theme_file( 'inc/customizer.php', false );
     533hvn_realty_load_theme_file( 'inc/breadcrumbs.php', false );
     534hvn_realty_load_theme_file( 'inc/admin/realty-admin.php', true );
     535hvn_realty_load_theme_file( 'inc/admin/theme-welcome-notice.php', true );
    504536
    505537if ( defined( 'JETPACK__VERSION' ) ) {
    506         require get_template_directory() . '/inc/jetpack.php';
    507 }
    508 
    509 require get_template_directory() . '/inc/class-hvn-realty-walker-nav-menu.php';
     538        hvn_realty_load_theme_file( 'inc/jetpack.php', true );
     539}
     540
     541hvn_realty_load_theme_file( 'inc/class-hvn-realty-walker-nav-menu.php', false );
  • havenlytics-realty/1.15.5/inc/admin/realty-admin.php

    r334339 r334420  
    7878function hvn_realty_get_getting_started_url() {
    7979        return hvn_realty_get_realty_admin_url();
    80 }
    81 
    82 /**
    83  * WordPress.org plugin install URL.
    84  *
    85  * @return string
    86  */
    87 function hvn_realty_get_plugin_install_url() {
    88         if ( hvn_realty_is_havenlytics_plugin_active() ) {
    89                 return admin_url( 'plugins.php' );
    90         }
    91 
    92         return admin_url( 'plugin-install.php?s=havenlytics&tab=search&type=term' );
    9380}
    9481
    … …  
    272259        }
    273260
    274         wp_enqueue_style(
    275                 'hvn-realty-admin',
    276                 HVN_REALTY_TEMPLATE_URL . '/assets/css/admin-realty.css',
    277                 array(),
    278                 HVN_REALTY_VERSION
    279         );
     261        if ( function_exists( 'hvn_realty_enqueue_theme_style' ) ) {
     262                hvn_realty_enqueue_theme_style( 'hvn-realty-admin', 'assets/css/admin-realty.css' );
     263        } elseif ( file_exists( get_template_directory() . '/assets/css/admin-realty.css' ) ) {
     264                wp_enqueue_style(
     265                        'hvn-realty-admin',
     266                        HVN_REALTY_TEMPLATE_URL . '/assets/css/admin-realty.css',
     267                        array(),
     268                        HVN_REALTY_VERSION
     269                );
     270        }
    280271}
    281272add_action( 'admin_enqueue_scripts', 'hvn_realty_admin_assets' );
  • havenlytics-realty/1.15.5/inc/custom-header.php

    r334339 r334420  
    11<?php
    22/**
    3  * Disable header/background image features (solid colors only).
     3 * Core custom header and background support.
     4 *
     5 * Site branding uses custom-logo; global background color uses Havenlytics
     6 * Theme Settings. Core header/background image panels stay hidden in
     7 * inc/customizer/panel-integration.php.
    48 *
    59 * @package Havenlytics_Realty
    … …  
    711
    812/**
    9  * Disable WordPress core custom header media support.
     13 * Register WordPress custom header and background support.
    1014 */
    11 function hvn_realty_disable_header_media() {
    12         remove_theme_support( 'custom-header' );
     15function hvn_realty_custom_header_setup() {
     16        add_theme_support(
     17                'custom-header',
     18                apply_filters(
     19                        'hvn_realty_custom_header_args',
     20                        array(
     21                                'default-image'      => '',
     22                                'default-text-color' => '1e1e2f',
     23                                'width'              => 1920,
     24                                'height'             => 400,
     25                                'flex-height'        => true,
     26                                'flex-width'         => true,
     27                                'uploads'            => true,
     28                                'header-text'        => false,
     29                                'wp-head-callback'   => 'hvn_realty_custom_header_style',
     30                        )
     31                )
     32        );
     33
     34        add_theme_support(
     35                'custom-background',
     36                apply_filters(
     37                        'hvn_realty_custom_background_args',
     38                        array(
     39                                'default-color' => 'f8f8f8',
     40                                'default-image' => '',
     41                        )
     42                )
     43        );
    1344}
    14 add_action( 'after_setup_theme', 'hvn_realty_disable_header_media', 20 );
     45add_action( 'after_setup_theme', 'hvn_realty_custom_header_setup' );
    1546
    1647/**
    17  * Disable WordPress custom background image (keep color via Global Design).
     48 * Output styles when a core custom header image is set.
    1849 */
    19 function hvn_realty_disable_background_image() {
    20         remove_theme_support( 'custom-background' );
     50function hvn_realty_custom_header_style() {
     51        $header_image = get_header_image();
     52
     53        if ( empty( $header_image ) ) {
     54                return;
     55        }
     56        ?>
     57        <style id="hvn-realty-custom-header-styles" type="text/css">
     58                .hvn-theme-header {
     59                        background-image: url(<?php echo esc_url( $header_image ); ?>);
     60                        background-size: cover;
     61                        background-position: center center;
     62                }
     63        </style>
     64        <?php
    2165}
    22 add_action( 'after_setup_theme', 'hvn_realty_disable_background_image', 20 );
  • havenlytics-realty/1.15.5/inc/customizer.php

    r334339 r334420  
    4545                        array(
    4646                                'selector'            => '.hvn-theme-logo',
     47                                'container_inclusive' => true,
     48                                'render_callback'     => 'hvn_realty_customize_partial_logo',
     49                        )
     50                );
     51                $wp_customize->selective_refresh->add_partial(
     52                        'custom_logo_mobile_branding',
     53                        array(
     54                                'settings'            => array( 'custom_logo' ),
     55                                'selector'            => '.hvn-theme-mobile-menu-branding',
    4756                                'container_inclusive' => true,
    4857                                'render_callback'     => 'hvn_realty_customize_partial_logo',
    … …  
    170179 */
    171180function hvn_realty_customize_preview_js() {
     181        if ( ! file_exists( get_template_directory() . '/assets/js/customizer.js' ) ) {
     182                return;
     183        }
     184
    172185        wp_enqueue_script(
    173186                'hvn-realty-customizer',
    … …  
    181194        $page_for_posts = (int) get_option( 'page_for_posts' );
    182195        if ( $page_for_posts ) {
    183                 $blog_url = get_permalink( $page_for_posts );
     196                $permalink = get_permalink( $page_for_posts );
     197                if ( is_string( $permalink ) && $permalink !== '' ) {
     198                        $blog_url = $permalink;
     199                }
    184200        }
    185201
    186202        $localize = array(
    187                 'fontStacks'       => hvn_realty_get_font_stack_map(),
    188                 'googleFonts'      => hvn_realty_get_google_fonts_api_map(),
     203                'fontStacks'       => function_exists( 'hvn_realty_get_font_stack_map' ) ? hvn_realty_get_font_stack_map() : array(),
     204                'googleFonts'      => function_exists( 'hvn_realty_get_google_fonts_api_map' ) ? hvn_realty_get_google_fonts_api_map() : array(),
    189205                'defaultCopyright' => sprintf(
    190206                        /* translators: 1: Year, 2: Site name */
    … …  
    196212        );
    197213
     214        if ( function_exists( 'hvn_realty_get_property_search_url' ) ) {
     215                $localize['propertySearchUrl'] = esc_url( hvn_realty_get_property_search_url() );
     216        }
     217
     218        $localize['homeUrl'] = esc_url( home_url( '/' ) );
     219
    198220        if ( function_exists( 'hvn_realty_get_customizer_home_section_selectors' ) ) {
    199221                $localize['homeSections'] = hvn_realty_get_customizer_home_section_selectors();
  • havenlytics-realty/1.15.5/inc/customizer/controls.php

    r334339 r334420  
    5555 */
    5656function hvn_realty_customize_controls_js() {
     57        if ( ! file_exists( get_template_directory() . '/assets/js/customizer-controls.js' ) ) {
     58                return;
     59        }
     60
    5761        wp_enqueue_script(
    5862                'hvn-realty-customizer-controls',
  • havenlytics-realty/1.15.5/inc/customizer/helpers.php

    r334339 r334420  
    300300
    301301/**
     302 * Sanitize a full URL, site path, or page slug for theme links.
     303 *
     304 * @param string $input Input.
     305 * @return string
     306 */
     307function hvn_realty_sanitize_url_or_path( $input ) {
     308        $input = trim( (string) $input );
     309
     310        if ( '' === $input ) {
     311                return '';
     312        }
     313
     314        if ( preg_match( '#^https?://#i', $input ) ) {
     315                return esc_url_raw( $input );
     316        }
     317
     318        if ( 0 === strpos( $input, '/' ) ) {
     319                return '/' . ltrim( sanitize_text_field( wp_unslash( $input ) ), '/' );
     320        }
     321
     322        return sanitize_title( $input );
     323}
     324
     325/**
     326 * Resolve a Customizer link value to a front-end URL.
     327 *
     328 * @param string $value        URL, path, slug, or empty.
     329 * @param string $fallback_url Used when value is empty.
     330 * @return string
     331 */
     332function hvn_realty_resolve_theme_link( $value, $fallback_url ) {
     333        $value = trim( (string) $value );
     334
     335        if ( '' === $value ) {
     336                return esc_url( $fallback_url );
     337        }
     338
     339        if ( preg_match( '#^https?://#i', $value ) ) {
     340                return esc_url( $value );
     341        }
     342
     343        if ( 0 === strpos( $value, '/' ) ) {
     344                return esc_url( home_url( $value ) );
     345        }
     346
     347        return esc_url( home_url( '/' . trim( $value, '/' ) . '/' ) );
     348}
     349
     350/**
    302351 * Get heading scale multiplier.
    303352 *
  • havenlytics-realty/1.15.5/inc/customizer/homepage-css-output.php

    r334339 r334420  
    144144
    145145/**
     146 * Stylesheet handle for homepage Customizer inline CSS.
     147 *
     148 * @return string
     149 */
     150function hvn_realty_get_homepage_inline_style_handle() {
     151        if ( wp_style_is( 'hvn-realty-havenlytics-compat', 'registered' ) ) {
     152                return 'hvn-realty-havenlytics-compat';
     153        }
     154
     155        if ( wp_style_is( 'hvn-realty-home-blog', 'registered' ) ) {
     156                return 'hvn-realty-home-blog';
     157        }
     158
     159        return 'hvn-realty-theme';
     160}
     161
     162
     163
     164/**
    146165
    147166 * Output homepage Customizer styles on the front end.
    … …  
    177196        }
    178197
    179 
    180 
    181         wp_add_inline_style( 'hvn-realty-home-sections', $css );
     198        $handle = hvn_realty_get_homepage_inline_style_handle();
     199
     200        if ( ! wp_style_is( $handle, 'enqueued' ) ) {
     201                wp_enqueue_style( $handle );
     202        }
     203
     204        wp_add_inline_style( $handle, $css );
    182205
    183206}
  • havenlytics-realty/1.15.5/inc/customizer/loader.php

    r334339 r334420  
    66 */
    77
    8 require get_template_directory() . '/inc/customizer/helpers.php';
    9 require get_template_directory() . '/inc/customizer/controls.php';
    10 require get_template_directory() . '/inc/customizer/sections.php';
    11 require get_template_directory() . '/inc/customizer/sections-homepage.php';
    12 require get_template_directory() . '/inc/customizer/panel-integration.php';
    13 require get_template_directory() . '/inc/customizer/selective-refresh.php';
    14 require get_template_directory() . '/inc/customizer/css-output.php';
    15 require get_template_directory() . '/inc/customizer/homepage-css-output.php';
     8if ( ! defined( 'ABSPATH' ) ) {
     9        exit;
     10}
     11
     12if ( ! function_exists( 'hvn_realty_load_theme_file' ) ) {
     13        $theme_loader = get_template_directory() . '/inc/theme-loader.php';
     14        if ( file_exists( $theme_loader ) ) {
     15                require_once $theme_loader;
     16        }
     17}
     18
     19$customizer_files = array(
     20        'helpers.php',
     21        'controls.php',
     22        'sections.php',
     23        'sections-homepage.php',
     24        'panel-integration.php',
     25        'selective-refresh.php',
     26        'css-output.php',
     27        'homepage-css-output.php',
     28);
     29
     30foreach ( $customizer_files as $customizer_file ) {
     31        hvn_realty_load_theme_file( 'inc/customizer/' . $customizer_file, false );
     32}
  • havenlytics-realty/1.15.5/inc/customizer/sections-homepage.php

    r334339 r334420  
    594594                )
    595595        );
     596
     597        hvn_realty_customizer_add_text(
     598                $wp_customize,
     599                'hvn_realty_home_department_button_text',
     600                $section,
     601                esc_html__( 'Footer button text', 'havenlytics-realty' ),
     602                __( 'View All Properties', 'havenlytics-realty' )
     603        );
     604
     605        $wp_customize->add_setting(
     606                'hvn_realty_home_department_button_url',
     607                array(
     608                        'default'           => '',
     609                        'sanitize_callback' => 'hvn_realty_sanitize_url_or_path',
     610                        'transport'         => 'postMessage',
     611                )
     612        );
     613        $wp_customize->add_control(
     614                'hvn_realty_home_department_button_url',
     615                array(
     616                        'label'       => esc_html__( 'Footer button link', 'havenlytics-realty' ),
     617                        'description' => esc_html__( 'Full URL, path (e.g. /properties/), or page slug. Leave empty for the property search page.', 'havenlytics-realty' ),
     618                        'section'     => $section,
     619                        'type'        => 'text',
     620                )
     621        );
    596622}
    597623
  • havenlytics-realty/1.15.5/inc/customizer/selective-refresh.php

    r334339 r334420  
    4141                                'container_inclusive' => false,
    4242                                'render_callback'     => function () {
    43                                         echo esc_html( hvn_realty_get_home_cta_headline() );
     43                                        if ( function_exists( 'hvn_realty_get_home_cta_headline' ) ) {
     44                                                echo esc_html( hvn_realty_get_home_cta_headline() );
     45                                        }
    4446                                },
    4547                        )
    … …  
    5254                                'container_inclusive' => false,
    5355                                'render_callback'     => function () {
    54                                         echo esc_html( hvn_realty_get_home_cta_subtext() );
     56                                        if ( function_exists( 'hvn_realty_get_home_cta_subtext' ) ) {
     57                                                echo esc_html( hvn_realty_get_home_cta_subtext() );
     58                                        }
    5559                                },
    5660                        )
    … …  
    6367                                'container_inclusive' => false,
    6468                                'render_callback'     => function () {
    65                                         echo esc_html( hvn_realty_get_home_cta_primary_text() );
     69                                        if ( function_exists( 'hvn_realty_get_home_cta_primary_text' ) ) {
     70                                                echo esc_html( hvn_realty_get_home_cta_primary_text() );
     71                                        }
    6672                                },
    6773                        )
    … …  
    7581 */
    7682function hvn_realty_customize_partial_copyright() {
     83        if ( ! function_exists( 'hvn_realty_get_copyright_text' ) ) {
     84                return;
     85        }
     86
    7787        echo hvn_realty_get_copyright_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    7888}
    … …  
    8292 */
    8393function hvn_realty_customize_partial_header_cta() {
    84         if ( ! hvn_realty_show_header_cta() ) {
     94        if ( ! function_exists( 'hvn_realty_show_header_cta' ) || ! hvn_realty_show_header_cta() ) {
     95                return;
     96        }
     97
     98        if ( ! function_exists( 'hvn_realty_get_header_cta_text' ) || ! function_exists( 'hvn_realty_get_header_cta_url' ) ) {
    8599                return;
    86100        }
  • havenlytics-realty/1.15.5/inc/layout.php

    r334339 r334420  
    6767
    6868        return is_wp_error( $terms ) ? array() : $terms;
     69}
     70
     71/**
     72 * Bedroom/bathroom option list for header property search.
     73 *
     74 * @return array<string, string> Value => label.
     75 */
     76function hvn_realty_get_search_count_select_options() {
     77        $options = array(
     78                '' => __( 'Any', 'havenlytics-realty' ),
     79        );
     80
     81        for ( $i = 1; $i <= 5; $i++ ) {
     82                $options[ (string) $i ] = (string) $i;
     83        }
     84
     85        $options['6'] = '6+';
     86
     87        return $options;
    6988}
    7089
  • havenlytics-realty/1.15.5/inc/plugin-hooks.php

    r334339 r334420  
    2121 */
    2222function hvn_realty_is_property_context() {
    23         return is_singular( 'hvnly_property' ) || is_post_type_archive( 'hvnly_property' );
     23        return post_type_exists( 'hvnly_property' ) && ( is_singular( 'hvnly_property' ) || is_post_type_archive( 'hvnly_property' ) );
    2424}
    2525
    … …  
    5858
    5959/**
     60 * WordPress.org plugin install URL (or plugins screen when already active).
     61 *
     62 * @return string
     63 */
     64if ( ! function_exists( 'hvn_realty_get_plugin_install_url' ) ) {
     65        function hvn_realty_get_plugin_install_url() {
     66                if ( hvn_realty_is_havenlytics_plugin_active() ) {
     67                        return admin_url( 'plugins.php' );
     68                }
     69
     70                return admin_url( 'plugin-install.php?s=havenlytics&tab=search&type=term' );
     71        }
     72}
     73
     74/**
    6075 * Register integration hooks when plugin is present.
    6176 */
    … …  
    7994 */
    8095function hvn_realty_property_archive_classes( $classes ) {
    81         if ( hvn_realty_is_property_view() ) {
     96        if ( function_exists( 'hvn_realty_is_property_view' ) && hvn_realty_is_property_view() ) {
    8297                $classes[] = 'hvn-property-view';
    8398                $classes   = apply_filters( 'hvn_realty_property_layout_classes', $classes );
    … …  
    91106 */
    92107function hvn_realty_property_styles_hook() {
    93         if ( ! hvn_realty_is_property_view() ) {
     108        if ( ! function_exists( 'hvn_realty_is_property_view' ) || ! hvn_realty_is_property_view() ) {
    94109                return;
    95110        }
    … …  
    126141 */
    127142function hvn_realty_property_archive_before() {
    128         if ( ! is_post_type_archive( 'hvnly_property' ) ) {
     143        if ( ! post_type_exists( 'hvnly_property' ) || ! is_post_type_archive( 'hvnly_property' ) ) {
    129144                return;
    130145        }
    … …  
    136151 */
    137152function hvn_realty_property_single_after() {
    138         if ( ! is_singular( 'hvnly_property' ) ) {
     153        if ( ! post_type_exists( 'hvnly_property' ) || ! is_singular( 'hvnly_property' ) ) {
    139154                return;
    140155        }
  • havenlytics-realty/1.15.5/inc/template-functions.php

    r334339 r334420  
    7777        $classes[] = 'hvn-container-' . hvn_realty_get_container_mode();
    7878
    79         if ( ! hvn_realty_show_header_search() ) {
     79        if ( ! function_exists( 'hvn_realty_show_header_search' ) || ! hvn_realty_show_header_search() ) {
    8080                $classes[] = 'hvn-header-no-search';
    8181        }
    8282
    83         if ( ! hvn_realty_show_header_cta() ) {
     83        if ( ! function_exists( 'hvn_realty_show_header_cta' ) || ! hvn_realty_show_header_cta() ) {
    8484                $classes[] = 'hvn-header-no-cta';
    8585        }
  • havenlytics-realty/1.15.5/readme.txt

    r334339 r334420  
    44Tags: two-columns, right-sidebar, custom-logo, custom-menu, featured-images, full-width-template, theme-options, translation-ready, block-styles, wide-blocks
    55Requires at least: 6.0
    6 Tested up to: 6.9
     6Tested up to: 7.0
    77Requires PHP: 7.4
    8 Stable tag: 1.15.0
     8Stable tag: 1.15.5
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1111
    12 Havenlytics Realty is the official companion theme for the free Havenlytics plugin — launch a professional real estate website with search, listings, and agents in minutes.
     12Official companion theme for the free Havenlytics plugin — launch a real estate website with map search, listings, agents, and agencies.
    1313
    1414== Description ==
    1515
    16 **Havenlytics Realty is the official companion theme for the free [Havenlytics](https://wordpress.org/plugins/havenlytics/) plugin.** Install both to launch a property directory with search, listings, agents, and agencies — or use the theme on its own as a fast, accessible blog and business layout.
    17 
    18 The theme automatically enhances the Havenlytics experience when both are active: a 10-section real estate homepage, plugin page styling, theme breadcrumbs, a Realty setup hub, and Customizer controls for hero text, listing counts, and section visibility.
    19 
    20 = Launch in 4 steps =
    21 
    22 1. Activate **Havenlytics Realty**
    23 2. Go to **Appearance → Realty**
    24 3. Install and activate the **Havenlytics** plugin (recommended, not required)
    25 4. Run the plugin **Setup Wizard** to import demo listings — the theme configures your homepage and menu automatically
    26 
    27 = Real estate homepage (with Havenlytics plugin) =
    28 
    29 * Hero property search section
    30 * Featured and latest property listings
    31 * Property category cards (Sale, Rent, and more)
    32 * Featured agents and agencies
    33 * Live statistics (properties, agents, agencies)
    34 * Call-to-action banners and blog section
    35 * Full Customizer control over hero, counts, and section toggles
     16**[Live demo](https://demo.havenlytics.com/)** | **[Havenlytics plugin](https://wordpress.org/plugins/havenlytics/)** | **[Theme author](https://profiles.wordpress.org/havenlytics/)**
     17
     18Havenlytics Realty is the official WordPress theme built to work with the free **[Havenlytics](https://wordpress.org/plugins/havenlytics/)** property plugin. Together they deliver a complete real estate website: interactive map search, filtered property archives, agent and agency directories, and a conversion-focused homepage — without custom code.
     19
     20Use the theme on its own for a fast, accessible blog or business site. Activate Havenlytics to unlock the full real estate experience.
     21
     22= What Havenlytics Realty provides =
     23
     24* A purpose-built **real estate homepage** with eight customizable sections
     25* **Appearance → Realty** onboarding hub with setup checklist and site snapshot
     26* **Automatic site launch** after demo import (homepage, menus, front page)
     27* **CSS harmonization** for property, agent, and agency pages
     28* **Header property search panel** with keyword, type, location, price, and bed/bath filters
     29* **Theme breadcrumbs** on listings, taxonomies, and directory pages
     30* **Customizer controls** for homepage sections, hero map height, listing counts, and CTAs
     31* **Full-width layouts** for plugin shortcode pages
     32* Accessible, responsive foundation for blogs and business pages
     33
     34= What the Havenlytics plugin adds =
     35
     36The plugin is the property engine. It registers properties, agents, and agencies; powers AJAX search with grid, list, and map views; renders single property pages with galleries, maps, and Contact Agent forms; and includes a Setup Wizard to import demo listings.
     37
     38**Install both** to get the experience shown in the [live demo](https://demo.havenlytics.com/).
     39
     40= Real estate homepage (Havenlytics plugin active) =
     41
     42Eight sections, each toggleable in **Appearance → Customize → Real Estate Homepage**:
     43
     441. **Hero map** — Interactive property map with Customizer department filtering and adjustable height (desktop/mobile)
     452. **Featured properties** — Curated listing carousel (featured meta)
     463. **Properties by department** — Tabbed grids per department (Sale, Rent, Let, Commercial, etc.)
     474. **Properties by location** — Location cards with term images and listing counts
     485. **Agents** — Agent directory carousel
     496. **Agencies** — Agency directory carousel
     507. **Latest blog posts** — Content marketing section (core WordPress posts)
     518. **Call to action** — Search and agent discovery prompts
     52
     53**Header search:** The search icon opens a property search panel (keyword, type, location, bedrooms, bathrooms, min/max price) that submits to the plugin Property Search page.
    3654
    3755= Havenlytics plugin integration =
    3856
    39 * CSS harmonization for property, agent, and agency pages
    40 * Header search links to the plugin Property Search page
    41 * Theme breadcrumbs on listings and agent archives
    42 * Full-width layout for plugin shortcode pages
    43 * Works with plugin templates — no forked overrides required
     57* Detects the plugin automatically — no theme edits to plugin files
     58* Primes plugin assets on the homepage for shortcode-rendered sections
     59* Resolves plugin page URLs for search, grid, lists, agents, and agencies
     60* Applies Customizer design tokens (primary, secondary, text) to plugin UI
     61* Adds context body classes for property, agent, agency, and shortcode pages
     62* Hooks `hvnly_before_main_content` for breadcrumb alignment
     63* Hides duplicate breadcrumbs on the real estate homepage
     64* Friendly fallback prompt when the plugin is not installed
    4465
    4566= Theme foundation =
    … …  
    6485= Performance =
    6586
    66 * Minimal vanilla JavaScript (no jQuery on the front end)
     87* Minimal vanilla JavaScript on the front end (jQuery only where plugin/home scripts require it)
    6788* Conditional stylesheet loading per template
    6889* Google Fonts loaded only for selected typography choices
    … …  
    7091= Works without the plugin =
    7192
    72 Havenlytics Realty remains a fully functional WordPress theme for blogs, business pages, and directories. Without the Havenlytics plugin, the real estate homepage shows a friendly prompt to install the plugin — no errors, no broken layouts.
     93Havenlytics Realty remains a fully functional WordPress theme for blogs, business pages, and directories. Without Havenlytics, the real estate homepage shows a clear prompt to install the plugin — no errors, no broken layouts.
    7394
    7495== Installation ==
    … …  
    7697= Quick start (recommended) =
    7798
    78 1. Install and activate **Havenlytics Realty** from Appearance → Themes.
     991. Install and activate **Havenlytics Realty** from **Appearance → Themes**.
    791002. Open **Appearance → Realty** for the setup checklist.
    80 3. Install the free **[Havenlytics plugin](https://wordpress.org/plugins/havenlytics/)** when prompted (recommended, not required).
    81 4. Run the plugin **Setup Wizard** to import demo properties.
    82 5. Visit your site — the theme creates the homepage, primary menu, and front page automatically.
     1013. Install and activate the free **[Havenlytics plugin](https://wordpress.org/plugins/havenlytics/)** when prompted.
     1024. In the plugin admin, run the **Property Import / Setup Wizard** to import demo listings, agents, and agencies.
     1035. Visit your site — the theme automatically configures the homepage, primary menu, and static front page after import.
     104
     105**See the finished result:** [https://demo.havenlytics.com/](https://demo.havenlytics.com/)
    83106
    84107= Manual install =
    85108
    86 1. In your admin panel, go to Appearance → Themes and click **Add New**.
     1091. In your admin panel, go to **Appearance → Themes** and click **Add New**.
    871102. Search for **Havenlytics Realty** or upload the theme ZIP file.
    881113. Click **Activate**.
    … …  
    91114= After activation =
    92115
    93 * **Appearance → Realty** — onboarding checklist and quick links
    94 * **Appearance → Customize → Real Estate Homepage** — hero, listings, and section controls (requires Havenlytics plugin)
     116* **Appearance → Realty** — onboarding checklist, progress ring, and quick links
     117* **Appearance → Customize → Real Estate Homepage** — hero map, sections, counts, and CTAs (requires Havenlytics plugin)
    95118* **Appearance → Customize → Havenlytics Theme Settings** — global design, header, footer, typography, layout
    96119
     120== Plugin integration ==
     121
     122Havenlytics Realty is designed as the **official front-end companion** for the Havenlytics plugin. Integration is entirely theme-side.
     123
     124= Plugin features represented in the theme =
     125
     126| Plugin capability | How the theme showcases it |
     127|-------------------|----------------------------|
     128| Property map search | Hero map section; header search panel; links to Property Search page |
     129| Property grid | Department tabs (`[hvnly_property_grid]`); Featured carousel (theme query) |
     130| Departments (`hvnly_prop_depts`) | Department tabs; hero map department filter; primary menu |
     131| Locations (`hvnly_prop_locations`) | Location cards; header search location filter |
     132| Property types (`hvnly_prop_types`) | Header search type filter; featured card badges |
     133| Featured listings | Featured properties homepage section |
     134| Agents (`hvnly_agent`) | Agents carousel; CTA links; breadcrumbs; CSS harmonization |
     135| Agencies (`hvnly_agent_agency`) | Agencies carousel; breadcrumbs; CSS harmonization |
     136| Property singles | Styled via compat CSS; theme breadcrumbs; full-width layout |
     137| Property archives & taxonomies | Breadcrumbs; body classes; archive styling |
     138| Contact Agent | Available on plugin single property pages (plugin feature; theme styles the page) |
     139| Setup Wizard demo import | Triggers theme auto-launch (homepage, menus, front page) |
     140| Five plugin pages | Auto-linked in primary menu and footer widgets after launch |
     141
     142= Plugin pages created on install =
     143
     144* Property Search (`[hvnly_property_search]`) — full filters, grid/list/map views
     145* Property Grid (`[hvnly_property_grid]`)
     146* Property Lists (`[hvnly_property_lists]`) — list layout
     147* Property Agents (`[hvnly_property_agents]`)
     148* Property Agencies (`[hvnly_property_agencies]`)
     149
     150The theme links to these pages from the homepage, header search, CTAs, and menus. Property Search and Lists pages are not embedded as homepage sections — users navigate to them via search and menu links.
     151
     152= What stays in the plugin =
     153
     154Property Builder layouts, archive filter sidebar configuration, Contact Agent forms, AJAX search logic, map providers, import wizard, and single-property widgets remain plugin responsibilities. The theme does not modify plugin code.
     155
    97156== Frequently Asked Questions ==
    98157
    99158= Do I need the Havenlytics plugin? =
    100159
    101 No. Havenlytics Realty works as a standalone WordPress theme for blogs and business sites. For property listings, search, agents, agencies, and the real estate homepage, install the free [Havenlytics plugin](https://wordpress.org/plugins/havenlytics/) — the theme is built as its official companion.
     160No. Havenlytics Realty works as a standalone WordPress theme. For property listings, search, agents, agencies, and the real estate homepage, install the free [Havenlytics plugin](https://wordpress.org/plugins/havenlytics/) — the theme is built as its official companion.
     161
     162= Where is the live demo? =
     163
     164[https://demo.havenlytics.com/](https://demo.havenlytics.com/) — Havenlytics Realty with the Havenlytics plugin and demo content.
    102165
    103166= How do I launch a real estate homepage? =
    … …  
    109172No. All integration is theme-side: shortcodes, CSS harmonization, body classes, breadcrumbs, and admin onboarding. The plugin continues to work with any WordPress theme.
    110173
    111 = Does this theme support plugins? =
    112 
    113 Yes. Havenlytics Realty integrates with the Havenlytics property plugin, Contact Form 7, Jetpack, and Elementor.
     174= How do I search for properties from the homepage? =
     175
     176Click the **search icon** in the header to open the property search panel, or use the **Call to Action** section and menu links to the Property Search page.
     177
     178= Does this theme support Elementor? =
     179
     180Yes. Havenlytics Realty supports Elementor full-width layouts. The Havenlytics plugin also provides Elementor widgets for property archives, agents, and agencies.
    114181
    115182= Is this theme accessibility ready? =
    … …  
    119186= How do I change the primary color? =
    120187
    121 Go to **Appearance → Customize → Havenlytics Theme Settings → Global Design System**.
     188Go to **Appearance → Customize → Havenlytics Theme Settings → Global Design System**. Colors also apply to Havenlytics plugin pages when both are active.
    122189
    123190= How do I customize the real estate homepage? =
    124191
    125 With the Havenlytics plugin active, go to **Appearance → Customize → Real Estate Homepage** to edit the hero, listing counts, CTA text, and section visibility.
    126 
    127 = How do I change blog columns or posts per page? =
    128 
    129 Go to **Appearance → Customize → Havenlytics Theme Settings → Layout Settings**.
     192With the Havenlytics plugin active, go to **Appearance → Customize → Real Estate Homepage** to edit section visibility, hero map height, listing counts, department filters, CTA text, and more.
    130193
    131194== Screenshots ==
    132195
    133 1. Real estate homepage with hero search and featured property listings (Havenlytics plugin active)
    134 2. Property single page with theme header and styled listing details
    135 3. Property search page with filters and results grid
    136 4. Agents directory grid page
    137 5. Customizer — Real Estate Homepage settings (hero, counts, section toggles)
    138 6. Mobile homepage with responsive search and listing cards
     196Recommended WordPress.org screenshot set (accurate to the product):
     197
     1981. **Homepage** — Hero map, featured properties, and department tabs ([live demo](https://demo.havenlytics.com/))
     1992. **Header search panel** — Expanded property search modal with filters
     2003. **Property single** — Gallery, details, and Contact Agent (plugin) with theme header
     2014. **Property search page** — Grid/list/map views and filter sidebar (plugin page)
     2025. **Agents directory** — Agent cards grid (plugin page or homepage section)
     2036. **Customizer** — Real Estate Homepage panel with section controls
     2047. **Appearance → Realty** — Onboarding hub with setup checklist
     2058. **Mobile homepage** — Responsive map hero and listing cards
    139206
    140207== Changelog ==
     208
     209= 1.15.5 - June 8, 2026 =
     210* Premium desktop dropdown styling: soft fade + translateY reveal, elevated panel shadow, improved spacing and typography
     211* Removed submenu label shift on hover (no padding-left jump); stable hover and focus highlights
     212* Fixed mobile dropdown arrow anchoring so the toggle stays fixed while submenus expand underneath
     213* Mobile submenu open/close now uses max-height and opacity (no display toggle or sliding keyframes)
     214* Submenu state resets when the mobile menu closes
     215
     216= 1.15.4 - June 8, 2026 =
     217* Fixed desktop dropdown menus closing when moving the pointer from parent item to submenu
     218* Added hover bridge and open-state handling for more reliable desktop submenu targeting
     219* Improved desktop dropdown transitions, submenu link hit area, and aria-expanded keyboard support
     220* Improved mobile submenu toggle indicators, aria-controls wiring, and expand/collapse behavior
     221* Navigation accessibility refinements for focus, Escape, and screen reader states
     222
     223= 1.15.3 - June 8, 2026 =
     224* Synchronized mobile off-canvas menu branding with Appearance → Customize → Site Identity custom logo
     225* Added Customizer selective refresh and live preview support for the mobile menu logo
     226* Improved desktop navigation dropdown transitions, hover states, focus states, and active submenu styles
     227* Improved mobile menu spacing, tap targets, submenu toggle controls, and visual hierarchy
     228* Responsive refinements for header logo sizing, homepage cards, department tabs, and footer columns (320px–1440px)
     229
     230= 1.15.2 - June 8, 2026 =
     231* Fixed hero map height Customizer controls (desktop and mobile vh) overriding plugin fixed map heights
     232* Fixed homepage Customizer inline CSS attaching to the correct stylesheet handle
     233* Department section footer button text and URL Customizer controls
     234* Expanded header property search panel with bedrooms, bathrooms, and price filters; smoother modal animation
     235* WordPress.org readme and product description accuracy improvements
    141236
    142237= 1.15.0 - June 8, 2026 =
    … …  
    154249* Phase 5: WordPress.org companion positioning — readme, installation guide, FAQ, and screenshot list
    155250* Updated theme description for Havenlytics plugin pairing
    156 * TGMPA recommended-plugin notice with Getting Started guidance
    157251* Font credits updated (Inter, Poppins)
    158252
    … …  
    169263* Manual theme setup trigger when demo is imported but homepage not configured
    170264* Welcome and launch notices link to Getting Started page
    171 * Getting Started link added on Themes screen
    172265
    173266= 1.9.0 - June 8, 2026 =
    … …  
    180273
    181274= 1.8.0 - June 8, 2026 =
    182 * Added Real Estate Homepage template with 10 sections (hero search, featured/latest listings, categories, agents, agencies, stats, CTAs, blog)
     275* Added Real Estate Homepage template with homepage sections (hero map, featured listings, departments, locations, agents, agencies, blog, CTA)
    183276* Added theme launch pack: auto-creates Home page, Primary menu, and front page assignment after demo import
    184277* Added Havenlytics integration module (helpers, shortcode wrappers, compat CSS)
    … …  
    187280= 1.7.4 - June 8, 2026 =
    188281* Fixed Havenlytics plugin detection (HvnlyNab class) and hvnly_property CPT integration
    189 * Header search icon links to the Havenlytics Property Search page when the plugin is active
     282* Header search links to the Havenlytics Property Search page when the plugin is active
    190283* Property archives no longer receive blog grid body classes
    191284* Added dismissible admin welcome notice after theme activation
    192285* Added read-only plugin page ID helper for theme integration without modifying plugin code
    193286
    194 = 1.7.3 - June 8, 2026 =
    195 * Fixed blog pagination double-box styling (removed duplicate CSS + inner span wrappers)
    196 * Pages and singles use full container width by default; sidebar grid only when sidebar is active
    197 * Removed unnecessary layout-row markup when no sidebar is assigned
    198 * Added animated hover effects for blog post cards
    199 * Elementor full-width reset scoped to #primary only — header/footer containers stay boxed
    200 
    201 = 1.7.2 - June 8, 2026 =
    202 * Reordered Customizer: Global Design System, Site Identity, Header, Typography, Layout, Footer
    203 * Removed header/background image sections and custom-background theme support
    204 * Fixed Posts Per Row grid on frontend (legacy layout CSS conflict + body/grid class collision)
    205 * Blog column CSS now matches Customizer preview selectors on saved frontend output
    206 
    207 = 1.6.0 - June 8, 2026 =
    208 * Architecture: Full template rebuild — isolated page, blog, and single layouts (Astra-style)
    209 * Removed: Search modal overlay (HTML, CSS, JS); header search icon now links to /?s= search page
    210 * Fixed: Page layout no longer force-centers content at 900px; full container width by default
    211 * Added: single.css loaded only on single posts; unified optional sidebar module (.hvn-theme-sidebar-area)
    212 * Fixed: Elementor pages get full-width container reset without breaking standard pages
    213 * CSS: Strict file split — page.css, blog.css, single.css, layouts.css (shared utilities only)
    214 
    215 = 1.5.1 - June 8, 2026 =
    216 * Refactor: Complete page/blog separation — dedicated `hvn-page-*` markup and `page.css` / `blog.css` loaded per context
    217 * Added: Conditional stylesheet enqueue (pages + 404 → page.css; blog/archive/search → blog.css)
    218 * Added: Body class `hvn-view-page` for static pages and 404
    219 * Fixed: Archive titles use `hvn-blog-title` instead of shared page title class
    220 * Fixed: 404 template uses page shell markup; inline blog grid CSS attaches to blog handle only
    221 
    222 = 1.5.0 - June 8, 2026 =
    223 * Refactor: Isolated layout system — blog, page, and single post use separate wrappers and CSS
    224 * Fixed: Removed global 12-column grid leakage from page/single/blog templates
    225 * Fixed: Sidebar default is now none; only renders when Customizer enables it
    226 * Added: layouts.css with scoped blog grid, page (900px), single (800px), pagination, breadcrumbs
    227 * Improved: Breadcrumb SEO schema (JSON-LD BreadcrumbList) and Home > Blog > Category > Post trail
    228 
    229 = 1.4.0 - June 8, 2026 =
    230 * Fixed: Typography system — Inter (body) + Poppins (headings) with optimized Google Fonts loading
    231 * Fixed: Mobile header navigation (hamburger menu, desktop nav hide/show)
    232 * Fixed: Header CTA visibility on desktop; CTA added to mobile drawer
    233 * Fixed: Blog grid cards, single post readability, search page, and default page layouts
    234 * Fixed: Footer widget columns, spacing, and bottom bar alignment
    235 * Added: Production polish stylesheet (assets/css/polish.css) for responsive and UI consistency
    236 
    237 = 1.3.1 - June 8, 2026 =
    238 * Added: Core WP Customizer sections (Site Identity, Header Image, Background) moved into Havenlytics Theme Settings panel
    239 * Added: Block editor design token output matching frontend Customizer values
    240 * Added: Selective refresh for copyright and header CTA
    241 * Added: Live preview for typography fonts, blog layout, container mode, and header toggles
    242 * Added: Property archive/single plugin hook calls in templates
    243 * Fixed: Footer widget text/title colors respect Customizer footer color settings
    244 
    245 = 1.3.0 - June 8, 2026 =
    246 * Added: Modular Customizer architecture (inc/customizer/)
    247 * Added: Typography section — font families, line height, heading scale
    248 * Added: Border color, transparent header, CTA toggle/link, footer link color, back-to-top
    249 * Added: Container width mode (boxed/full) and plugin-ready hooks (inc/plugin-hooks.php)
    250 * Changed: Dynamic Google Fonts loading from Customizer typography choices
    251 
    252 = 1.2.0 - June 8, 2026 =
    253 * Added: Unified Customizer panel "Havenlytics Theme Settings" (Global Design, Header, Footer, Layout)
    254 * Refactor: Modern header markup (hvn-theme-header, hvn-theme-nav, hvn-theme-actions)
    255 * Refactor: Modern footer markup (hvn-theme-footer, hvn-cols-{n}, hvn-theme-footer-bottom)
    256 * Added: Header layout options (1–3), search toggle, CTA button text
    257 * Added: Footer column count, footer background/text colors via CSS variables
    258 * Changed: CSS consolidated to theme.css and editor.css only; property styles merged into theme.css
    259 
    260 = 1.1.0 - June 8, 2026 =
    261 * Refactor: Merged fragmented CSS into assets/css/theme.css
    262 * Added: Havenlytics Global Settings customizer section with design tokens
    263 * Added: Elementor support, theme.json (FSE-ready), property.css placeholder
    264 * Added: Header/footer template parts; vanilla JS theme.js (no jQuery)
    265 * Changed: Footer widgets expanded to 4 columns
    266 * Changed: Sticky header is now optional via customizer
    267 
    268 = 1.0.5 - May 26, 2026 =
    269 * Fixed: CSS small issues with blog columns and mobile menu
    270 
    271 = 1.0.4 - May 26, 2026 =
    272 * Added: Added TGMPA plugin activation for recommended plugins (Havenlytics Core Plugin)
    273 
    274 = 1.0.3 - May 15, 2026 =
    275 * Fixed: Removed prohibited tags (blog, real-estate, property-listings, directory) from style.css header
    276 * Fixed: Added underline styling for all content links (posts, excerpts, comments, text widgets) to meet WordPress.org requirements
    277 * Fixed: Removed direct wp_enqueue_script('jquery') and now loading jQuery as dependency only
    278 * Fixed: Added default header image path in custom-header.php
    279 * Fixed: Removed missing accessibility.js file reference from functions.php
    280 * Improved: Code quality and WordPress.org theme review standards compliance
    281 
    282 = 1.0.2 =
    283 * Added: Primary menu fallback when no menu is assigned
    284 * Added: Mobile menu fallback for better user experience
    285 * Fixed: Blog columns CSS override issues
    286 * Improved: Mobile menu keyboard navigation
    287 * Improved: Accessibility enhancements
    288 
    289 = 1.0.1 =
    290 * Added: Professional breadcrumb navigation with schema markup
    291 * Added: Custom blog columns setting (1-4 columns)
    292 * Added: Posts per page control in Customizer
    293 * Added: Ignore sticky posts option
    294 * Added: Scroll to top button with animation
    295 * Added: Customizer layout options section
    296 * Improved: Blog grid and list layout options
    297 * Improved: Sticky sidebar functionality
    298 
    299 = 1.0.0 =
    300 * Initial release
    301 * Full accessibility support
    302 * Keyboard navigation
    303 * Focus styles
    304 * WordPress.org standards compliance
    305 * Custom block styles and patterns
    306 * Sticky sidebar
    307 * Modern pagination design
    308 * Mobile-responsive hamburger menu
    309 * Customizable blog columns (1-4 columns)
    310 * Posts per page control
    311 * Ignore sticky posts option
    312 * Professional breadcrumb navigation
    313 * Scroll to top button with pulse animation
    314 * Modern widget styling
    315 * Customizer controls for layout options
    316 
    317287== Credits ==
    318288
    … …  
    330300All images used in the theme screenshot and demo content are from free stock photo websites with GPL-compatible licenses.
    331301
    332 == Image for theme screenshot ==
    333 
    334 License: CC0 1.0 Universal (CC0 1.0)
    335 License URL: https://pxhere.com/en/license
    336 Source: https://pxhere.com/en/photo/979977
    337 
    338 License: CC0 1.0 Universal (CC0 1.0)
    339 License URL: https://pxhere.com/en/license
    340 Source: https://pxhere.com/en/photo/709518 
    341 
    342 License: CC0 1.0 Universal (CC0 1.0)
    343 License URL: https://pxhere.com/en/license
    344 Source: https://pxhere.com/en/photo/15020
    345 
    346 License: CC0 1.0 Universal (CC0 1.0)
    347 License URL: https://pxhere.com/en/license
    348 Source: https://pxhere.com/en/photo/1284562
    349 
    350 License: CC0 1.0 Universal (CC0 1.0)
    351 License URL: https://pxhere.com/en/license
    352 Source: https://pxhere.com/en/photo/966576
    353 
    354 License: CC0 1.0 Universal (CC0 1.0)
    355 License URL: https://pxhere.com/en/license
    356 Source: https://pxhere.com/en/photo/1094638
    357 
    358 = SVG Icons =
    359 
    360 Custom SVG icons used in the theme (scroll to top, search, menu, breadcrumb home) are original creations by the theme author and are licensed under GPLv2 or later.
    361 
    362302== Additional Information ==
    363303
    … …  
    365305Havenlytics Realty Theme is distributed under the terms of the GNU GPL.
    366306
    367 * Theme: https://wordpress.org/themes/havenlytics-realty/
    368 * Plugin: https://wordpress.org/plugins/havenlytics/
    369 * Author: https://profiles.wordpress.org/havenlytics/
     307* **Live demo:** https://demo.havenlytics.com/
     308* **Theme:** https://wordpress.org/themes/havenlytics-realty/
     309* **Plugin:** https://wordpress.org/plugins/havenlytics/
     310* **Author:** https://profiles.wordpress.org/havenlytics/
    370311
    371312== Upgrade Notice ==
     313
     314= 1.15.5 =
     315Premium navigation interaction polish: desktop dropdown animation and styling, fixed mobile arrow positioning, smoother submenu expand. No breaking changes.
     316
     317= 1.15.4 =
     318Navigation improvements: reliable desktop dropdowns, mobile submenu usability, and accessibility refinements. No breaking changes.
     319
     320= 1.15.3 =
     321Logo synchronization, navigation polish, mobile menu usability, and responsive refinements. No breaking changes.
     322
     323= 1.15.2 =
     324Hero map height fix, header search panel improvements, department button Customizer controls, and readme accuracy updates. No breaking changes.
    372325
    373326= 1.15.0 =
    … …  
    376329= 1.12.0 =
    377330WordPress.org companion positioning update. Readme, onboarding guide, and Havenlytics plugin pairing documentation improved. No breaking changes.
    378 
    379 = 1.11.0 =
    380 Adds Real Estate Homepage Customizer controls when the Havenlytics plugin is active.
    381 
    382 = 1.0.3 =
    383 This release includes important fixes for WordPress.org theme review compliance. Please update to ensure your theme meets all requirements.
    384 
    385 = 1.0.0 =
    386 Initial release. A modern, accessible real estate blog theme with full customizer controls, multiple layout options, and professional breadcrumb navigation.
  • havenlytics-realty/1.15.5/style.css

    r334339 r334420  
    44Author: Havenlytics
    55Author URI: https://profiles.wordpress.org/havenlytics/
    6 Description: Official companion theme for the free Havenlytics plugin. Launch a real estate website with property search, listings, and agents — or use as a lightweight, accessible blog and business theme.
    7 Version: 1.15.0
    8 Tested up to: 6.9
     6Description: Official companion theme for the free Havenlytics plugin — build a real estate, realtor, or estate agency website with property listings, map search, agent directories, and agency directories. Live demo: https://demo.havenlytics.com/
     7Version: 1.15.5
     8Requires at least: 6.0
     9Tested up to: 7.0
    910Requires PHP: 7.4
    1011License: GPLv2 or later
    … …  
    5960
    6061
     62
    6163/* Required WordPress Classes */
    6264.wp-caption {
    … …  
    258260    display: inline-block;
    259261}
     262
     263
  • havenlytics-realty/1.15.5/template-parts/header/mobile-menu.php

    r334339 r334420  
    88<div class="hvn-theme-mobile-menu" id="hvn-mobile-menu" aria-hidden="true">
    99        <div class="hvn-theme-mobile-menu-header">
    10                 <div class="hvn-theme-mobile-menu-title">
    11                         <?php echo esc_html( get_bloginfo( 'name' ) ); ?>
     10                <div class="hvn-theme-mobile-menu-branding">
     11                        <?php get_template_part( 'template-parts/header/branding' ); ?>
    1212                </div>
    1313                <button class="hvn-theme-mobile-menu-close" type="button" aria-label="<?php esc_attr_e( 'Close Menu', 'havenlytics-realty' ); ?>">
  • havenlytics-realty/1.15.5/template-parts/header/navigation.php

    r334339 r334420  
    99        <?php
    1010        if ( has_nav_menu( 'primary' ) ) {
     11                $walker = function_exists( 'hvn_realty_get_nav_menu_walker' ) ? hvn_realty_get_nav_menu_walker() : null;
     12
    1113                wp_nav_menu(
    1214                        array(
    … …  
    1719                                'fallback_cb'    => '__return_false',
    1820                                'depth'          => 3,
    19                                 'walker'         => new HVN_Realty_Walker_Nav_Menu(),
     21                                'walker'         => $walker,
    2022                        )
    2123                );
  • havenlytics-realty/1.15.5/template-parts/header/property-search-panel.php

    r334339 r334420  
    11<?php
     2
    23/**
     4
    35 * Header property search panel (overlay form).
     6
    47 *
     8
    59 * @package Havenlytics_Realty
     10
    611 */
    712
     13
     14
    815if ( ! defined( 'ABSPATH' ) ) {
     16
    917        exit;
     18
    1019}
    1120
     21
     22
    1223if ( ! function_exists( 'hvn_realty_use_header_property_search_panel' ) || ! hvn_realty_use_header_property_search_panel() ) {
     24
    1325        return;
     26
    1427}
    1528
     29
     30
    1631$search_url     = hvn_realty_get_property_search_url();
     32
    1733$property_types = hvn_realty_get_header_search_terms( 'hvnly_prop_types' );
     34
    1835$locations      = hvn_realty_get_header_search_terms( 'hvnly_prop_locations' );
     36
     37$count_options  = hvn_realty_get_search_count_select_options();
     38
    1939?>
     40
    2041<div id="hvn-property-search-panel" class="hvn-theme-property-search-panel" aria-hidden="true" hidden>
     42
    2143        <div class="hvn-theme-property-search-panel__overlay" data-hvn-search-close tabindex="-1" aria-hidden="true"></div>
     44
    2245        <div class="hvn-theme-property-search-panel__dialog" role="dialog" aria-modal="true" aria-labelledby="hvn-property-search-panel-title">
     46
    2347                <button type="button" class="hvn-theme-property-search-panel__close" data-hvn-search-close aria-label="<?php esc_attr_e( 'Close search', 'havenlytics-realty' ); ?>">
     48
    2449                        <span aria-hidden="true">&times;</span>
     50
    2551                </button>
    2652
     53
     54
    2755                <div class="hvn-theme-property-search-panel__inner">
     56
    2857                        <p class="hvn-theme-property-search-panel__eyebrow"><?php esc_html_e( 'Property search', 'havenlytics-realty' ); ?></p>
     58
    2959                        <h2 id="hvn-property-search-panel-title" class="hvn-theme-property-search-panel__title">
     60
    3061                                <?php esc_html_e( 'Find your next property', 'havenlytics-realty' ); ?>
     62
    3163                        </h2>
    3264
     65
     66
    3367                        <form class="hvn-theme-property-search-panel__form" method="get" action="<?php echo esc_url( $search_url ); ?>">
     68
    3469                                <div class="hvn-theme-property-search-panel__field">
     70
    3571                                        <label for="hvn-header-address-keyword"><?php esc_html_e( 'Keyword or address', 'havenlytics-realty' ); ?></label>
     72
    3673                                        <input
     74
    3775                                                type="search"
     76
    3877                                                class="hvn-theme-form-control"
     78
    3979                                                id="hvn-header-address-keyword"
     80
    4081                                                name="address_keyword"
     82
    4183                                                autocomplete="off"
     84
    4285                                                placeholder="<?php esc_attr_e( 'City, neighborhood, or address', 'havenlytics-realty' ); ?>"
     86
    4387                                        />
    44                                 </div>
     88
     89                                </div>
     90
     91
    4592
    4693                                <div class="hvn-theme-property-search-panel__row">
    47                                         <div class="hvn-theme-property-search-panel__field">
     94
     95                                        <div class="hvn-theme-property-search-panel__field">
     96
    4897                                                <label for="hvn-header-property-type"><?php esc_html_e( 'Property type', 'havenlytics-realty' ); ?></label>
     98
    4999                                                <select class="hvn-theme-form-control" id="hvn-header-property-type" name="property_type">
     100
    50101                                                        <option value=""><?php esc_html_e( 'Any type', 'havenlytics-realty' ); ?></option>
     102
    51103                                                        <?php foreach ( $property_types as $term ) : ?>
     104
    52105                                                                <option value="<?php echo esc_attr( $term->slug ); ?>"><?php echo esc_html( $term->name ); ?></option>
    53                                                         <?php endforeach; ?>
    54                                                 </select>
    55                                         </div>
    56 
    57                                         <div class="hvn-theme-property-search-panel__field">
     106
     107                                                        <?php endforeach; ?>
     108
     109                                                </select>
     110
     111                                        </div>
     112
     113
     114
     115                                        <div class="hvn-theme-property-search-panel__field">
     116
    58117                                                <label for="hvn-header-location"><?php esc_html_e( 'Location', 'havenlytics-realty' ); ?></label>
     118
    59119                                                <select class="hvn-theme-form-control" id="hvn-header-location" name="location">
     120
    60121                                                        <option value=""><?php esc_html_e( 'Any location', 'havenlytics-realty' ); ?></option>
     122
    61123                                                        <?php foreach ( $locations as $term ) : ?>
     124
    62125                                                                <option value="<?php echo esc_attr( $term->slug ); ?>"><?php echo esc_html( $term->name ); ?></option>
    63                                                         <?php endforeach; ?>
    64                                                 </select>
    65                                         </div>
    66                                 </div>
     126
     127                                                        <?php endforeach; ?>
     128
     129                                                </select>
     130
     131                                        </div>
     132
     133                                </div>
     134
     135
     136
     137                                <div class="hvn-theme-property-search-panel__row">
     138
     139                                        <div class="hvn-theme-property-search-panel__field">
     140
     141                                                <label for="hvn-header-bedrooms"><?php esc_html_e( 'Bedrooms', 'havenlytics-realty' ); ?></label>
     142
     143                                                <select class="hvn-theme-form-control" id="hvn-header-bedrooms" name="bedrooms">
     144
     145                                                        <?php foreach ( $count_options as $value => $label ) : ?>
     146
     147                                                                <option value="<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $label ); ?></option>
     148
     149                                                        <?php endforeach; ?>
     150
     151                                                </select>
     152
     153                                        </div>
     154
     155
     156
     157                                        <div class="hvn-theme-property-search-panel__field">
     158
     159                                                <label for="hvn-header-bathrooms"><?php esc_html_e( 'Bathrooms', 'havenlytics-realty' ); ?></label>
     160
     161                                                <select class="hvn-theme-form-control" id="hvn-header-bathrooms" name="bathrooms">
     162
     163                                                        <?php foreach ( $count_options as $value => $label ) : ?>
     164
     165                                                                <option value="<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $label ); ?></option>
     166
     167                                                        <?php endforeach; ?>
     168
     169                                                </select>
     170
     171                                        </div>
     172
     173                                </div>
     174
     175
     176
     177                                <div class="hvn-theme-property-search-panel__row">
     178
     179                                        <div class="hvn-theme-property-search-panel__field">
     180
     181                                                <label for="hvn-header-min-price"><?php esc_html_e( 'Min price', 'havenlytics-realty' ); ?></label>
     182
     183                                                <input
     184
     185                                                        type="number"
     186
     187                                                        class="hvn-theme-form-control"
     188
     189                                                        id="hvn-header-min-price"
     190
     191                                                        name="min_price"
     192
     193                                                        min="0"
     194
     195                                                        step="1"
     196
     197                                                        inputmode="numeric"
     198
     199                                                        placeholder="<?php esc_attr_e( 'No min', 'havenlytics-realty' ); ?>"
     200
     201                                                />
     202
     203                                        </div>
     204
     205
     206
     207                                        <div class="hvn-theme-property-search-panel__field">
     208
     209                                                <label for="hvn-header-max-price"><?php esc_html_e( 'Max price', 'havenlytics-realty' ); ?></label>
     210
     211                                                <input
     212
     213                                                        type="number"
     214
     215                                                        class="hvn-theme-form-control"
     216
     217                                                        id="hvn-header-max-price"
     218
     219                                                        name="max_price"
     220
     221                                                        min="0"
     222
     223                                                        step="1"
     224
     225                                                        inputmode="numeric"
     226
     227                                                        placeholder="<?php esc_attr_e( 'No max', 'havenlytics-realty' ); ?>"
     228
     229                                                />
     230
     231                                        </div>
     232
     233                                </div>
     234
     235
    67236
    68237                                <input type="hidden" name="view_type" value="grid" />
     238
    69239                                <input type="hidden" name="orderby" value="date" />
     240
    70241                                <input type="hidden" name="paged" value="1" />
    71242
     243
     244
    72245                                <div class="hvn-theme-property-search-panel__actions">
     246
    73247                                        <button type="submit" class="hvn-theme-btn hvn-theme-btn-primary hvn-theme-property-search-panel__submit">
     248
    74249                                                <?php esc_html_e( 'Search properties', 'havenlytics-realty' ); ?>
     250
    75251                                        </button>
    76                                 </div>
     252
     253                                </div>
     254
    77255                        </form>
     256
    78257                </div>
     258
    79259        </div>
     260
    80261</div>
     262
     263
  • havenlytics-realty/1.15.5/template-parts/home/department-tabs.php

    r334339 r334420  
    1919$columns     = 3;
    2020
    21 $search_url  = hvn_realty_get_property_search_url();
     21$view_all_url  = hvn_realty_get_home_department_button_url();
     22$view_all_text = hvn_realty_get_home_department_button_text();
    2223
    2324$departments = hvn_realty_get_property_departments();
    … …  
    212213                <footer class="hvn-realty-section__footer">
    213214
    214                         <a class="hvn-realty-btn hvn-realty-btn--outline" href="<?php echo esc_url( $search_url ); ?>">
    215 
    216                                 <?php esc_html_e( 'View All Properties', 'havenlytics-realty' ); ?>
     215                        <a id="hvn-realty-dept-view-all" class="hvn-realty-btn hvn-realty-btn--outline" href="<?php echo esc_url( $view_all_url ); ?>">
     216
     217                                <?php echo esc_html( $view_all_text ); ?>
    217218
    218219                        </a>
  • havenlytics-realty/1.15.5/templates/template-realty-home.php

    r334339 r334420  
    1111
    1212<main id="primary" class="hvn-realty-home">
    13         <?php hvn_realty_render_homepage_sections(); ?>
     13        <?php
     14        if ( function_exists( 'hvn_realty_render_homepage_sections' ) ) {
     15                hvn_realty_render_homepage_sections();
     16        }
     17        ?>
    1418</main>
    1519
Note: See TracChangeset for help on using the changeset viewer.