feat: Enhance gallery functionality with lightbox feature and improve icon sprite accessibility
- Added a lightbox feature for gallery images, allowing users to view photos in a modal with navigation controls. - Updated the SVG sprite to include inline styles for better accessibility. - Increased CSS specificity by doubling the `.vc-app` class to ensure styles override Astra theme defaults. - Introduced new shortcodes for bus tours, about section, and articles in the VC_Sections class. - Improved the handling of featured images in the single tour template for sharper display. - Updated version number to 1.8.15.
This commit is contained in:
Binary file not shown.
@@ -421,6 +421,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hoho .ring-badge--top .ring-badge__text {
|
.hoho .ring-badge--top .ring-badge__text {
|
||||||
|
fill: #808080;
|
||||||
font-size: 0.625rem;
|
font-size: 0.625rem;
|
||||||
letter-spacing: 0.02em;
|
letter-spacing: 0.02em;
|
||||||
}
|
}
|
||||||
@@ -546,15 +547,26 @@
|
|||||||
box-shadow: 0 1.25rem 2.5rem rgba(0, 0, 0, 0.18);
|
box-shadow: 0 1.25rem 2.5rem rgba(0, 0, 0, 0.18);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transform: translateY(-0.375rem);
|
transform: translateY(-0.4rem) scale(0.97);
|
||||||
transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), visibility 0.2s;
|
transform-origin: top center;
|
||||||
|
/* Exit: fade/slide out, then flip visibility only once the fade is done, so
|
||||||
|
the close animates instead of snapping. */
|
||||||
|
transition:
|
||||||
|
opacity 0.16s var(--ease),
|
||||||
|
transform 0.16s var(--ease),
|
||||||
|
visibility 0s linear 0.16s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hoho .nav__item--has-menu:hover .nav__submenu,
|
.hoho .nav__item--has-menu:hover .nav__submenu,
|
||||||
.hoho .nav__toggle[aria-expanded="true"]+.nav__submenu {
|
.hoho .nav__toggle[aria-expanded="true"]+.nav__submenu {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transform: translateY(0);
|
transform: translateY(0) scale(1);
|
||||||
|
/* Enter: a slightly longer, soft ease-out so it settles in gently. */
|
||||||
|
transition:
|
||||||
|
opacity 0.24s var(--ease),
|
||||||
|
transform 0.28s cubic-bezier(0.16, 1, 0.3, 1),
|
||||||
|
visibility 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hoho .nav__submenu a {
|
.hoho .nav__submenu a {
|
||||||
@@ -1868,14 +1880,24 @@
|
|||||||
box-shadow: 0 1.5rem 3rem rgba(0, 0, 0, 0.22);
|
box-shadow: 0 1.5rem 3rem rgba(0, 0, 0, 0.22);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transform: translateY(-0.5rem);
|
transform: translateY(-0.6rem) scale(0.98);
|
||||||
transition: opacity 0.25s var(--ease), transform 0.25s var(--ease), visibility 0.25s;
|
transform-origin: top right;
|
||||||
|
/* Exit: fade/slide up, then hide once the fade completes. */
|
||||||
|
transition:
|
||||||
|
opacity 0.18s var(--ease),
|
||||||
|
transform 0.18s var(--ease),
|
||||||
|
visibility 0s linear 0.18s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hoho .nav.is-open {
|
.hoho .nav.is-open {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transform: translateY(0);
|
transform: translateY(0) scale(1);
|
||||||
|
/* Enter: soft ease-out settle. */
|
||||||
|
transition:
|
||||||
|
opacity 0.26s var(--ease),
|
||||||
|
transform 0.3s cubic-bezier(0.16, 1, 0.3, 1),
|
||||||
|
visibility 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hoho .nav__list {
|
.hoho .nav__list {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<svg class="sprite" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg">
|
<svg class="sprite" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" style="position:absolute;width:0;height:0;overflow:hidden">
|
||||||
<defs>
|
<defs>
|
||||||
<path id="ringPathScroll" fill="none" d="M50,50 m-39,0 a39,39 0 1,1 78,0 a39,39 0 1,1 -78,0" />
|
<path id="ringPathScroll" fill="none" d="M50,50 m-39,0 a39,39 0 1,1 78,0 a39,39 0 1,1 -78,0" />
|
||||||
</defs>
|
</defs>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@@ -23,10 +23,10 @@ add_action( 'wp_enqueue_scripts', function () {
|
|||||||
if ( wp_style_is( 'astra-theme-css', 'registered' ) ) {
|
if ( wp_style_is( 'astra-theme-css', 'registered' ) ) {
|
||||||
$deps[] = 'astra-theme-css';
|
$deps[] = 'astra-theme-css';
|
||||||
}
|
}
|
||||||
wp_enqueue_style( 'hoho-chrome', get_stylesheet_directory_uri() . '/assets/chrome.css', $deps, '1.0.2' );
|
wp_enqueue_style( 'hoho-chrome', get_stylesheet_directory_uri() . '/assets/chrome.css', $deps, '1.0.8' );
|
||||||
|
|
||||||
// Header interactions: burger, submenu, sticky, back-to-top, search toggle.
|
// Header interactions: burger, submenu, sticky, back-to-top, search toggle.
|
||||||
wp_enqueue_script( 'hoho-chrome', get_stylesheet_directory_uri() . '/assets/chrome.js', array(), '1.0.2', true );
|
wp_enqueue_script( 'hoho-chrome', get_stylesheet_directory_uri() . '/assets/chrome.js', array(), '1.0.8', true );
|
||||||
|
|
||||||
wp_add_inline_style(
|
wp_add_inline_style(
|
||||||
'hoho-chrome',
|
'hoho-chrome',
|
||||||
@@ -40,9 +40,17 @@ add_action( 'wp_enqueue_scripts', function () {
|
|||||||
'.hoho,.hoho h1,.hoho h2,.hoho h3,.hoho p,.hoho a,.hoho span,.hoho li,.hoho button,.hoho input{font-family:"Plus Jakarta Sans",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}',
|
'.hoho,.hoho h1,.hoho h2,.hoho h3,.hoho p,.hoho a,.hoho span,.hoho li,.hoho button,.hoho input{font-family:"Plus Jakarta Sans",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}',
|
||||||
// Normalize: stop Astra decorating header/footer links & lists.
|
// Normalize: stop Astra decorating header/footer links & lists.
|
||||||
'.hoho a,.hoho.hoho a{text-decoration:none!important;box-shadow:none!important;}',
|
'.hoho a,.hoho.hoho a{text-decoration:none!important;box-shadow:none!important;}',
|
||||||
'.hoho a:not(.btn){color:inherit;}',
|
// …but not ring-badges: they set their own colour on the <a>, and this
|
||||||
|
// inherit rule out-specifies .ring-badge--top, turning the footer
|
||||||
|
// "back to top" text white (inherited) on the light area above it.
|
||||||
|
'.hoho a:not(.btn):not(.ring-badge){color:inherit;}',
|
||||||
'.hoho :is(ul,ol){list-style:none!important;margin:0!important;padding:0!important;}',
|
'.hoho :is(ul,ol){list-style:none!important;margin:0!important;padding:0!important;}',
|
||||||
'.hoho li{margin:0!important;}',
|
'.hoho li{margin:0!important;}',
|
||||||
|
// Let the footer\'s decorative bits (back-to-top ring, support tab)
|
||||||
|
// spill vertically, but keep the horizontal axis clipped so nothing
|
||||||
|
// causes a mobile sideways scroll. overflow-x:clip (unlike hidden)
|
||||||
|
// does not force overflow-y to auto, so the y-axis stays visible.
|
||||||
|
'.hoho,.hoho .footer{overflow-x:clip;overflow-y:visible;}',
|
||||||
// Icon buttons: Astra gives <button> a filled background + its own
|
// Icon buttons: Astra gives <button> a filled background + its own
|
||||||
// colour, which turned the header search icon into an empty dark
|
// colour, which turned the header search icon into an empty dark
|
||||||
// circle. Reset them (real .btn buttons keep their styling).
|
// circle. Reset them (real .btn buttons keep their styling).
|
||||||
@@ -51,9 +59,11 @@ add_action( 'wp_enqueue_scripts', function () {
|
|||||||
'.hoho button:not(.btn):not(.burger){background:transparent;border:0;color:inherit;}',
|
'.hoho button:not(.btn):not(.burger){background:transparent;border:0;color:inherit;}',
|
||||||
// Expanding header search field.
|
// Expanding header search field.
|
||||||
'.hoho .header__search{display:flex;align-items:center;gap:.25rem;}',
|
'.hoho .header__search{display:flex;align-items:center;gap:.25rem;}',
|
||||||
'.hoho .header__search-input{width:0;min-width:0;padding:0;border:0;outline:0;opacity:0;height:var(--control-h);border-radius:var(--r-pill);background:rgba(255,255,255,.15);color:#fff;transition:width .25s var(--ease),opacity .2s var(--ease),padding .25s var(--ease);}',
|
// !important beats Astra's input[type=search] styling, which forces a
|
||||||
|
// white field background (and, with a white placeholder, hid the text).
|
||||||
|
'.hoho .header__search-input{width:0;min-width:0;padding:0;border:0!important;outline:0;opacity:0;height:var(--control-h);border-radius:var(--r-pill)!important;background:rgba(255,255,255,.15)!important;color:#fff!important;box-shadow:none!important;transition:width .25s var(--ease),opacity .2s var(--ease),padding .25s var(--ease);}',
|
||||||
'.hoho .header__search.is-open .header__search-input{width:min(52vw,15rem);padding:0 1rem;opacity:1;}',
|
'.hoho .header__search.is-open .header__search-input{width:min(52vw,15rem);padding:0 1rem;opacity:1;}',
|
||||||
'.hoho .header__search-input::placeholder{color:rgba(255,255,255,.7);}',
|
'.hoho .header__search-input::placeholder{color:rgba(255,255,255,.7)!important;opacity:1;}',
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -105,9 +115,11 @@ add_action( 'wp', function () {
|
|||||||
}
|
}
|
||||||
add_action( 'astra_header', 'hoho_render_header' );
|
add_action( 'astra_header', 'hoho_render_header' );
|
||||||
|
|
||||||
if ( function_exists( 'astra_footer_markup' ) ) {
|
// Astra renders its footer via the Footer Builder (a class method on the
|
||||||
remove_action( 'astra_footer', 'astra_footer_markup' );
|
// `astra_footer` hook), so removing the legacy astra_footer_markup() alone
|
||||||
}
|
// left a second, default footer. Clear every callback on the hook, then add
|
||||||
|
// ours — a clean, full replacement regardless of classic vs builder footer.
|
||||||
|
remove_all_actions( 'astra_footer' );
|
||||||
add_action( 'astra_footer', 'hoho_render_footer' );
|
add_action( 'astra_footer', 'hoho_render_footer' );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -146,7 +158,10 @@ function hoho_logo() {
|
|||||||
* Small helper: inline-icon markup referencing the sprite.
|
* Small helper: inline-icon markup referencing the sprite.
|
||||||
*/
|
*/
|
||||||
function hoho_icon( $name, $extra = '' ) {
|
function hoho_icon( $name, $extra = '' ) {
|
||||||
$name = preg_replace( '/[^a-z0-9\-]/', '', 'i-' . ltrim( (string) $name, 'i-' ) );
|
// Strip an existing "i-" prefix (once), then re-add it. ltrim() with a
|
||||||
|
// charlist would eat any leading i/-, turning "instagram" into "nstagram".
|
||||||
|
$slug = preg_replace( '/^i-/', '', (string) $name );
|
||||||
|
$name = preg_replace( '/[^a-z0-9\-]/', '', 'i-' . $slug );
|
||||||
$class = 'icon' . ( $extra ? ' ' . $extra : '' );
|
$class = 'icon' . ( $extra ? ' ' . $extra : '' );
|
||||||
return '<svg class="' . esc_attr( $class ) . '" aria-hidden="true"><use href="#' . esc_attr( $name ) . '"></use></svg>';
|
return '<svg class="' . esc_attr( $class ) . '" aria-hidden="true"><use href="#' . esc_attr( $name ) . '"></use></svg>';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,10 @@ $hoho_footer_col = function ( $location ) {
|
|||||||
<footer class="footer" id="footer">
|
<footer class="footer" id="footer">
|
||||||
<a class="ring-badge ring-badge--top" href="#top" aria-label="<?php echo esc_attr__( 'Back to top', 'hoponhopoff' ); ?>">
|
<a class="ring-badge ring-badge--top" href="#top" aria-label="<?php echo esc_attr__( 'Back to top', 'hoponhopoff' ); ?>">
|
||||||
<svg class="ring-badge__ring" viewBox="0 0 100 100" aria-hidden="true">
|
<svg class="ring-badge__ring" viewBox="0 0 100 100" aria-hidden="true">
|
||||||
<text class="ring-badge__text"><textPath href="#ringPathScroll" startOffset="0">BACK TO TOP · BACK TO TOP · </textPath></text>
|
<text class="ring-badge__text" text-anchor="middle">
|
||||||
|
<textPath href="#ringPathScroll" startOffset="25%"><?php echo esc_html__( 'BACK TO TOP', 'hoponhopoff' ); ?></textPath>
|
||||||
|
<textPath href="#ringPathScroll" startOffset="75%"><?php echo esc_html__( 'BACK TO TOP', 'hoponhopoff' ); ?></textPath>
|
||||||
|
</text>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="ring-badge__dot"><svg class="icon" aria-hidden="true"><use href="#i-arrow-up"></use></svg></span>
|
<span class="ring-badge__dot"><svg class="icon" aria-hidden="true"><use href="#i-arrow-up"></use></svg></span>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Theme URI: https://git.kaamyab.dev/vargha/TourWebsite
|
|||||||
Description: Astra child theme that replaces the Astra header and footer with the "Hop On Hop Off" design. Pairs with the Viator Collector plugin.
|
Description: Astra child theme that replaces the Astra header and footer with the "Hop On Hop Off" design. Pairs with the Viator Collector plugin.
|
||||||
Author: Amir Bagheri
|
Author: Amir Bagheri
|
||||||
Template: astra
|
Template: astra
|
||||||
Version: 1.0.2
|
Version: 1.0.8
|
||||||
Text Domain: hoponhopoff
|
Text Domain: hoponhopoff
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,15 @@ html {
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
font-family:
|
||||||
|
"Plus Jakarta Sans",
|
||||||
|
system-ui,
|
||||||
|
-apple-system,
|
||||||
|
"Segoe UI",
|
||||||
|
Roboto,
|
||||||
|
Helvetica,
|
||||||
|
Arial,
|
||||||
|
sans-serif;
|
||||||
font-size: var(--fs-body);
|
font-size: var(--fs-body);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: var(--c-ink);
|
color: var(--c-ink);
|
||||||
@@ -173,7 +181,10 @@ dd {
|
|||||||
border-radius: var(--r-pill);
|
border-radius: var(--r-pill);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
transition: transform 0.2s var(--ease), filter 0.2s var(--ease), background 0.2s var(--ease);
|
transition:
|
||||||
|
transform 0.2s var(--ease),
|
||||||
|
filter 0.2s var(--ease),
|
||||||
|
background 0.2s var(--ease);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover {
|
.btn:hover {
|
||||||
@@ -378,7 +389,9 @@ dd {
|
|||||||
|
|
||||||
.rail:hover .dcard.is-active:not(:hover):not(:focus-within) .ring-badge__dot,
|
.rail:hover .dcard.is-active:not(:hover):not(:focus-within) .ring-badge__dot,
|
||||||
.rail:hover .acard.is-active:not(:hover):not(:focus-within) .ring-badge__dot,
|
.rail:hover .acard.is-active:not(:hover):not(:focus-within) .ring-badge__dot,
|
||||||
.bus__grid:hover .tcard.is-active:not(:hover):not(:focus-within) .ring-badge__dot {
|
.bus__grid:hover
|
||||||
|
.tcard.is-active:not(:hover):not(:focus-within)
|
||||||
|
.ring-badge__dot {
|
||||||
color: var(--c-white);
|
color: var(--c-white);
|
||||||
background: var(--c-glass);
|
background: var(--c-glass);
|
||||||
}
|
}
|
||||||
@@ -430,7 +443,6 @@ dd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes nudge {
|
@keyframes nudge {
|
||||||
|
|
||||||
0%,
|
0%,
|
||||||
60%,
|
60%,
|
||||||
100% {
|
100% {
|
||||||
@@ -541,11 +553,14 @@ dd {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transform: translateY(-0.375rem);
|
transform: translateY(-0.375rem);
|
||||||
transition: opacity 0.2s var(--ease), transform 0.2s var(--ease), visibility 0.2s;
|
transition:
|
||||||
|
opacity 0.2s var(--ease),
|
||||||
|
transform 0.2s var(--ease),
|
||||||
|
visibility 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__item--has-menu:hover .nav__submenu,
|
.nav__item--has-menu:hover .nav__submenu,
|
||||||
.nav__toggle[aria-expanded="true"]+.nav__submenu {
|
.nav__toggle[aria-expanded="true"] + .nav__submenu {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
@@ -585,7 +600,9 @@ dd {
|
|||||||
height: 0.125rem;
|
height: 0.125rem;
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
background: var(--c-white);
|
background: var(--c-white);
|
||||||
transition: transform 0.25s var(--ease), opacity 0.25s var(--ease);
|
transition:
|
||||||
|
transform 0.25s var(--ease),
|
||||||
|
opacity 0.25s var(--ease);
|
||||||
}
|
}
|
||||||
|
|
||||||
.burger[aria-expanded="true"] span:nth-child(1) {
|
.burger[aria-expanded="true"] span:nth-child(1) {
|
||||||
@@ -607,9 +624,9 @@ dd {
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: end;
|
align-items: end;
|
||||||
min-height: clamp(34rem, 88svh, 56rem);
|
|
||||||
margin: var(--page-inset);
|
margin: var(--page-inset);
|
||||||
padding-block: calc(var(--header-h) + var(--sp-6)) clamp(3rem, 1.5rem + 6vw, 6rem);
|
padding-block: calc(var(--header-h) + var(--sp-6))
|
||||||
|
clamp(3rem, 1.5rem + 6vw, 6rem);
|
||||||
border-radius: var(--r-xl);
|
border-radius: var(--r-xl);
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
}
|
}
|
||||||
@@ -634,7 +651,11 @@ dd {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background:
|
background:
|
||||||
radial-gradient(39.91% 77.37% at 60.22% 50%, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.5) 100%),
|
radial-gradient(
|
||||||
|
39.91% 77.37% at 60.22% 50%,
|
||||||
|
rgba(0, 0, 0, 0) 0%,
|
||||||
|
rgba(0, 0, 0, 0.5) 100%
|
||||||
|
),
|
||||||
linear-gradient(180deg, rgba(44, 175, 194, 0) 51.6%, var(--c-teal) 100%);
|
linear-gradient(180deg, rgba(44, 175, 194, 0) 51.6%, var(--c-teal) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,24 +835,28 @@ dd {
|
|||||||
scroll-snap-type: x mandatory;
|
scroll-snap-type: x mandatory;
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
/* negative margin + padding keeps the card glow from being clipped by the scroller */
|
/* overflow-x:auto forces overflow-y to auto, so the hover glow (a ~3.75rem
|
||||||
margin-inline: calc(var(--gap) * -1);
|
blur) would be clipped by the scroller. Don't switch to overflow:visible —
|
||||||
padding: var(--sp-4) var(--gap);
|
that kills the horizontal scroll. Instead give the rail enough padding for
|
||||||
|
the glow to paint INSIDE the scroll box, and pull it back with an equal
|
||||||
|
negative margin so the visible spacing is unchanged. Scrolling stays intact. */
|
||||||
|
margin: calc(var(--sp-5) * -1) calc(var(--gap) * -1);
|
||||||
|
padding: 4.5rem var(--gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rail::-webkit-scrollbar {
|
.rail::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rail>* {
|
.rail > * {
|
||||||
scroll-snap-align: start;
|
scroll-snap-align: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rail--dest>* {
|
.rail--dest > * {
|
||||||
flex: 0 0 calc((100% - 4 * var(--gap)) / 5);
|
flex: 0 0 calc((100% - 4 * var(--gap)) / 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rail--art>* {
|
.rail--art > * {
|
||||||
flex: 0 0 calc((100% - 2 * var(--gap)) / 3);
|
flex: 0 0 calc((100% - 2 * var(--gap)) / 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -870,7 +895,11 @@ dd {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 35%, rgba(0, 0, 0, 0.65) 100%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0) 35%,
|
||||||
|
rgba(0, 0, 0, 0.65) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* highlighted card — gradients copied from Figma */
|
/* highlighted card — gradients copied from Figma */
|
||||||
@@ -884,7 +913,11 @@ dd {
|
|||||||
.dcard:hover::after,
|
.dcard:hover::after,
|
||||||
.dcard:focus-within::after {
|
.dcard:focus-within::after {
|
||||||
background:
|
background:
|
||||||
linear-gradient(180deg, rgba(255, 255, 255, 0) 51.6%, rgba(255, 255, 255, 0.4) 100%),
|
linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(255, 255, 255, 0) 51.6%,
|
||||||
|
rgba(255, 255, 255, 0.4) 100%
|
||||||
|
),
|
||||||
linear-gradient(0deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
|
linear-gradient(0deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
|
||||||
linear-gradient(180deg, rgba(44, 175, 194, 0) 37.33%, #2cafc2 84.53%);
|
linear-gradient(180deg, rgba(44, 175, 194, 0) 37.33%, #2cafc2 84.53%);
|
||||||
}
|
}
|
||||||
@@ -897,7 +930,11 @@ dd {
|
|||||||
|
|
||||||
.rail:hover .dcard.is-active:not(:hover):not(:focus-within)::after,
|
.rail:hover .dcard.is-active:not(:hover):not(:focus-within)::after,
|
||||||
.rail:focus-within .dcard.is-active:not(:hover):not(:focus-within)::after {
|
.rail:focus-within .dcard.is-active:not(:hover):not(:focus-within)::after {
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 35%, rgba(0, 0, 0, 0.65) 100%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0) 35%,
|
||||||
|
rgba(0, 0, 0, 0.65) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcard__desc {
|
.dcard__desc {
|
||||||
@@ -907,7 +944,9 @@ dd {
|
|||||||
top: clamp(2.75rem, 2rem + 2vw, 3.5rem);
|
top: clamp(2.75rem, 2rem + 2vw, 3.5rem);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-0.375rem);
|
transform: translateY(-0.375rem);
|
||||||
transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
|
transition:
|
||||||
|
opacity 0.25s var(--ease),
|
||||||
|
transform 0.25s var(--ease);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcard.is-active .dcard__desc,
|
.dcard.is-active .dcard__desc,
|
||||||
@@ -918,7 +957,9 @@ dd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rail:hover .dcard.is-active:not(:hover):not(:focus-within) .dcard__desc,
|
.rail:hover .dcard.is-active:not(:hover):not(:focus-within) .dcard__desc,
|
||||||
.rail:focus-within .dcard.is-active:not(:hover):not(:focus-within) .dcard__desc {
|
.rail:focus-within
|
||||||
|
.dcard.is-active:not(:hover):not(:focus-within)
|
||||||
|
.dcard__desc {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-0.375rem);
|
transform: translateY(-0.375rem);
|
||||||
}
|
}
|
||||||
@@ -1039,7 +1080,8 @@ dd {
|
|||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
object-position: left;
|
object-position: left;
|
||||||
/* Figma tints the blue coach towards the teal brand colour */
|
/* Figma tints the blue coach towards the teal brand colour */
|
||||||
filter: hue-rotate(-32deg) saturate(1.15) drop-shadow(0 1rem 1.5rem rgba(0, 0, 0, 0.18));
|
filter: hue-rotate(-32deg) saturate(1.15)
|
||||||
|
drop-shadow(0 1rem 1.5rem rgba(0, 0, 0, 0.18));
|
||||||
}
|
}
|
||||||
|
|
||||||
.promo__note {
|
.promo__note {
|
||||||
@@ -1085,7 +1127,12 @@ dd {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.1) 45%, rgba(0, 0, 0, 0.6) 100%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0.45) 0%,
|
||||||
|
rgba(0, 0, 0, 0.1) 45%,
|
||||||
|
rgba(0, 0, 0, 0.6) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tcard.is-active,
|
.tcard.is-active,
|
||||||
@@ -1098,7 +1145,11 @@ dd {
|
|||||||
.tcard:hover::after,
|
.tcard:hover::after,
|
||||||
.tcard:focus-within::after {
|
.tcard:focus-within::after {
|
||||||
background:
|
background:
|
||||||
radial-gradient(53.59% 137.34% at 50% 46.41%, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.4) 100%),
|
radial-gradient(
|
||||||
|
53.59% 137.34% at 50% 46.41%,
|
||||||
|
rgba(255, 255, 255, 0) 0%,
|
||||||
|
rgba(255, 255, 255, 0.4) 100%
|
||||||
|
),
|
||||||
linear-gradient(0deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
|
linear-gradient(0deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
|
||||||
linear-gradient(180deg, rgba(44, 175, 194, 0) 37.33%, #2cafc2 84.53%);
|
linear-gradient(180deg, rgba(44, 175, 194, 0) 37.33%, #2cafc2 84.53%);
|
||||||
}
|
}
|
||||||
@@ -1110,7 +1161,12 @@ dd {
|
|||||||
|
|
||||||
.bus__grid:hover .tcard.is-active:not(:hover):not(:focus-within)::after,
|
.bus__grid:hover .tcard.is-active:not(:hover):not(:focus-within)::after,
|
||||||
.bus__grid:focus-within .tcard.is-active:not(:hover):not(:focus-within)::after {
|
.bus__grid:focus-within .tcard.is-active:not(:hover):not(:focus-within)::after {
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.1) 45%, rgba(0, 0, 0, 0.6) 100%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0.45) 0%,
|
||||||
|
rgba(0, 0, 0, 0.1) 45%,
|
||||||
|
rgba(0, 0, 0, 0.6) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tcard__k {
|
.tcard__k {
|
||||||
@@ -1164,11 +1220,13 @@ dd {
|
|||||||
right: -34%;
|
right: -34%;
|
||||||
top: 42%;
|
top: 42%;
|
||||||
height: 28%;
|
height: 28%;
|
||||||
background: linear-gradient(90deg,
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
rgba(44, 175, 194, 0) 0%,
|
rgba(44, 175, 194, 0) 0%,
|
||||||
rgba(44, 175, 194, 0.16) 22%,
|
rgba(44, 175, 194, 0.16) 22%,
|
||||||
rgba(44, 175, 194, 0.2) 55%,
|
rgba(44, 175, 194, 0.2) 55%,
|
||||||
rgba(44, 175, 194, 0) 100%);
|
rgba(44, 175, 194, 0) 100%
|
||||||
|
);
|
||||||
/* small blur only — the band keeps its straight parallel edges */
|
/* small blur only — the band keeps its straight parallel edges */
|
||||||
filter: blur(2px);
|
filter: blur(2px);
|
||||||
/* 25deg == the roof ridge angle, measured off the artwork itself */
|
/* 25deg == the roof ridge angle, measured off the artwork itself */
|
||||||
@@ -1295,8 +1353,20 @@ dd {
|
|||||||
.marquee {
|
.marquee {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
-webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
|
-webkit-mask-image: linear-gradient(
|
||||||
mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
|
90deg,
|
||||||
|
transparent,
|
||||||
|
#000 8%,
|
||||||
|
#000 92%,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
mask-image: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent,
|
||||||
|
#000 8%,
|
||||||
|
#000 92%,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.marquee__route {
|
.marquee__route {
|
||||||
@@ -1420,14 +1490,23 @@ dd {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.35) 0%, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.65) 100%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0.35) 0%,
|
||||||
|
rgba(0, 0, 0, 0.05) 40%,
|
||||||
|
rgba(0, 0, 0, 0.65) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.acard.is-active .acard__media::after,
|
.acard.is-active .acard__media::after,
|
||||||
.acard:hover .acard__media::after,
|
.acard:hover .acard__media::after,
|
||||||
.acard:focus-within .acard__media::after {
|
.acard:focus-within .acard__media::after {
|
||||||
background:
|
background:
|
||||||
radial-gradient(53.59% 137.34% at 50% 46.41%, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.4) 100%),
|
radial-gradient(
|
||||||
|
53.59% 137.34% at 50% 46.41%,
|
||||||
|
rgba(255, 255, 255, 0) 0%,
|
||||||
|
rgba(255, 255, 255, 0.4) 100%
|
||||||
|
),
|
||||||
linear-gradient(0deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
|
linear-gradient(0deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
|
||||||
linear-gradient(180deg, rgba(44, 175, 194, 0) 37.33%, #2cafc2 84.53%);
|
linear-gradient(180deg, rgba(44, 175, 194, 0) 37.33%, #2cafc2 84.53%);
|
||||||
}
|
}
|
||||||
@@ -1446,8 +1525,15 @@ dd {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rail:hover .acard.is-active:not(:hover):not(:focus-within) .acard__media::after {
|
.rail:hover
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.35) 0%, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.65) 100%);
|
.acard.is-active:not(:hover):not(:focus-within)
|
||||||
|
.acard__media::after {
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0.35) 0%,
|
||||||
|
rgba(0, 0, 0, 0.05) 40%,
|
||||||
|
rgba(0, 0, 0, 0.65) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.acard__time {
|
.acard__time {
|
||||||
@@ -1559,7 +1645,7 @@ dd {
|
|||||||
gap: var(--sp-4);
|
gap: var(--sp-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.acc+.acc {
|
.acc + .acc {
|
||||||
border-top: 1px solid var(--c-line);
|
border-top: 1px solid var(--c-line);
|
||||||
padding-top: var(--sp-4);
|
padding-top: var(--sp-4);
|
||||||
}
|
}
|
||||||
@@ -1613,11 +1699,14 @@ dd {
|
|||||||
transition: grid-template-rows 0.3s var(--ease);
|
transition: grid-template-rows 0.3s var(--ease);
|
||||||
}
|
}
|
||||||
|
|
||||||
.acc__q[aria-expanded="true"]+.acc__a {
|
/* The button sits inside an <h3>, so .acc__a is the h3's sibling — not the
|
||||||
|
button's. Match on the .acc wrapper's expanded state instead of an adjacent
|
||||||
|
sibling, which never matched through the heading. */
|
||||||
|
.acc:has(.acc__q[aria-expanded="true"]) .acc__a {
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.acc__a>* {
|
.acc__a > * {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1634,7 +1723,8 @@ dd {
|
|||||||
========================================================= */
|
========================================================= */
|
||||||
.footer {
|
.footer {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: calc(var(--sp-6) + var(--page-inset)) var(--page-inset) var(--page-inset);
|
margin: calc(var(--sp-6) + var(--page-inset)) var(--page-inset)
|
||||||
|
var(--page-inset);
|
||||||
padding-block: var(--sp-6) 0;
|
padding-block: var(--sp-6) 0;
|
||||||
border-radius: var(--r-xl);
|
border-radius: var(--r-xl);
|
||||||
color: var(--c-white);
|
color: var(--c-white);
|
||||||
@@ -1770,7 +1860,11 @@ dd {
|
|||||||
padding: var(--sp-4);
|
padding: var(--sp-4);
|
||||||
padding-bottom: clamp(2rem, 1rem + 3vw, 3rem);
|
padding-bottom: clamp(2rem, 1rem + 3vw, 3rem);
|
||||||
border-radius: var(--r-pill) var(--r-pill) 0 0;
|
border-radius: var(--r-pill) var(--r-pill) 0 0;
|
||||||
background: linear-gradient(180deg, rgba(44, 175, 194, 0.99) 0%, var(--c-teal-20) 100%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(44, 175, 194, 0.99) 0%,
|
||||||
|
var(--c-teal-20) 100%
|
||||||
|
);
|
||||||
box-shadow: 0 0.75rem 8.25rem var(--c-teal);
|
box-shadow: 0 0.75rem 8.25rem var(--c-teal);
|
||||||
-webkit-backdrop-filter: blur(4px);
|
-webkit-backdrop-filter: blur(4px);
|
||||||
backdrop-filter: blur(4px);
|
backdrop-filter: blur(4px);
|
||||||
@@ -1785,7 +1879,7 @@ dd {
|
|||||||
Responsive
|
Responsive
|
||||||
========================================================= */
|
========================================================= */
|
||||||
@media (max-width: 78rem) {
|
@media (max-width: 78rem) {
|
||||||
.rail--dest>* {
|
.rail--dest > * {
|
||||||
flex: 0 0 calc((100% - 3 * var(--gap)) / 4);
|
flex: 0 0 calc((100% - 3 * var(--gap)) / 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1813,7 +1907,7 @@ dd {
|
|||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rail--dest>* {
|
.rail--dest > * {
|
||||||
flex: 0 0 calc((100% - 2 * var(--gap)) / 3);
|
flex: 0 0 calc((100% - 2 * var(--gap)) / 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1861,7 +1955,10 @@ dd {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transform: translateY(-0.5rem);
|
transform: translateY(-0.5rem);
|
||||||
transition: opacity 0.25s var(--ease), transform 0.25s var(--ease), visibility 0.25s;
|
transition:
|
||||||
|
opacity 0.25s var(--ease),
|
||||||
|
transform 0.25s var(--ease),
|
||||||
|
visibility 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav.is-open {
|
.nav.is-open {
|
||||||
@@ -1902,7 +1999,7 @@ dd {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__toggle[aria-expanded="true"]+.nav__submenu {
|
.nav__toggle[aria-expanded="true"] + .nav__submenu {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1915,7 +2012,7 @@ dd {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rail--art>* {
|
.rail--art > * {
|
||||||
flex: 0 0 calc((100% - var(--gap)) / 2);
|
flex: 0 0 calc((100% - var(--gap)) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1925,7 +2022,7 @@ dd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 48rem) {
|
@media (max-width: 48rem) {
|
||||||
.rail--dest>* {
|
.rail--dest > * {
|
||||||
flex: 0 0 calc((100% - var(--gap)) / 2);
|
flex: 0 0 calc((100% - var(--gap)) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1953,10 +2050,6 @@ dd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 40rem) {
|
@media (max-width: 40rem) {
|
||||||
.hero {
|
|
||||||
min-height: clamp(30rem, 84svh, 44rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.booking {
|
.booking {
|
||||||
padding: var(--sp-2);
|
padding: var(--sp-2);
|
||||||
}
|
}
|
||||||
@@ -1988,11 +2081,11 @@ dd {
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rail--dest>* {
|
.rail--dest > * {
|
||||||
flex: 0 0 min(72%, 15rem);
|
flex: 0 0 min(72%, 15rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rail--art>* {
|
.rail--art > * {
|
||||||
flex: 0 0 min(85%, 22rem);
|
flex: 0 0 min(85%, 22rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2200,20 +2293,23 @@ dd {
|
|||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
background: var(--c-white);
|
background: var(--c-white);
|
||||||
transition: color 0.2s var(--ease), background-color 0.2s var(--ease), border-color 0.2s var(--ease);
|
transition:
|
||||||
|
color 0.2s var(--ease),
|
||||||
|
background-color 0.2s var(--ease),
|
||||||
|
border-color 0.2s var(--ease);
|
||||||
}
|
}
|
||||||
|
|
||||||
.check:hover .check__box {
|
.check:hover .check__box {
|
||||||
border-color: rgba(0, 0, 0, 0.45);
|
border-color: rgba(0, 0, 0, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
.check__input:checked+.check__box {
|
.check__input:checked + .check__box {
|
||||||
color: var(--c-white);
|
color: var(--c-white);
|
||||||
border-color: var(--c-black);
|
border-color: var(--c-black);
|
||||||
background: var(--c-black);
|
background: var(--c-black);
|
||||||
}
|
}
|
||||||
|
|
||||||
.check__input:focus-visible+.check__box {
|
.check__input:focus-visible + .check__box {
|
||||||
outline: 2px solid var(--c-teal);
|
outline: 2px solid var(--c-teal);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
@@ -2225,7 +2321,7 @@ dd {
|
|||||||
transition: opacity 0.2s var(--ease);
|
transition: opacity 0.2s var(--ease);
|
||||||
}
|
}
|
||||||
|
|
||||||
.check__input:checked~.check__text {
|
.check__input:checked ~ .check__text {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2345,7 +2441,9 @@ dd {
|
|||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
transition: color 0.25s var(--ease), opacity 0.25s var(--ease);
|
transition:
|
||||||
|
color 0.25s var(--ease),
|
||||||
|
opacity 0.25s var(--ease);
|
||||||
}
|
}
|
||||||
|
|
||||||
.titem.is-active .titem__desc,
|
.titem.is-active .titem__desc,
|
||||||
@@ -2357,7 +2455,9 @@ dd {
|
|||||||
|
|
||||||
/* hovering any card in the grid hands the highlight over to it */
|
/* hovering any card in the grid hands the highlight over to it */
|
||||||
.tgrid:hover .titem.is-active:not(:hover):not(:focus-within) .titem__desc,
|
.tgrid:hover .titem.is-active:not(:hover):not(:focus-within) .titem__desc,
|
||||||
.tgrid:focus-within .titem.is-active:not(:hover):not(:focus-within) .titem__desc {
|
.tgrid:focus-within
|
||||||
|
.titem.is-active:not(:hover):not(:focus-within)
|
||||||
|
.titem__desc {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
@@ -2369,11 +2469,17 @@ dd {
|
|||||||
|
|
||||||
.tgrid:hover .dcard.is-active:not(:hover):not(:focus-within)::after,
|
.tgrid:hover .dcard.is-active:not(:hover):not(:focus-within)::after,
|
||||||
.tgrid:focus-within .dcard.is-active:not(:hover):not(:focus-within)::after {
|
.tgrid:focus-within .dcard.is-active:not(:hover):not(:focus-within)::after {
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 35%, rgba(0, 0, 0, 0.65) 100%);
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0) 35%,
|
||||||
|
rgba(0, 0, 0, 0.65) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tgrid:hover .dcard.is-active:not(:hover):not(:focus-within) .ring-badge__dot,
|
.tgrid:hover .dcard.is-active:not(:hover):not(:focus-within) .ring-badge__dot,
|
||||||
.tgrid:focus-within .dcard.is-active:not(:hover):not(:focus-within) .ring-badge__dot {
|
.tgrid:focus-within
|
||||||
|
.dcard.is-active:not(:hover):not(:focus-within)
|
||||||
|
.ring-badge__dot {
|
||||||
color: var(--c-white);
|
color: var(--c-white);
|
||||||
background: var(--c-glass);
|
background: var(--c-glass);
|
||||||
}
|
}
|
||||||
@@ -2411,7 +2517,10 @@ dd {
|
|||||||
color: var(--c-black);
|
color: var(--c-black);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 0.2s var(--ease), background-color 0.2s var(--ease), border-color 0.2s var(--ease);
|
transition:
|
||||||
|
color 0.2s var(--ease),
|
||||||
|
background-color 0.2s var(--ease),
|
||||||
|
border-color 0.2s var(--ease);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pager__num {
|
.pager__num {
|
||||||
@@ -2530,7 +2639,8 @@ dd {
|
|||||||
--word: clamp(3rem, 0.4rem + 9.5vw, 11.25rem);
|
--word: clamp(3rem, 0.4rem + 9.5vw, 11.25rem);
|
||||||
min-height: clamp(26rem, 62svh, 38rem);
|
min-height: clamp(26rem, 62svh, 38rem);
|
||||||
margin: var(--page-inset);
|
margin: var(--page-inset);
|
||||||
padding-block: calc(var(--header-h) + var(--sp-5)) clamp(0.75rem, 0.4rem + 1vw, 1.5rem);
|
padding-block: calc(var(--header-h) + var(--sp-5))
|
||||||
|
clamp(0.75rem, 0.4rem + 1vw, 1.5rem);
|
||||||
border-radius: var(--r-xl);
|
border-radius: var(--r-xl);
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
overflow: clip;
|
overflow: clip;
|
||||||
@@ -2547,6 +2657,13 @@ dd {
|
|||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
object-position: center 26%;
|
object-position: center 26%;
|
||||||
|
/* Viator only ships ~720px images, which look soft blown up to a full-width
|
||||||
|
banner. Rather than fight it, lean in: a gentle blur turns the photo into an
|
||||||
|
intentional backdrop (the crisp image lives in the gallery lightbox). The
|
||||||
|
scale hides the blur's feathered edges, which overflow:clip on .hero-tour
|
||||||
|
then trims to the rounded frame. */
|
||||||
|
filter: blur(10px) saturate(1.12);
|
||||||
|
transform: scale(1.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-tour__sky {
|
.hero-tour__sky {
|
||||||
@@ -2555,8 +2672,17 @@ dd {
|
|||||||
z-index: -1;
|
z-index: -1;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
background:
|
background:
|
||||||
radial-gradient(70% 50% at 50% 100%, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0) 70%),
|
radial-gradient(
|
||||||
linear-gradient(180deg, rgba(27, 147, 166, 0.82) 0%, rgba(44, 175, 194, 0.62) 45%, rgba(111, 208, 221, 0.78) 100%);
|
70% 50% at 50% 100%,
|
||||||
|
rgba(255, 255, 255, 0.3) 0%,
|
||||||
|
rgba(255, 255, 255, 0) 70%
|
||||||
|
),
|
||||||
|
linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(27, 147, 166, 0.55) 0%,
|
||||||
|
rgba(44, 175, 194, 0.42) 45%,
|
||||||
|
rgba(111, 208, 221, 0.72) 100%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-tour__inner {
|
.hero-tour__inner {
|
||||||
@@ -2590,7 +2716,7 @@ dd {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.crumbs__list li+li::before {
|
.crumbs__list li + li::before {
|
||||||
content: "/";
|
content: "/";
|
||||||
margin-inline-end: var(--sp-1);
|
margin-inline-end: var(--sp-1);
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
@@ -2681,7 +2807,7 @@ dd {
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tour__text+.tour__text {
|
.tour__text + .tour__text {
|
||||||
margin-block-start: var(--sp-3);
|
margin-block-start: var(--sp-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2808,6 +2934,119 @@ dd {
|
|||||||
border-radius: var(--r-card);
|
border-radius: var(--r-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gallery img {
|
||||||
|
cursor: zoom-in;
|
||||||
|
transition: transform 0.25s var(--ease), filter 0.25s var(--ease);
|
||||||
|
}
|
||||||
|
|
||||||
|
.gallery img:hover {
|
||||||
|
filter: brightness(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================================================
|
||||||
|
Gallery lightbox (built by vc-design.js)
|
||||||
|
========================================================= */
|
||||||
|
.vc-lightbox {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
padding: clamp(1rem, 0.5rem + 3vw, 3.5rem);
|
||||||
|
background: rgba(0, 0, 0, 0.86);
|
||||||
|
-webkit-backdrop-filter: blur(6px);
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition:
|
||||||
|
opacity 0.25s var(--ease),
|
||||||
|
visibility 0s linear 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox.is-open {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
transition: opacity 0.25s var(--ease);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox__img {
|
||||||
|
max-width: min(100%, 78rem);
|
||||||
|
max-height: 84vh;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
border-radius: var(--r-card);
|
||||||
|
box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.5);
|
||||||
|
transform: scale(0.96);
|
||||||
|
transition: transform 0.25s var(--ease), opacity 0.15s var(--ease);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox.is-open .vc-lightbox__img {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox__btn {
|
||||||
|
position: absolute;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
width: 3rem;
|
||||||
|
height: 3rem;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: var(--r-pill);
|
||||||
|
color: var(--c-white);
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s var(--ease), transform 0.2s var(--ease);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox__btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.24);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox__close {
|
||||||
|
top: clamp(1rem, 3vw, 2rem);
|
||||||
|
right: clamp(1rem, 3vw, 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox__prev,
|
||||||
|
.vc-lightbox__next {
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox__prev {
|
||||||
|
left: clamp(0.5rem, 2vw, 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox__next {
|
||||||
|
right: clamp(0.5rem, 2vw, 2rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox__prev:hover,
|
||||||
|
.vc-lightbox__next:hover {
|
||||||
|
transform: translateY(-50%) scale(1.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-lightbox__count {
|
||||||
|
position: absolute;
|
||||||
|
bottom: clamp(1rem, 3vw, 2rem);
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* single image → hide the arrows */
|
||||||
|
.vc-lightbox--single .vc-lightbox__prev,
|
||||||
|
.vc-lightbox--single .vc-lightbox__next,
|
||||||
|
.vc-lightbox--single .vc-lightbox__count {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- included / not included --- */
|
/* --- included / not included --- */
|
||||||
.lists {
|
.lists {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
Binary file not shown.
@@ -16,23 +16,31 @@ listing/single/section assets load only on pages that use them.
|
|||||||
| `[viator_top_tours]` | Rail of tour cards | `viator_tour` posts |
|
| `[viator_top_tours]` | Rail of tour cards | `viator_tour` posts |
|
||||||
| `[viator_top_attractions]` | Rail of tour cards | `viator_tour` posts |
|
| `[viator_top_attractions]` | Rail of tour cards | `viator_tour` posts |
|
||||||
| `[viator_browse_activities]` | Rail of tour cards | `viator_tour` posts |
|
| `[viator_browse_activities]` | Rail of tour cards | `viator_tour` posts |
|
||||||
|
| `[viator_bus_tours]` | "Open Top Bus Tours" promo + 4-up card grid | `viator_tour` posts |
|
||||||
|
| `[viator_about]` | Static "About / Memorable" block + stat counters | Shortcode attributes |
|
||||||
|
| `[viator_articles]` | Rail of latest blog-post cards | `post` posts |
|
||||||
| `[viator_faq]` | FAQ accordion | Shortcode `items` / `vc_faq_items` filter |
|
| `[viator_faq]` | FAQ accordion | Shortcode `items` / `vc_faq_items` filter |
|
||||||
| `[viator_testimonials]` | Testimonials marquee | `vc_testimonials` filter |
|
| `[viator_testimonials]` | Testimonials marquee | `vc_testimonials` filter |
|
||||||
|
|
||||||
`[viator_hero_slider]` is kept as a **back-compat alias** for `[viator_hero]`.
|
`[viator_hero_slider]` is kept as a **back-compat alias** for `[viator_hero]`.
|
||||||
|
|
||||||
A typical home page:
|
Home page matching the design mockup (`new front/index.html`), top to bottom:
|
||||||
|
|
||||||
```
|
```
|
||||||
[viator_hero]
|
[viator_hero image_id="125304"]
|
||||||
[viator_top_destinations]
|
[viator_top_destinations]
|
||||||
|
[viator_bus_tours image_id="125310"]
|
||||||
[viator_top_tours]
|
[viator_top_tours]
|
||||||
[viator_top_attractions]
|
[viator_about image_id="125311"]
|
||||||
[viator_browse_activities]
|
|
||||||
[viator_testimonials]
|
[viator_testimonials]
|
||||||
|
[viator_articles]
|
||||||
[viator_faq]
|
[viator_faq]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Heavy decorative images (hero photo, open-top bus, isometric bus) are **not
|
||||||
|
bundled** — upload them in **Media** and pass the attachment `image_id` (exactly
|
||||||
|
like `[viator_hero image_id="…"]`). Icons and tiny decorations are inline SVG.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## `[viator_hero]`
|
## `[viator_hero]`
|
||||||
@@ -99,6 +107,71 @@ are hidden automatically.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## `[viator_bus_tours]`
|
||||||
|
|
||||||
|
The "Open Top Bus Tours…" promo block (route line, headline, "See all", a chip,
|
||||||
|
a bus illustration and note) followed by a 4-up grid of tour cards. Selects tours
|
||||||
|
with the **same attributes as the tour rails** (`ids` / `codes` / `destination` /
|
||||||
|
`tag` / `limit` / `orderby`), plus:
|
||||||
|
|
||||||
|
| Attribute | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `title` | `Open Top<br>Bus Tours…` | Promo headline (`<br>` allowed). |
|
||||||
|
| `note` | *(default copy)* | Paragraph under the headline. |
|
||||||
|
| `chip` | `Very Fun!` | Small accent chip. |
|
||||||
|
| `image_id` | — | **Media attachment ID** of the open-top bus illustration. |
|
||||||
|
| `image` | — | …or a direct image URL (use `image_id` when possible). |
|
||||||
|
|
||||||
|
```
|
||||||
|
[viator_bus_tours image_id="125310" tag="hop-on-hop-off" limit="4"]
|
||||||
|
[viator_bus_tours ids="482,517,690,701"]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `[viator_about]`
|
||||||
|
|
||||||
|
Static "About" marketing block: headline with an accent chip, a paragraph, four
|
||||||
|
stat counters and a decorative isometric bus. All copy is editable.
|
||||||
|
|
||||||
|
| Attribute | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `title` | `Hop On Hop Off` | Headline. |
|
||||||
|
| `highlight` | `Memorable` | Accent chip appended to the headline. |
|
||||||
|
| `text` | *(default copy)* | Paragraph. |
|
||||||
|
| `button_text` | `About us` | Button label. |
|
||||||
|
| `button_url` | *(tours archive)* | Button link. |
|
||||||
|
| `image_id` | — | **Media attachment ID** of the isometric bus illustration. |
|
||||||
|
| `image` | — | …or a direct image URL. |
|
||||||
|
| `stats` | *(4 defaults)* | `25+|Years Experience;;500+|Tours Conducted` — `|` separates number from label, `;;` separates stats. A trailing `+` is styled. |
|
||||||
|
|
||||||
|
```
|
||||||
|
[viator_about image_id="125311" stats="25+|Years Experience;;500+|Tours Conducted;;430+|Tours this Year;;15+|Different Tours"]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## `[viator_articles]`
|
||||||
|
|
||||||
|
A rail of the latest blog posts styled as the design's "Latest Articles" cards
|
||||||
|
(featured image, relative time, title, excerpt → links to the post).
|
||||||
|
|
||||||
|
| Attribute | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `title` | `Latest<br>Articles` | Heading (`<br>` allowed). |
|
||||||
|
| `lead` | *(default copy)* | Sub-heading. |
|
||||||
|
| `limit` | `8` | Number of posts. |
|
||||||
|
| `category` | — | Restrict to a category slug. |
|
||||||
|
| `view_all` | *(posts page)* | "See all" link; defaults to the blog page. |
|
||||||
|
|
||||||
|
Cards use each post's **featured image**; set one so the card isn't blank.
|
||||||
|
|
||||||
|
```
|
||||||
|
[viator_articles limit="8" category="travel-tips"]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## `[viator_faq]`
|
## `[viator_faq]`
|
||||||
|
|
||||||
FAQ accordion.
|
FAQ accordion.
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
<svg class="sprite" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg">
|
<svg class="sprite" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" style="position:absolute;width:0;height:0;overflow:hidden">
|
||||||
<defs>
|
<defs>
|
||||||
<path id="ringPathScroll" fill="none" d="M50,50 m-39,0 a39,39 0 1,1 78,0 a39,39 0 1,1 -78,0" />
|
<path id="ringPathScroll" fill="none" d="M50,50 m-39,0 a39,39 0 1,1 78,0 a39,39 0 1,1 -78,0" />
|
||||||
</defs>
|
</defs>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@@ -92,4 +92,57 @@
|
|||||||
var g = app.querySelector(".gallery, .tour-gallery");
|
var g = app.querySelector(".gallery, .tour-gallery");
|
||||||
if (g) g.scrollIntoView({ behavior: "smooth" });
|
if (g) g.scrollIntoView({ behavior: "smooth" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ---------- gallery lightbox (zoom / view photos) ---------- */
|
||||||
|
(function () {
|
||||||
|
var imgs = Array.prototype.slice.call(app.querySelectorAll(".gallery img"));
|
||||||
|
if (!imgs.length) return;
|
||||||
|
|
||||||
|
var host = imgs[0].closest(".vc-app") || document.body;
|
||||||
|
var box = document.createElement("div");
|
||||||
|
box.className = "vc-lightbox" + (imgs.length < 2 ? " vc-lightbox--single" : "");
|
||||||
|
box.setAttribute("role", "dialog");
|
||||||
|
box.setAttribute("aria-modal", "true");
|
||||||
|
box.innerHTML =
|
||||||
|
'<button class="vc-lightbox__btn vc-lightbox__prev" type="button" aria-label="Previous"><svg class="icon icon--24" aria-hidden="true"><use href="#i-arrow-left"></use></svg></button>' +
|
||||||
|
'<img class="vc-lightbox__img" alt="">' +
|
||||||
|
'<button class="vc-lightbox__btn vc-lightbox__next" type="button" aria-label="Next"><svg class="icon icon--24" aria-hidden="true"><use href="#i-arrow-right"></use></svg></button>' +
|
||||||
|
'<button class="vc-lightbox__btn vc-lightbox__close" type="button" aria-label="Close"><svg class="icon icon--24" aria-hidden="true"><use href="#i-x"></use></svg></button>' +
|
||||||
|
'<span class="vc-lightbox__count"></span>';
|
||||||
|
host.appendChild(box);
|
||||||
|
|
||||||
|
var bigImg = box.querySelector(".vc-lightbox__img");
|
||||||
|
var count = box.querySelector(".vc-lightbox__count");
|
||||||
|
var idx = 0;
|
||||||
|
|
||||||
|
function show(i) {
|
||||||
|
idx = (i + imgs.length) % imgs.length;
|
||||||
|
bigImg.src = imgs[idx].currentSrc || imgs[idx].src;
|
||||||
|
bigImg.alt = imgs[idx].alt || "";
|
||||||
|
count.textContent = (idx + 1) + " / " + imgs.length;
|
||||||
|
}
|
||||||
|
function open(i) {
|
||||||
|
show(i);
|
||||||
|
box.classList.add("is-open");
|
||||||
|
document.documentElement.style.overflow = "hidden";
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
box.classList.remove("is-open");
|
||||||
|
document.documentElement.style.overflow = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
imgs.forEach(function (img, i) {
|
||||||
|
img.addEventListener("click", function () { open(i); });
|
||||||
|
});
|
||||||
|
box.querySelector(".vc-lightbox__prev").addEventListener("click", function () { show(idx - 1); });
|
||||||
|
box.querySelector(".vc-lightbox__next").addEventListener("click", function () { show(idx + 1); });
|
||||||
|
box.querySelector(".vc-lightbox__close").addEventListener("click", close);
|
||||||
|
box.addEventListener("click", function (e) { if (e.target === box) close(); });
|
||||||
|
document.addEventListener("keydown", function (e) {
|
||||||
|
if (!box.classList.contains("is-open")) return;
|
||||||
|
if (e.key === "Escape") close();
|
||||||
|
else if (e.key === "ArrowLeft") show(idx - 1);
|
||||||
|
else if (e.key === "ArrowRight") show(idx + 1);
|
||||||
|
});
|
||||||
|
})();
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -17,7 +17,11 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const SCOPE = process.env.VC_SCOPE || '.vc-app';
|
// Doubled class (`.vc-app.vc-app`) so every ported rule carries an extra class
|
||||||
|
// of specificity — enough to win against Astra's content selectors (e.g.
|
||||||
|
// `.ast-… .entry-content a`) without touching the markup, which still only
|
||||||
|
// needs a single `class="vc-app"`. Override with VC_SCOPE if ever needed.
|
||||||
|
const SCOPE = process.env.VC_SCOPE || '.vc-app.vc-app';
|
||||||
|
|
||||||
function prefixSelector(sel) {
|
function prefixSelector(sel) {
|
||||||
sel = sel.trim();
|
sel = sel.trim();
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ class VC_Display {
|
|||||||
'viator_top_destinations',
|
'viator_top_destinations',
|
||||||
'viator_top_attractions',
|
'viator_top_attractions',
|
||||||
'viator_browse_activities',
|
'viator_browse_activities',
|
||||||
|
'viator_bus_tours',
|
||||||
|
'viator_about',
|
||||||
|
'viator_articles',
|
||||||
'viator_faq',
|
'viator_faq',
|
||||||
'viator_testimonials',
|
'viator_testimonials',
|
||||||
'viator_tour_card',
|
'viator_tour_card',
|
||||||
@@ -24,9 +27,12 @@ class VC_Display {
|
|||||||
);
|
);
|
||||||
|
|
||||||
public static function init() {
|
public static function init() {
|
||||||
// Priority 20 so Astra has already registered/enqueued its stylesheet;
|
// Priority 100 so the theme (Astra + child) has already enqueued ALL of
|
||||||
// our CSS then loads after it and wins equal-specificity ties.
|
// its stylesheets — including the parent style.css and the child's chrome
|
||||||
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ), 20 );
|
// CSS, which are enqueued by the theme at priority 20 and would otherwise
|
||||||
|
// print AFTER us (plugins run before themes at equal priority) and win
|
||||||
|
// equal-specificity ties. Loading last makes our design win those ties.
|
||||||
|
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ), 100 );
|
||||||
add_action( 'template_redirect', array( __CLASS__, 'maybe_enrich_single' ) );
|
add_action( 'template_redirect', array( __CLASS__, 'maybe_enrich_single' ) );
|
||||||
add_action( 'pre_get_posts', array( __CLASS__, 'filter_archive_query' ) );
|
add_action( 'pre_get_posts', array( __CLASS__, 'filter_archive_query' ) );
|
||||||
add_filter( 'single_template', array( __CLASS__, 'single_template' ) );
|
add_filter( 'single_template', array( __CLASS__, 'single_template' ) );
|
||||||
@@ -146,11 +152,15 @@ class VC_Display {
|
|||||||
wp_enqueue_style( 'vc-fonts', 'https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;700;800&display=swap', array(), null );
|
wp_enqueue_style( 'vc-fonts', 'https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;700;800&display=swap', array(), null );
|
||||||
|
|
||||||
// One scoped stylesheet (.vc-app) covers listing, single, and homepage
|
// One scoped stylesheet (.vc-app) covers listing, single, and homepage
|
||||||
// sections — ported from the "Hop On Hop Off" design. Depend on the Astra
|
// sections — ported from the "Hop On Hop Off" design. Declare every theme
|
||||||
// theme stylesheet (when present) so our rules load AFTER it and win ties.
|
// stylesheet that could fight ours as a dependency (when present) so our
|
||||||
|
// rules always print AFTER them and win equal-specificity ties. We enqueue
|
||||||
|
// at priority 100, so by now the theme (priority 20) has registered them.
|
||||||
$deps = array( 'vc-fonts' );
|
$deps = array( 'vc-fonts' );
|
||||||
if ( wp_style_is( 'astra-theme-css', 'registered' ) ) {
|
foreach ( array( 'astra-theme-css', 'astra-parent', 'hoho-chrome' ) as $handle ) {
|
||||||
$deps[] = 'astra-theme-css';
|
if ( wp_style_is( $handle, 'registered' ) ) {
|
||||||
|
$deps[] = $handle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
wp_enqueue_style( 'vc-design', VC_PLUGIN_URL . 'assets/css/vc-design.css', $deps, VC_VERSION );
|
wp_enqueue_style( 'vc-design', VC_PLUGIN_URL . 'assets/css/vc-design.css', $deps, VC_VERSION );
|
||||||
wp_add_inline_style( 'vc-design', self::astra_overrides() );
|
wp_add_inline_style( 'vc-design', self::astra_overrides() );
|
||||||
@@ -180,42 +190,89 @@ class VC_Display {
|
|||||||
. '.vc-fullwidth.ast-page-builder-template .site-main>article'
|
. '.vc-fullwidth.ast-page-builder-template .site-main>article'
|
||||||
. '{padding:0!important;margin:0!important;border:0!important;max-width:none!important;}',
|
. '{padding:0!important;margin:0!important;border:0!important;max-width:none!important;}',
|
||||||
'.vc-fullwidth #primary,.vc-fullwidth #content{margin:0!important;padding:0!important;}',
|
'.vc-fullwidth #primary,.vc-fullwidth #content{margin:0!important;padding:0!important;}',
|
||||||
|
// display:block undoes Astra's `.site-content .ast-container{display:flex}`
|
||||||
|
// (≥922px), which otherwise sizes our content to its contents instead
|
||||||
|
// of filling the full-width container.
|
||||||
'.vc-fullwidth .site-content .ast-container,.vc-fullwidth .ast-container'
|
'.vc-fullwidth .site-content .ast-container,.vc-fullwidth .ast-container'
|
||||||
. '{max-width:none!important;padding-left:0!important;padding-right:0!important;}',
|
. '{max-width:none!important;padding-left:0!important;padding-right:0!important;display:block!important;}',
|
||||||
'.vc-fullwidth .entry-content{margin-block:0!important;}',
|
'.vc-fullwidth .entry-content{margin-block:0!important;}',
|
||||||
'.vc-fullwidth .site-content{padding:0!important;}',
|
'.vc-fullwidth .site-content{padding:0!important;}',
|
||||||
// Guard against a stray horizontal scrollbar from any full-bleed child.
|
// Guard against a stray horizontal scrollbar from any full-bleed child.
|
||||||
'body.vc-fullwidth{overflow-x:hidden;}',
|
'body.vc-fullwidth{overflow-x:hidden;}',
|
||||||
// A gradient stands in when no hero image is set, so the banner is
|
// A gradient stands in when no hero image is set, so the banner is
|
||||||
// never blank.
|
// never blank.
|
||||||
'.vc-app .hero__media,.vc-app .hero-tour{background:linear-gradient(120deg,#101317,#2cafc2);}',
|
'.vc-app.vc-app .hero__media,.vc-app.vc-app .hero-tour{background:linear-gradient(120deg,#101317,#2cafc2);}',
|
||||||
// Normalize: stop Astra from decorating our components. Its
|
// Normalize: stop Astra from decorating our components. Its
|
||||||
// .entry-content rules (underlined links, list bullets/indent)
|
// .entry-content rules (underlined links, list bullets/indent)
|
||||||
// out-specify our scoped base, so reset them here (!important beats
|
// out-specify our scoped base, so reset them here (!important beats
|
||||||
// e.g. `.ast-single-post .entry-content a`). The .vc-app.vc-app
|
// e.g. `.ast-single-post .entry-content a`). Every selector matches
|
||||||
// doubling lifts specificity without touching design class rules.
|
// the doubled `.vc-app.vc-app` scope of the main stylesheet so these
|
||||||
'.vc-app a,.vc-app.vc-app a{text-decoration:none;box-shadow:none;}',
|
// patches keep winning against it, not just against the theme.
|
||||||
'.vc-app :is(ul,ol),.vc-app.vc-app :is(ul,ol){list-style:none!important;margin:0!important;padding:0!important;}',
|
'.vc-app.vc-app a{text-decoration:none;box-shadow:none;}',
|
||||||
'.vc-app li{margin:0!important;}',
|
// No !important on margin/padding: the doubled .vc-app.vc-app scope
|
||||||
|
// already out-specifies Astra's list rules, and !important here was
|
||||||
|
// steamrolling design lists that need their own spacing — e.g. the
|
||||||
|
// rail (<ul class="rail">) whose vertical padding gives the hover glow
|
||||||
|
// room. list-style stays !important (purely cosmetic; prose lists
|
||||||
|
// re-enable bullets via a higher-specificity rule).
|
||||||
|
'.vc-app.vc-app :is(ul,ol){list-style:none!important;margin:0;padding:0;}',
|
||||||
|
'.vc-app.vc-app li{margin:0;}',
|
||||||
// …but keep the tour "Overview" prose (the_content) readable.
|
// …but keep the tour "Overview" prose (the_content) readable.
|
||||||
'.vc-app .tour__text a{text-decoration:underline;}',
|
'.vc-app.vc-app .tour__text a{text-decoration:underline;}',
|
||||||
'.vc-app .tour__text ul{list-style:disc!important;margin:1em 0!important;padding-inline-start:1.5em!important;}',
|
'.vc-app.vc-app .tour__text ul{list-style:disc!important;margin:1em 0!important;padding-inline-start:1.5em!important;}',
|
||||||
'.vc-app .tour__text ol{list-style:decimal!important;margin:1em 0!important;padding-inline-start:1.5em!important;}',
|
'.vc-app.vc-app .tour__text ol{list-style:decimal!important;margin:1em 0!important;padding-inline-start:1.5em!important;}',
|
||||||
'.vc-app .tour__text li{margin:.25em 0!important;}',
|
'.vc-app.vc-app .tour__text li{margin:.25em 0!important;}',
|
||||||
// Links must inherit the design colour (Astra colours links with its
|
// Links must inherit the design colour (Astra colours links with its
|
||||||
// theme colour, which turned white card titles/destination names into
|
// theme colour, which turned white card titles/destination names into
|
||||||
// coloured text). Buttons keep their own colour.
|
// coloured text). Buttons keep their own colour.
|
||||||
'.vc-app a:not(.btn){color:inherit;}',
|
// …but not ring-badges: they set their own colour on the <a>, and this
|
||||||
'.vc-app .tour__text a{color:var(--c-teal);}',
|
// inherit rule out-specifies .ring-badge--top/--scroll, which turned
|
||||||
|
// the badge text white (inherited) on light backgrounds.
|
||||||
|
'.vc-app.vc-app a:not(.btn):not(.ring-badge){color:inherit;}',
|
||||||
|
'.vc-app.vc-app .tour__text a{color:var(--c-teal);}',
|
||||||
// Real tour titles are far longer than the mockup\'s city names — clamp
|
// Real tour titles are far longer than the mockup\'s city names — clamp
|
||||||
// them and stop price/duration values wrapping mid-number.
|
// them and stop price/duration values wrapping mid-number.
|
||||||
'.vc-app .dcard__title{display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}',
|
'.vc-app.vc-app .dcard__title{display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}',
|
||||||
'.vc-app .titem__desc{display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}',
|
'.vc-app.vc-app .titem__desc{display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}',
|
||||||
'.vc-app .dcard__meta .meta__v{white-space:nowrap;}',
|
// Real tour descriptions run long; the on-hover card blurb clamps to
|
||||||
|
// three lines with an ellipsis instead of spilling over the card.
|
||||||
|
'.vc-app.vc-app .dcard__desc p{display:-webkit-box;-webkit-line-clamp:3;line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;}',
|
||||||
|
'.vc-app.vc-app .dcard__meta .meta__v{white-space:nowrap;}',
|
||||||
|
// Astra's page-builder layout centres/limits every entry-content child
|
||||||
|
// (max-width:none; margin:auto), which flattened the hero inset. Our
|
||||||
|
// sections are full-bleed, so re-assert 0 side-margins; !important
|
||||||
|
// beats Astra's 4-class selector. The hero keeps its small inset — the
|
||||||
|
// only section with an intentional side margin.
|
||||||
|
'.vc-app.vc-app>*{max-width:none!important;margin-inline:0!important;}',
|
||||||
|
// Heroes keep their small inset (rounded card look) — .hero on the
|
||||||
|
// home/archive pages and .hero-tour on the single-tour page. Without
|
||||||
|
// this they went full-bleed after the margin-inline:0 reset above.
|
||||||
|
'.vc-app.vc-app>.hero,.vc-app.vc-app>.hero-tour{margin:var(--page-inset)!important;}',
|
||||||
|
// Let vertical decorations (rotating badges, card glow, promo bus, FAQ
|
||||||
|
// watermark) escape, but keep the horizontal axis clipped so a
|
||||||
|
// full-bleed section or decorative element can't cause a mobile
|
||||||
|
// sideways scroll. overflow-x:clip (unlike hidden) does NOT force
|
||||||
|
// overflow-y to auto, so the y-axis stays truly visible.
|
||||||
|
'.vc-app.vc-app{overflow-x:clip;overflow-y:visible;}',
|
||||||
|
// Astra wraps the content in article/.entry-content/.ast-container,
|
||||||
|
// some of which clip overflow — that cut the card hover glow at the
|
||||||
|
// rail edges. Open them up on our pages only (page scroll stays
|
||||||
|
// contained by body.vc-fullwidth{overflow-x:hidden}).
|
||||||
|
'.vc-fullwidth .site-main,.vc-fullwidth #primary,.vc-fullwidth #content,'
|
||||||
|
. '.vc-fullwidth .site-content,.vc-fullwidth .ast-container,'
|
||||||
|
. '.vc-fullwidth .entry-content,.vc-fullwidth .site-main>article'
|
||||||
|
. '{overflow:visible!important;}',
|
||||||
|
// FAQ question buttons must stay flat — Astra gives <button> a shadow.
|
||||||
|
'.vc-app.vc-app .acc__q{box-shadow:none!important;}',
|
||||||
|
// Astra colours every heading on the element itself (h1–h6 and
|
||||||
|
// .entry-content :where(h1..h6)), beating our container-inherited
|
||||||
|
// colours. Make headings inherit again — no !important, so design
|
||||||
|
// classes (.hero__title, the white .tcard titles) keep their own.
|
||||||
|
'.vc-app.vc-app :is(h1,h2,h3,h4,h5,h6){color:inherit;}',
|
||||||
// Typography: force the design font over Astra inside our scopes.
|
// Typography: force the design font over Astra inside our scopes.
|
||||||
'.vc-app,.vc-app h1,.vc-app h2,.vc-app h3,.vc-app h4,.vc-app h5,.vc-app h6,'
|
'.vc-app.vc-app,.vc-app.vc-app h1,.vc-app.vc-app h2,.vc-app.vc-app h3,.vc-app.vc-app h4,.vc-app.vc-app h5,.vc-app.vc-app h6,'
|
||||||
. '.vc-app p,.vc-app a,.vc-app span,.vc-app li,.vc-app button,.vc-app input,'
|
. '.vc-app.vc-app p,.vc-app.vc-app a,.vc-app.vc-app span,.vc-app.vc-app li,.vc-app.vc-app button,.vc-app.vc-app input,'
|
||||||
. '.vc-app select,.vc-app textarea,.vc-app blockquote'
|
. '.vc-app.vc-app select,.vc-app.vc-app textarea,.vc-app.vc-app blockquote'
|
||||||
. '{font-family:"Plus Jakarta Sans",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}',
|
. '{font-family:"Plus Jakarta Sans",system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}',
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ class VC_Sections {
|
|||||||
const TOP_ATTRS = 'viator_top_attractions';
|
const TOP_ATTRS = 'viator_top_attractions';
|
||||||
const ACTIVITIES = 'viator_browse_activities';
|
const ACTIVITIES = 'viator_browse_activities';
|
||||||
const TOP_TOURS = 'viator_top_tours';
|
const TOP_TOURS = 'viator_top_tours';
|
||||||
|
const BUS_TOURS = 'viator_bus_tours';
|
||||||
|
const ABOUT = 'viator_about';
|
||||||
|
const ARTICLES = 'viator_articles';
|
||||||
const FAQ = 'viator_faq';
|
const FAQ = 'viator_faq';
|
||||||
const REVIEWS = 'viator_testimonials';
|
const REVIEWS = 'viator_testimonials';
|
||||||
|
|
||||||
@@ -28,6 +31,9 @@ class VC_Sections {
|
|||||||
add_shortcode( self::TOP_ATTRS, array( __CLASS__, 'top_attractions' ) );
|
add_shortcode( self::TOP_ATTRS, array( __CLASS__, 'top_attractions' ) );
|
||||||
add_shortcode( self::ACTIVITIES, array( __CLASS__, 'browse_activities' ) );
|
add_shortcode( self::ACTIVITIES, array( __CLASS__, 'browse_activities' ) );
|
||||||
add_shortcode( self::TOP_TOURS, array( __CLASS__, 'top_tours' ) );
|
add_shortcode( self::TOP_TOURS, array( __CLASS__, 'top_tours' ) );
|
||||||
|
add_shortcode( self::BUS_TOURS, array( __CLASS__, 'bus_tours' ) );
|
||||||
|
add_shortcode( self::ABOUT, array( __CLASS__, 'about' ) );
|
||||||
|
add_shortcode( self::ARTICLES, array( __CLASS__, 'articles' ) );
|
||||||
add_shortcode( self::FAQ, array( __CLASS__, 'faq' ) );
|
add_shortcode( self::FAQ, array( __CLASS__, 'faq' ) );
|
||||||
add_shortcode( self::REVIEWS, array( __CLASS__, 'testimonials' ) );
|
add_shortcode( self::REVIEWS, array( __CLASS__, 'testimonials' ) );
|
||||||
}
|
}
|
||||||
@@ -130,7 +136,7 @@ class VC_Sections {
|
|||||||
* Open a titled rail section and return its unique rail id. Caller fills the
|
* Open a titled rail section and return its unique rail id. Caller fills the
|
||||||
* <ul> with cards and then calls close_rail().
|
* <ul> with cards and then calls close_rail().
|
||||||
*/
|
*/
|
||||||
private static function open_rail( $title, $lead, $view_all_url, $view_all_text ) {
|
private static function open_rail( $title, $lead, $view_all_url, $view_all_text, $rail_class = 'rail--dest' ) {
|
||||||
$id = 'vcRail' . ( ++self::$rail_seq );
|
$id = 'vcRail' . ( ++self::$rail_seq );
|
||||||
ob_start();
|
ob_start();
|
||||||
?>
|
?>
|
||||||
@@ -150,7 +156,7 @@ class VC_Sections {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="rail rail--dest" id="<?php echo esc_attr( $id ); ?>">
|
<ul class="rail <?php echo esc_attr( $rail_class ); ?>" id="<?php echo esc_attr( $id ); ?>">
|
||||||
<?php
|
<?php
|
||||||
return array( 'id' => $id, 'head' => ob_get_clean() );
|
return array( 'id' => $id, 'head' => ob_get_clean() );
|
||||||
}
|
}
|
||||||
@@ -450,6 +456,299 @@ class VC_Sections {
|
|||||||
return ob_get_clean();
|
return ob_get_clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [viator_bus_tours limit="4" tag="hop-on-hop-off" image_id="123"]
|
||||||
|
*
|
||||||
|
* The "Open Top Bus Tours…" promo block + a 4-up grid of tour cards. Backed
|
||||||
|
* by the same curated/filtered tour query as the rails. The bus illustration
|
||||||
|
* is a heavy image, so it comes from the Media Library via image_id (or a
|
||||||
|
* direct image URL) — nothing is bundled with the plugin.
|
||||||
|
*/
|
||||||
|
public static function bus_tours( $atts ) {
|
||||||
|
$atts = shortcode_atts(
|
||||||
|
array(
|
||||||
|
'ids' => '',
|
||||||
|
'codes' => '',
|
||||||
|
'destination' => '',
|
||||||
|
'tag' => '',
|
||||||
|
'limit' => 4,
|
||||||
|
'orderby' => 'rating',
|
||||||
|
'title' => __( 'Open Top<br>Bus Tours…', 'viator-collector' ),
|
||||||
|
'note' => __( 'An open-top hop on hop off bus is the best way to discover the historic and cultural sights of any city.', 'viator-collector' ),
|
||||||
|
'chip' => __( 'Very Fun!', 'viator-collector' ),
|
||||||
|
'image_id' => '', // WordPress media attachment ID for the bus illustration.
|
||||||
|
'image' => '', // …or a direct image URL.
|
||||||
|
),
|
||||||
|
$atts,
|
||||||
|
self::BUS_TOURS
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = self::tour_query( $atts );
|
||||||
|
if ( ! $query->have_posts() ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$archive = get_post_type_archive_link( 'viator_tour' );
|
||||||
|
$archive = $archive ? $archive : home_url( '/' );
|
||||||
|
|
||||||
|
$bus_img = '';
|
||||||
|
if ( '' !== $atts['image_id'] ) {
|
||||||
|
// 'full' (uncropped original) — some sites register a hard-cropped
|
||||||
|
// square 'large', which slices decorative illustrations in half.
|
||||||
|
$bus_img = wp_get_attachment_image( absint( $atts['image_id'] ), 'full', false, array( 'alt' => '', 'loading' => 'lazy' ) );
|
||||||
|
} elseif ( '' !== $atts['image'] ) {
|
||||||
|
$bus_img = '<img src="' . esc_url( $atts['image'] ) . '" alt="" loading="lazy">';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<div class="vc-app vc-sections">
|
||||||
|
<section class="section section--tight">
|
||||||
|
<div class="container bus">
|
||||||
|
<div class="promo">
|
||||||
|
<svg class="promo__route" viewBox="0 0 416 526" fill="none" aria-hidden="true" preserveAspectRatio="none">
|
||||||
|
<path d="M-20 470 C 90 470, 60 350, 150 330 S 300 300, 300 210 S 210 120, 300 60 S 430 40, 440 -10" stroke="#2CAFC2" stroke-dasharray="3 3" />
|
||||||
|
</svg>
|
||||||
|
<div class="promo__head">
|
||||||
|
<h2 class="promo__title"><?php echo wp_kses( $atts['title'], array( 'br' => array() ) ); ?></h2>
|
||||||
|
<a class="btn btn--white" href="<?php echo esc_url( $archive ); ?>"><span><?php echo esc_html__( 'See all', 'viator-collector' ); ?></span><?php echo vc_icon( 'arrow-ne' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a>
|
||||||
|
</div>
|
||||||
|
<?php if ( $atts['chip'] ) : ?><span class="chip chip--accent promo__chip"><?php echo esc_html( $atts['chip'] ); ?></span><?php endif; ?>
|
||||||
|
<?php if ( $bus_img ) : ?><div class="promo__bus" aria-hidden="true"><?php echo $bus_img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div><?php endif; ?>
|
||||||
|
<?php if ( $atts['note'] ) : ?><p class="promo__note"><?php echo esc_html( $atts['note'] ); ?></p><?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="bus__grid">
|
||||||
|
<?php
|
||||||
|
$i = 0;
|
||||||
|
while ( $query->have_posts() ) :
|
||||||
|
$query->the_post();
|
||||||
|
++$i;
|
||||||
|
$pid = get_the_ID();
|
||||||
|
$price = get_post_meta( $pid, '_vc_price_from', true );
|
||||||
|
$currency = get_post_meta( $pid, '_vc_currency', true );
|
||||||
|
$terms = get_the_terms( $pid, 'viator_destination' );
|
||||||
|
$kicker = ( $terms && ! is_wp_error( $terms ) ) ? $terms[0]->name : '';
|
||||||
|
$active = ( 1 === $i );
|
||||||
|
$badge = vc_ring_badge( 'BOOK NOW · BOOK NOW · ', get_permalink( $pid ), 'card' );
|
||||||
|
if ( $active ) {
|
||||||
|
$badge = str_replace( 'ring-badge--card', 'ring-badge--card ring-badge--accent', $badge );
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<li class="tcard<?php echo $active ? ' is-active' : ''; ?>">
|
||||||
|
<?php echo vc_tour_image_html( $pid, 'tcard__img' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||||
|
<div class="tcard__top">
|
||||||
|
<?php if ( $kicker ) : ?><span class="tcard__k"><?php echo esc_html( $kicker ); ?></span><?php endif; ?>
|
||||||
|
<h3 class="tcard__title"><a href="<?php echo esc_url( get_permalink( $pid ) ); ?>"><?php echo esc_html( get_the_title( $pid ) ); ?></a></h3>
|
||||||
|
</div>
|
||||||
|
<div class="tcard__bottom">
|
||||||
|
<?php if ( '' !== (string) $price ) : ?>
|
||||||
|
<div class="meta">
|
||||||
|
<span class="meta__k"><?php echo esc_html__( 'Start at', 'viator-collector' ); ?></span>
|
||||||
|
<span class="meta__v"><?php echo esc_html( vc_money( $price, $currency ) ); ?></span>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php echo $badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<?php endwhile; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
wp_reset_postdata();
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [viator_about image_id="123" title="Hop On Hop Off" highlight="Memorable"
|
||||||
|
* stats="25+|Years Experience;;500+|Tours Conducted"]
|
||||||
|
*
|
||||||
|
* A static "About" marketing block (headline + chip, paragraph, stat counters
|
||||||
|
* and a decorative bus). All copy is editable via attributes; the isometric
|
||||||
|
* bus is a heavy image supplied from the Media Library (image_id / image).
|
||||||
|
*/
|
||||||
|
public static function about( $atts ) {
|
||||||
|
$atts = shortcode_atts(
|
||||||
|
array(
|
||||||
|
'title' => __( 'Hop On Hop Off', 'viator-collector' ),
|
||||||
|
'highlight' => __( 'Memorable', 'viator-collector' ),
|
||||||
|
'text' => __( 'Discover cities the memorable way — hop on and off our open-top buses at every major sight and explore each place at your own pace, with expert local commentary along the way.', 'viator-collector' ),
|
||||||
|
'button_text' => __( 'About us', 'viator-collector' ),
|
||||||
|
'button_url' => '',
|
||||||
|
'image_id' => '',
|
||||||
|
'image' => '',
|
||||||
|
'stats' => '', // "25+|Years Experience;;500+|Tours Conducted;;430+|Tours this Year;;15+|Different Tours"
|
||||||
|
),
|
||||||
|
$atts,
|
||||||
|
self::ABOUT
|
||||||
|
);
|
||||||
|
|
||||||
|
$stats = array();
|
||||||
|
if ( '' !== $atts['stats'] ) {
|
||||||
|
foreach ( explode( ';;', $atts['stats'] ) as $pair ) {
|
||||||
|
$p = array_map( 'trim', explode( '|', $pair, 2 ) );
|
||||||
|
if ( '' !== $p[0] && isset( $p[1] ) ) {
|
||||||
|
$stats[] = array( 'num' => $p[0], 'label' => $p[1] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( empty( $stats ) ) {
|
||||||
|
$stats = array(
|
||||||
|
array( 'num' => '25+', 'label' => __( 'Years Experience', 'viator-collector' ) ),
|
||||||
|
array( 'num' => '500+', 'label' => __( 'Tours Conducted', 'viator-collector' ) ),
|
||||||
|
array( 'num' => '430+', 'label' => __( 'Tours this Year', 'viator-collector' ) ),
|
||||||
|
array( 'num' => '15+', 'label' => __( 'Different Tours', 'viator-collector' ) ),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$button_url = $atts['button_url'];
|
||||||
|
if ( '' === $button_url ) {
|
||||||
|
$button_url = get_post_type_archive_link( 'viator_tour' );
|
||||||
|
$button_url = $button_url ? $button_url : home_url( '/' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$bus_img = '';
|
||||||
|
if ( '' !== $atts['image_id'] ) {
|
||||||
|
// 'full' (uncropped original) — some sites register a hard-cropped
|
||||||
|
// square 'large', which slices decorative illustrations in half.
|
||||||
|
$bus_img = wp_get_attachment_image( absint( $atts['image_id'] ), 'full', false, array( 'alt' => '', 'loading' => 'lazy' ) );
|
||||||
|
} elseif ( '' !== $atts['image'] ) {
|
||||||
|
$bus_img = '<img src="' . esc_url( $atts['image'] ) . '" alt="" loading="lazy">';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<div class="vc-app vc-sections">
|
||||||
|
<section class="section">
|
||||||
|
<div class="container about">
|
||||||
|
<div class="about__art" aria-hidden="true">
|
||||||
|
<span class="about__glow"></span>
|
||||||
|
<span class="about__trail" aria-hidden="true"></span>
|
||||||
|
<?php if ( $bus_img ) : ?><span class="about__bus"><?php echo $bus_img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span><?php endif; ?>
|
||||||
|
<svg class="about__arcs" viewBox="0 0 480 440" fill="none" aria-hidden="true">
|
||||||
|
<defs>
|
||||||
|
<path id="busArc" pathLength="100" d="M20 336.6V221.6C20 96.6 170 23.4 170 148.4V263.4" />
|
||||||
|
</defs>
|
||||||
|
<g stroke="#FFCC00" stroke-width="9" stroke-linecap="round" fill="none" transform="translate(16 24)">
|
||||||
|
<use href="#busArc" opacity="0.1" stroke-dasharray="62 100" />
|
||||||
|
<use href="#busArc" opacity="0.25" stroke-dasharray="69 100" transform="translate(48 22)" />
|
||||||
|
<use href="#busArc" opacity="0.45" stroke-dasharray="76 100" transform="translate(96 44)" />
|
||||||
|
<use href="#busArc" opacity="0.8" stroke-dasharray="83 100" transform="translate(144 66)" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="about__body">
|
||||||
|
<div class="about__head">
|
||||||
|
<h2 class="about__title"><?php echo esc_html( $atts['title'] ); ?><?php if ( $atts['highlight'] ) : ?><span class="chip chip--accent about__chip"><?php echo esc_html( $atts['highlight'] ); ?></span><?php endif; ?></h2>
|
||||||
|
<a class="btn btn--dark" href="<?php echo esc_url( $button_url ); ?>"><span><?php echo esc_html( $atts['button_text'] ); ?></span><?php echo vc_icon( 'arrow-right' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ( $atts['text'] ) : ?><p class="about__text"><?php echo esc_html( $atts['text'] ); ?></p><?php endif; ?>
|
||||||
|
|
||||||
|
<dl class="stats">
|
||||||
|
<?php
|
||||||
|
foreach ( $stats as $s ) :
|
||||||
|
$num = $s['num'];
|
||||||
|
$suffix = '';
|
||||||
|
if ( preg_match( '/^(.*?)(\+)$/', $num, $m ) ) {
|
||||||
|
$num = $m[1];
|
||||||
|
$suffix = $m[2];
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="stat">
|
||||||
|
<dt class="stat__num"><?php echo esc_html( $num ); ?><?php if ( $suffix ) : ?><span><?php echo esc_html( $suffix ); ?></span><?php endif; ?></dt>
|
||||||
|
<dd class="stat__label"><?php echo esc_html( $s['label'] ); ?></dd>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [viator_articles limit="8" category="news"]
|
||||||
|
*
|
||||||
|
* A rail of the latest blog posts, styled as the design's "Latest Articles"
|
||||||
|
* cards. Uses each post's featured image, time and excerpt; links to the post.
|
||||||
|
*/
|
||||||
|
public static function articles( $atts ) {
|
||||||
|
$atts = shortcode_atts(
|
||||||
|
array(
|
||||||
|
'title' => __( 'Latest<br>Articles', 'viator-collector' ),
|
||||||
|
'lead' => __( 'News, tips and stories from our tours.', 'viator-collector' ),
|
||||||
|
'limit' => 8,
|
||||||
|
'category' => '',
|
||||||
|
'view_all' => '',
|
||||||
|
),
|
||||||
|
$atts,
|
||||||
|
self::ARTICLES
|
||||||
|
);
|
||||||
|
|
||||||
|
$args = array(
|
||||||
|
'post_type' => 'post',
|
||||||
|
'post_status' => 'publish',
|
||||||
|
'posts_per_page' => max( 1, absint( $atts['limit'] ) ),
|
||||||
|
'ignore_sticky_posts' => true,
|
||||||
|
'no_found_rows' => true,
|
||||||
|
);
|
||||||
|
if ( '' !== $atts['category'] ) {
|
||||||
|
$args['category_name'] = sanitize_title( $atts['category'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = new WP_Query( $args );
|
||||||
|
if ( ! $query->have_posts() ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$view_all = $atts['view_all'];
|
||||||
|
if ( '' === $view_all ) {
|
||||||
|
$posts_page = (int) get_option( 'page_for_posts' );
|
||||||
|
$view_all = $posts_page ? get_permalink( $posts_page ) : home_url( '/' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$rail = self::open_rail( $atts['title'], $atts['lead'], $view_all, __( 'See all', 'viator-collector' ), 'rail--art' );
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
echo $rail['head']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||||
|
$i = 0;
|
||||||
|
while ( $query->have_posts() ) :
|
||||||
|
$query->the_post();
|
||||||
|
++$i;
|
||||||
|
$pid = get_the_ID();
|
||||||
|
$active = ( 1 === $i );
|
||||||
|
$img = has_post_thumbnail( $pid ) ? get_the_post_thumbnail( $pid, 'large', array( 'class' => 'acard__img', 'loading' => 'lazy', 'alt' => '' ) ) : '';
|
||||||
|
$time = human_time_diff( (int) get_post_time( 'U', true, $pid ), (int) current_time( 'timestamp', true ) );
|
||||||
|
$excerpt = get_the_excerpt( $pid );
|
||||||
|
$badge = vc_ring_badge( 'READ MORE · READ MORE · ', get_permalink( $pid ), 'card' );
|
||||||
|
if ( $active ) {
|
||||||
|
$badge = str_replace( 'ring-badge--card', 'ring-badge--card ring-badge--accent', $badge );
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<li class="acard<?php echo $active ? ' is-active' : ''; ?>">
|
||||||
|
<div class="acard__media">
|
||||||
|
<?php echo $img; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||||
|
<span class="chip chip--glass acard__time"><?php printf( /* translators: %s: human-readable time difference, e.g. "3 hours". */ esc_html__( '%s ago', 'viator-collector' ), esc_html( $time ) ); ?></span>
|
||||||
|
<div class="acard__overlay">
|
||||||
|
<h3 class="acard__title"><a href="<?php echo esc_url( get_permalink( $pid ) ); ?>"><?php echo esc_html( get_the_title( $pid ) ); ?></a></h3>
|
||||||
|
<?php echo $badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php if ( $excerpt ) : ?><p class="acard__excerpt"><?php echo esc_html( $excerpt ); ?></p><?php endif; ?>
|
||||||
|
</li>
|
||||||
|
<?php
|
||||||
|
endwhile;
|
||||||
|
echo self::close_rail(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||||
|
wp_reset_postdata();
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared tour query for the landing sections. Supports curation (ids /
|
* Shared tour query for the landing sections. Supports curation (ids /
|
||||||
* product codes) and filtering (destination / tag), with a sane fallback.
|
* product codes) and filtering (destination / tag), with a sane fallback.
|
||||||
|
|||||||
@@ -595,7 +595,10 @@ function vc_print_icon_sprite() {
|
|||||||
* e.g. vc_icon( 'star', 'rating__star' ) => <svg class="icon rating__star">…</svg>
|
* e.g. vc_icon( 'star', 'rating__star' ) => <svg class="icon rating__star">…</svg>
|
||||||
*/
|
*/
|
||||||
function vc_icon( $name, $extra_class = '' ) {
|
function vc_icon( $name, $extra_class = '' ) {
|
||||||
$name = preg_replace( '/[^a-z0-9\-]/', '', 'i-' . ltrim( (string) $name, 'i-' ) );
|
// Strip an existing "i-" prefix (once), then re-add it. ltrim() with a
|
||||||
|
// charlist would eat any leading i/-, turning "instagram" into "nstagram".
|
||||||
|
$slug = preg_replace( '/^i-/', '', (string) $name );
|
||||||
|
$name = preg_replace( '/[^a-z0-9\-]/', '', 'i-' . $slug );
|
||||||
$class = 'icon' . ( $extra_class ? ' ' . $extra_class : '' );
|
$class = 'icon' . ( $extra_class ? ' ' . $extra_class : '' );
|
||||||
return '<svg class="' . esc_attr( $class ) . '" aria-hidden="true"><use href="#' . esc_attr( $name ) . '"></use></svg>';
|
return '<svg class="' . esc_attr( $class ) . '" aria-hidden="true"><use href="#' . esc_attr( $name ) . '"></use></svg>';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,9 +38,18 @@ while ( have_posts() ) :
|
|||||||
?>
|
?>
|
||||||
<div class="vc-app vc-single">
|
<div class="vc-app vc-single">
|
||||||
<section class="hero-tour">
|
<section class="hero-tour">
|
||||||
<?php if ( $primary ) : ?>
|
<?php
|
||||||
|
// Prefer a WordPress featured image (full size, responsive srcset) for a
|
||||||
|
// sharp hero — Viator's own image URLs cap out around 720px and look soft
|
||||||
|
// blown up to a full-width banner. Fall back to the Viator primary image.
|
||||||
|
if ( has_post_thumbnail( $post_id ) ) :
|
||||||
|
echo get_the_post_thumbnail( $post_id, 'full', array( 'class' => 'hero-tour__photo', 'fetchpriority' => 'high', 'alt' => '', 'sizes' => '100vw' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||||
|
elseif ( $primary ) :
|
||||||
|
?>
|
||||||
<img class="hero-tour__photo" src="<?php echo esc_url( $primary ); ?>" alt="" fetchpriority="high">
|
<img class="hero-tour__photo" src="<?php echo esc_url( $primary ); ?>" alt="" fetchpriority="high">
|
||||||
<?php endif; ?>
|
<?php
|
||||||
|
endif;
|
||||||
|
?>
|
||||||
<div class="hero-tour__sky" aria-hidden="true"></div>
|
<div class="hero-tour__sky" aria-hidden="true"></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="hero-tour__block">
|
<div class="hero-tour__block">
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ defined('ABSPATH') || exit;
|
|||||||
/**
|
/**
|
||||||
* Plugin Name: Viator Collector
|
* Plugin Name: Viator Collector
|
||||||
* Description: Import Viator tours using the Viator Partner API and display them with native WordPress post types, templates, and shortcodes.
|
* Description: Import Viator tours using the Viator Partner API and display them with native WordPress post types, templates, and shortcodes.
|
||||||
* Version: 1.8.4
|
* Version: 1.8.15
|
||||||
* Author: Amir Bagheri
|
* Author: Amir Bagheri
|
||||||
* License: GPL-2.0-or-later
|
* License: GPL-2.0-or-later
|
||||||
* Text Domain: viator-collector
|
* Text Domain: viator-collector
|
||||||
*/
|
*/
|
||||||
|
|
||||||
define('VC_VERSION', '1.8.4');
|
define('VC_VERSION', '1.8.15');
|
||||||
define('VC_PLUGIN_FILE', __FILE__);
|
define('VC_PLUGIN_FILE', __FILE__);
|
||||||
define('VC_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
define('VC_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||||
define('VC_PLUGIN_URL', plugin_dir_url(__FILE__));
|
define('VC_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||||
|
|||||||
Reference in New Issue
Block a user