Experience the
Tranquillity of a
Historic House
Book on Our Website and Enjoy Exclusive Benefits
Only by booking through our official website will you enjoy exclusive access to the facilities of Hotel Es Port, along with a selection of privileges designed to make your stay even more memorable.
Breakfast at
Hotel Es Port.
Unlimited access to the spa, swimming pools and gardens at Hotel Es Port
Exclusive discount at Sa Figuera Restaurant
Ca n’Isabel is a boutique hotel in Sóller set within a beautifully restored historic townhouse in the heart of the old town. With just five exclusive rooms, it combines the timeless charm of traditional Mallorcan architecture with contemporary comfort.
One Unforgettable Stay.
When you book directly with us, your stay at Ca n’Isabel includes exclusive access to the facilities of Hotel Es Port.
More privacy.
More tranquillity.
More personalised service.
Five Ways to Experience Sóller.
Our building dates back to the late nineteenth century, a time when Sóller flourished thanks to trade and emigration. Many families returning from abroad built elegant townhouses throughout the village, inspired by the architectural styles they had discovered across Europe.
Ca n’Isabel is part of this history. Carefully restored to preserve its original character, the house has been transformed into an intimate boutique hotel designed for comfort, simplicity and authenticity.




Built during Sóller’s golden age at the end of the nineteenth century, the house reflects a period when returning emigrants invested in elegant homes inspired by European design.
Although not a formal example of Catalan Modernisme, Ca n’Isabel features decorative elements typical of Sóller’s distinctive popular Modernisme—a geometric and handcrafted interpretation of the architectural style that defines the valley.
“We didn’t restore a house to turn it into a hotel. We restored a house so it could continue to be a home that welcomes travellers.”

True Spirit of Sóller
Descubre qué opinan quienes ya se han alojado en Ca’n Isabel. Huéspedes que valoran la tranquilidad, la ubicación en el centro de Sóller y el encanto de una casa histórica convertida en hotel boutique.
<!-- ─────────────────────────────────────────
YARA Review Widget — Bricks Code element
Paste this whole <section> into a Bricks "Code" element with
"Execute Code" OFF. The build step inlines widget.css and
widget.js between the ⟨STYLE⟩ and ⟨SCRIPT⟩ markers below.
───────────────────────────────────────── -->
<section class="yara-page-content yara-bricks-module yara-review-widget">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500&display=swap" rel="stylesheet">
<style>
/* ─────────────────────────────────────────
YARA Review Widget — all styles
Tokens, layout, hero, card, tags, textarea, AI badge,
rewrite row, CTA, language switcher, footer, responsive.
───────────────────────────────────────── */
/* ── Tokens + scoped reset ─────────────── */
.yara-review-widget {
--yrw-green-dark: #04170a;
--yrw-green-mid: #0d1d0f;
--yrw-green-light: #152517;
--yrw-gold: #A1916B;
--yrw-gold-light: #c9b88a;
--yrw-gold-glow: rgba(161, 145, 107, 0.15);
--yrw-cream: #f8f5f0;
--yrw-text-primary: rgba(255, 255, 255, 0.95);
--yrw-text-soft: rgba(255, 255, 255, 0.82);
--yrw-text-muted: rgba(255, 255, 255, 0.70);
--yrw-border: rgba(161, 145, 107, 0.28);
--yrw-border-hover: rgba(161, 145, 107, 0.55);
--yrw-font-body: 'Montserrat', sans-serif;
background: var(--yrw-green-dark);
color: var(--yrw-text-primary);
font-family: var(--yrw-font-body);
font-weight: 300;
font-size: 14px;
line-height: 1.8;
width: 100%;
-webkit-font-smoothing: antialiased;
}
.yara-review-widget,
.yara-review-widget *,
.yara-review-widget *::before,
.yara-review-widget *::after {
box-sizing: border-box;
}
.yara-review-widget a {
color: inherit;
text-decoration: none;
}
/* ── Hero image ────────────────────────── */
.yara-review-widget .yrw-hero-image {
width: 100%;
height: 60vh;
min-height: 420px;
position: relative;
overflow: hidden;
background: var(--yrw-green-dark);
}
.yara-review-widget .yrw-hero-image img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center 60%;
}
.yara-review-widget .yrw-hero-image::after {
content: '';
position: absolute;
bottom: 0; left: 0; right: 0;
height: 40%;
background: linear-gradient(to top, var(--yrw-green-dark) 0%, transparent 100%);
pointer-events: none;
}
/* ── Hero title section ────────────────── */
.yara-review-widget .yrw-hero-title-section {
background: var(--yrw-green-dark);
padding: 60px 80px 70px;
text-align: center;
border-bottom: 1px solid var(--yrw-border);
position: relative;
}
.yara-review-widget .yrw-eyebrow {
font-size: 9px;
letter-spacing: 5px;
text-transform: uppercase;
color: var(--yrw-gold);
font-weight: 400;
margin-bottom: 24px;
position: relative;
}
.yara-review-widget .yrw-title {
font-family: var(--yrw-font-body);
font-weight: 300;
font-size: 48px;
letter-spacing: 8px;
text-transform: uppercase;
color: var(--yrw-text-primary);
line-height: 1.1;
margin: 0 0 24px;
position: relative;
}
.yara-review-widget .yrw-title-rule {
width: 48px;
height: 1px;
background: var(--yrw-gold);
margin: 0 auto 24px;
}
.yara-review-widget .yrw-subtitle {
font-size: 14px;
letter-spacing: 0.5px;
color: var(--yrw-text-soft);
font-weight: 300;
max-width: 520px;
margin: 0 auto;
line-height: 1.9;
position: relative;
}
/* ── Main section (card) ───────────────── */
.yara-review-widget .yrw-main-section {
background: var(--yrw-green-mid);
padding: 70px 80px;
border-bottom: 1px solid var(--yrw-border);
}
.yara-review-widget .yrw-card {
max-width: 640px;
margin: 0 auto;
}
/* ── Step header ───────────────────────── */
.yara-review-widget .yrw-step {
margin-bottom: 50px;
}
.yara-review-widget .yrw-step:last-child {
margin-bottom: 0;
}
.yara-review-widget .yrw-step-header {
display: flex;
align-items: baseline;
gap: 16px;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid var(--yrw-border);
}
.yara-review-widget .yrw-step-num {
font-family: var(--yrw-font-body);
font-style: italic;
font-size: 32px;
color: var(--yrw-gold);
opacity: 0.7;
line-height: 1;
}
.yara-review-widget .yrw-step-title {
font-family: var(--yrw-font-body);
font-weight: 300;
font-size: 11px;
letter-spacing: 4px;
text-transform: uppercase;
color: var(--yrw-text-primary);
}
/* ── Tag grid ──────────────────────────── */
.yara-review-widget .yrw-tag-grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px 12px;
}
.yara-review-widget .yrw-tag {
padding: 12px 28px;
border: 1px solid var(--yrw-border);
border-radius: 50px;
background: transparent;
font-family: var(--yrw-font-body);
font-size: 11px;
font-weight: 400;
letter-spacing: 1.5px;
color: var(--yrw-text-soft);
cursor: pointer;
transition: all 0.25s;
text-align: center;
text-transform: uppercase;
white-space: nowrap;
user-select: none;
}
.yara-review-widget .yrw-tag:hover {
border-color: var(--yrw-border-hover);
color: var(--yrw-text-primary);
background: rgba(161, 145, 107, 0.10);
}
.yara-review-widget .yrw-tag.selected {
border-color: var(--yrw-gold);
color: var(--yrw-gold-light);
background: var(--yrw-gold-glow);
}
/* ── Review box (textarea + char counter + AI badge) ── */
.yara-review-widget .yrw-review-wrap {
position: relative;
}
.yara-review-widget .yrw-textarea {
width: 100%;
min-height: 200px;
background: var(--yrw-green-light);
border: 1px solid var(--yrw-border);
color: var(--yrw-text-primary);
font-family: var(--yrw-font-body);
font-size: 14px;
font-weight: 300;
line-height: 2;
padding: 20px 24px 38px;
resize: vertical;
outline: none;
transition: border-color 0.25s, box-shadow 0.25s;
}
.yara-review-widget .yrw-textarea:focus {
border-color: var(--yrw-gold);
box-shadow: 0 0 0 3px rgba(161, 145, 107, 0.15);
}
.yara-review-widget .yrw-textarea::placeholder {
color: var(--yrw-text-muted);
font-weight: 300;
font-style: italic;
}
.yara-review-widget .yrw-char-count {
position: absolute;
bottom: 14px;
right: 18px;
font-size: 10px;
letter-spacing: 1px;
color: var(--yrw-text-muted);
pointer-events: none;
}
.yara-review-widget .yrw-ai-badge {
position: absolute;
top: 12px;
right: 12px;
display: none;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 999px;
background: rgba(161, 145, 107, 0.15);
border: 1px solid var(--yrw-border);
font-size: 10px;
font-weight: 400;
letter-spacing: 1.5px;
text-transform: uppercase;
color: var(--yrw-gold-light);
}
.yara-review-widget .yrw-ai-badge.visible {
display: inline-flex;
}
.yara-review-widget .yrw-ai-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--yrw-gold);
animation: yrwPulse 1.4s ease-in-out infinite;
}
@keyframes yrwPulse {
0%, 100% { opacity: 0.4; transform: scale(0.85); }
50% { opacity: 1; transform: scale(1.1); }
}
/* ── Rewrite row ───────────────────────── */
.yara-review-widget .yrw-rewrite {
margin-top: 20px;
display: none;
}
.yara-review-widget .yrw-rewrite.visible {
display: block;
}
.yara-review-widget .yrw-rewrite-label {
display: block;
font-size: 10px;
font-weight: 400;
letter-spacing: 2px;
text-transform: uppercase;
color: var(--yrw-gold);
margin-bottom: 10px;
}
.yara-review-widget .yrw-rewrite-row {
display: flex;
gap: 10px;
}
.yara-review-widget .yrw-rewrite-input {
flex: 1;
padding: 12px 16px;
border: 1px solid var(--yrw-border);
background: var(--yrw-green-light);
color: var(--yrw-text-primary);
font-family: var(--yrw-font-body);
font-weight: 300;
font-size: 13px;
outline: none;
transition: border-color 0.25s, box-shadow 0.25s;
}
.yara-review-widget .yrw-rewrite-input:focus {
border-color: var(--yrw-gold);
box-shadow: 0 0 0 3px rgba(161, 145, 107, 0.15);
}
.yara-review-widget .yrw-rewrite-input::placeholder {
color: var(--yrw-text-muted);
font-style: italic;
}
.yara-review-widget .yrw-rewrite-btn {
padding: 12px 24px;
border: 1px solid var(--yrw-border-hover);
background: transparent;
color: var(--yrw-gold-light);
font-family: var(--yrw-font-body);
font-weight: 400;
font-size: 9px;
letter-spacing: 3px;
text-transform: uppercase;
cursor: pointer;
transition: all 0.25s;
white-space: nowrap;
}
.yara-review-widget .yrw-rewrite-btn:hover {
border-color: var(--yrw-gold);
background: var(--yrw-gold-glow);
}
/* ── CTA section ───────────────────────── */
.yara-review-widget .yrw-cta-section {
text-align: center;
}
.yara-review-widget .yrw-step.yrw-cta-section .yrw-step-header {
justify-content: center;
}
.yara-review-widget .yrw-btn-gold {
font-family: var(--yrw-font-body);
font-weight: 400;
font-size: 10px;
letter-spacing: 3px;
text-transform: uppercase;
background: var(--yrw-gold);
color: #fff;
border: 1px solid var(--yrw-gold);
padding: 16px 40px;
cursor: pointer;
transition: all 0.25s;
display: inline-flex;
align-items: center;
gap: 10px;
}
.yara-review-widget .yrw-btn-gold:hover {
background: var(--yrw-gold-light);
border-color: var(--yrw-gold-light);
}
.yara-review-widget .yrw-btn-gold.copied {
background: #2d6a4f;
border-color: #2d6a4f;
}
.yara-review-widget .yrw-btn-gold svg {
width: 14px;
height: 14px;
}
.yara-review-widget .yrw-cta-hint {
margin-top: 20px;
font-size: 11px;
letter-spacing: 0.5px;
color: var(--yrw-text-muted);
transition: color 0.3s ease, font-size 0.3s ease;
}
.yara-review-widget .yrw-cta-hint.yrw-hint-highlight {
color: var(--yrw-gold-light);
font-size: 13px;
}
/* ── Language switcher ─────────────────── */
.yara-review-widget .yrw-lang-section {
background: var(--yrw-green-mid);
padding: 40px 80px;
text-align: center;
border-bottom: 1px solid var(--yrw-border);
}
.yara-review-widget .yrw-lang-switcher {
display: flex;
justify-content: center;
gap: 8px;
}
.yara-review-widget .yrw-lang-btn {
padding: 10px 20px;
background: transparent;
border: 1px solid var(--yrw-border);
color: var(--yrw-text-muted);
font-family: var(--yrw-font-body);
font-size: 9px;
font-weight: 400;
letter-spacing: 3px;
text-transform: uppercase;
cursor: pointer;
transition: all 0.25s;
}
.yara-review-widget .yrw-lang-btn:hover {
border-color: var(--yrw-border-hover);
color: var(--yrw-text-soft);
}
.yara-review-widget .yrw-lang-btn.active {
border-color: var(--yrw-gold);
color: var(--yrw-gold-light);
background: var(--yrw-gold-glow);
}
/* ── Footer CTA ────────────────────────── */
.yara-review-widget .yrw-footer-cta {
background: var(--yrw-green-dark);
padding: 80px 80px;
text-align: center;
position: relative;
}
.yara-review-widget .yrw-footer-cta::before {
content: '';
position: absolute;
bottom: 0; left: 0; right: 0;
height: 180px;
background: none;
pointer-events: none;
}
.yara-review-widget .yrw-footer-cta .yrw-eyebrow {
margin-bottom: 20px;
position: relative;
}
.yara-review-widget .yrw-footer-title {
font-family: var(--yrw-font-body);
font-weight: 300;
font-size: 32px;
letter-spacing: 6px;
text-transform: uppercase;
color: var(--yrw-text-primary);
margin: 0 0 28px;
line-height: 1.2;
position: relative;
}
.yara-review-widget .yrw-ctas {
display: flex;
justify-content: center;
gap: 16px;
flex-wrap: wrap;
position: relative;
}
.yara-review-widget .yrw-btn-ghost {
font-family: var(--yrw-font-body);
font-weight: 400;
font-size: 9px;
letter-spacing: 3px;
text-transform: uppercase;
background: transparent;
color: var(--yrw-gold-light);
border: 1px solid var(--yrw-border-hover);
padding: 14px 32px;
cursor: pointer;
transition: all 0.2s;
display: inline-block;
}
.yara-review-widget .yrw-btn-ghost:hover {
border-color: var(--yrw-gold);
background: var(--yrw-gold-glow);
}
/* ── Publish modal ─────────────────────── */
.yara-review-widget .yrw-modal {
position: fixed;
inset: 0;
z-index: 999999;
display: none;
align-items: center;
justify-content: center;
padding: 24px;
}
.yara-review-widget .yrw-modal.visible {
display: flex;
animation: yrwModalFade 0.2s ease-out;
}
.yara-review-widget .yrw-modal-backdrop {
position: absolute;
inset: 0;
background: rgba(4, 23, 10, 0.78);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
cursor: pointer;
}
.yara-review-widget .yrw-modal-card {
position: relative;
width: 100%;
max-width: 520px;
max-height: calc(100vh - 48px);
overflow-y: auto;
background: var(--yrw-green-mid);
border: 1px solid var(--yrw-border);
padding: 40px 36px 32px;
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.6);
animation: yrwModalRise 0.25s ease-out;
text-align: center;
}
.yara-review-widget .yrw-modal-close {
position: absolute;
top: 12px;
right: 14px;
width: 34px;
height: 34px;
border: none;
background: transparent;
color: var(--yrw-text-muted);
font-size: 22px;
line-height: 1;
cursor: pointer;
transition: color 0.2s;
}
.yara-review-widget .yrw-modal-close:hover {
color: var(--yrw-gold);
}
.yara-review-widget .yrw-modal-eyebrow {
font-size: 9px;
letter-spacing: 5px;
text-transform: uppercase;
color: var(--yrw-gold);
font-weight: 400;
margin-bottom: 16px;
}
.yara-review-widget .yrw-modal-title {
font-family: var(--yrw-font-body);
font-weight: 300;
font-size: 22px;
letter-spacing: 4px;
text-transform: uppercase;
color: var(--yrw-text-primary);
margin: 0 0 12px;
line-height: 1.3;
}
.yara-review-widget .yrw-modal-subtitle {
font-size: 12px;
letter-spacing: 0.5px;
color: var(--yrw-text-soft);
margin: 0 0 22px;
line-height: 1.6;
}
.yara-review-widget .yrw-modal-preview-wrap {
position: relative;
margin: 0 0 20px;
}
.yara-review-widget .yrw-modal-preview {
background: var(--yrw-green-light);
border: 1px solid var(--yrw-border);
color: var(--yrw-text-primary);
font-family: var(--yrw-font-body);
font-weight: 300;
font-size: 13px;
line-height: 1.75;
padding: 18px 20px 44px;
text-align: left;
max-height: 180px;
overflow-y: auto;
white-space: pre-wrap;
word-break: break-word;
}
.yara-review-widget .yrw-modal-copy {
position: absolute;
bottom: 10px;
right: 10px;
padding: 7px 14px;
border: 1px solid var(--yrw-border-hover);
background: rgba(13, 29, 15, 0.85);
color: var(--yrw-gold-light);
font-family: var(--yrw-font-body);
font-weight: 400;
font-size: 9px;
letter-spacing: 2px;
text-transform: uppercase;
cursor: pointer;
transition: all 0.2s;
}
.yara-review-widget .yrw-modal-copy:hover {
border-color: var(--yrw-gold);
background: var(--yrw-gold-glow);
}
.yara-review-widget .yrw-modal-copy.copied {
border-color: #2d6a4f;
color: #7bd4a8;
}
.yara-review-widget .yrw-modal-copy-status {
position: absolute;
bottom: -20px;
left: 50%;
transform: translateX(-50%);
font-size: 10px;
letter-spacing: 2px;
text-transform: uppercase;
color: #7bd4a8;
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
.yara-review-widget .yrw-modal-copy-status.visible {
opacity: 1;
}
.yara-review-widget .yrw-modal-instruction {
font-size: 11px;
line-height: 1.7;
color: var(--yrw-text-muted);
margin: 16px 0 24px;
letter-spacing: 0.3px;
}
.yara-review-widget .yrw-modal-actions {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
.yara-review-widget .yrw-modal-platform {
display: inline-flex;
align-items: center;
justify-content:center;
gap: 10px;
padding: 16px 12px;
border: 1px solid var(--yrw-border-hover);
background: var(--yrw-green-dark);
color: var(--yrw-text-primary);
font-family: var(--yrw-font-body);
font-weight: 500;
font-size: 11px;
letter-spacing: 2px;
text-transform: uppercase;
cursor: pointer;
transition: all 0.2s;
min-height: 54px;
}
.yara-review-widget .yrw-modal-platform:hover {
border-color: var(--yrw-gold);
background: var(--yrw-green-light);
transform: translateY(-1px);
}
.yara-review-widget .yrw-modal-platform:active {
transform: translateY(0);
}
.yara-review-widget .yrw-modal-platform-icon {
width: 22px;
height: 22px;
flex-shrink: 0;
}
@keyframes yrwModalFade {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes yrwModalRise {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
/* ── Responsive ────────────────────────── */
@media (max-width: 768px) {
.yara-review-widget .yrw-hero-image { height: 40vh; min-height: 280px; }
.yara-review-widget .yrw-hero-title-section { padding: 40px 24px 40px; }
.yara-review-widget .yrw-title { font-size: 28px; letter-spacing: 4px; }
.yara-review-widget .yrw-main-section { padding: 50px 24px; }
.yara-review-widget .yrw-tag { padding: 10px 20px; font-size: 10px; }
.yara-review-widget .yrw-step-header { gap: 12px; }
.yara-review-widget .yrw-step-num { font-size: 26px; }
.yara-review-widget .yrw-step-title { font-size: 10px; letter-spacing: 3px; }
.yara-review-widget .yrw-rewrite-row { flex-direction: column; }
.yara-review-widget .yrw-rewrite-btn { width: 100%; }
.yara-review-widget .yrw-lang-section { padding: 30px 24px; }
.yara-review-widget .yrw-footer-cta { padding: 50px 24px; }
.yara-review-widget .yrw-footer-title { font-size: 22px; letter-spacing: 4px; }
.yara-review-widget .yrw-modal-card { padding: 36px 22px 26px; }
.yara-review-widget .yrw-modal-title { font-size: 18px; letter-spacing: 3px; }
.yara-review-widget .yrw-modal-actions { grid-template-columns: 1fr; }
}
</style>
<!-- ── Hero image ─────────────────────────── -->
<div class="yrw-hero-image">
<img
src="https://yarapuertoportals.com/wp-content/uploads/yara-review-hero-robata.jpg"
alt=""
loading="lazy"
/>
</div>
<!-- ── Hero title ─────────────────────────── -->
<header class="yrw-hero-title-section">
<div class="yrw-eyebrow" data-i18n="page_eyebrow"></div>
<h1 class="yrw-title" data-i18n="page_title"></h1>
<div class="yrw-title-rule"></div>
<p class="yrw-subtitle" data-i18n="page_subtitle"></p>
</header>
<!-- ── Main card (3 steps) ────────────────── -->
<main class="yrw-main-section">
<div class="yrw-card">
<!-- Step 1 — Tags -->
<section class="yrw-step">
<div class="yrw-step-header">
<span class="yrw-step-num">01</span>
<span class="yrw-step-title" data-i18n="step1_label"></span>
</div>
<div class="yrw-tag-grid" id="yrwTagGrid"><!-- pills injected by widget.js --></div>
</section>
<!-- Step 2 — Review box + rewrite row -->
<section class="yrw-step">
<div class="yrw-step-header">
<span class="yrw-step-num">02</span>
<span class="yrw-step-title" data-i18n="step2_label"></span>
</div>
<div class="yrw-review-wrap">
<textarea
id="yrwReviewText"
class="yrw-textarea"
maxlength="1500"
data-i18n-attr="placeholder:textarea_placeholder"
></textarea>
<div class="yrw-ai-badge" id="yrwAiBadge">
<span class="yrw-ai-dot"></span>
<span data-i18n="ai_writing"></span>
</div>
<div class="yrw-char-count">
<span id="yrwCharCount">0</span><span data-i18n="char_max_label"></span>
</div>
</div>
<div class="yrw-rewrite" id="yrwRewrite">
<label class="yrw-rewrite-label" data-i18n="rewrite_label"></label>
<div class="yrw-rewrite-row">
<input
id="yrwRewriteInput"
class="yrw-rewrite-input"
type="text"
data-i18n-attr="placeholder:rewrite_placeholder"
/>
<button
type="button"
class="yrw-rewrite-btn"
id="yrwRewriteBtn"
data-i18n="rewrite_btn"
></button>
</div>
</div>
</section>
<!-- Step 3 — CTA -->
<section class="yrw-step yrw-cta-section">
<div class="yrw-step-header">
<span class="yrw-step-num">03</span>
<span class="yrw-step-title" data-i18n="step3_label"></span>
</div>
<button type="button" class="yrw-btn-gold" id="yrwCtaBtn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
<span data-i18n="cta_btn"></span>
</button>
<p class="yrw-cta-hint" data-i18n="cta_hint"></p>
</section>
</div>
</main>
<!-- ── Language switcher ──────────────────── -->
<div class="yrw-lang-section">
<div class="yrw-lang-switcher" id="yrwLangSwitcher">
<button type="button" class="yrw-lang-btn" data-locale="es" data-i18n="lang_es"></button>
<button type="button" class="yrw-lang-btn" data-locale="en" data-i18n="lang_en"></button>
<button type="button" class="yrw-lang-btn" data-locale="de" data-i18n="lang_de"></button>
</div>
</div>
<!-- ── Footer CTA ─────────────────────────── -->
<footer class="yrw-footer-cta">
<div class="yrw-eyebrow" data-i18n="footer_eyebrow"></div>
<h2 class="yrw-footer-title" data-i18n="footer_title"></h2>
<div class="yrw-ctas">
<a class="yrw-btn-ghost" id="yrwBookBtn"
href="https://www.sevenrooms.com/explore/yaraportals/reservations/create/search"
target="_blank" rel="noopener"
data-i18n="btn_book"></a>
<a class="yrw-btn-ghost" id="yrwMenusBtn"
href="/menus/"
data-i18n="btn_menus"></a>
</div>
</footer>
<!-- ── Publish modal ──────────────────────── -->
<div class="yrw-modal" id="yrwPublishModal" role="dialog" aria-modal="true" aria-hidden="true" aria-labelledby="yrwModalTitle">
<div class="yrw-modal-backdrop" data-yrw-modal-close></div>
<div class="yrw-modal-card" role="document">
<button type="button" class="yrw-modal-close" data-yrw-modal-close aria-label="Close">×</button>
<div class="yrw-modal-eyebrow" data-i18n="modal_eyebrow"></div>
<h3 class="yrw-modal-title" id="yrwModalTitle" data-i18n="modal_title"></h3>
<p class="yrw-modal-subtitle" data-i18n="modal_subtitle"></p>
<div class="yrw-modal-preview-wrap">
<div class="yrw-modal-preview" id="yrwModalPreview"></div>
<button type="button" class="yrw-modal-copy" id="yrwModalCopyBtn" data-i18n="modal_copy_btn"></button>
<span class="yrw-modal-copy-status" id="yrwModalCopyStatus" data-i18n="modal_copied_status"></span>
</div>
<p class="yrw-modal-instruction" data-i18n="modal_instruction"></p>
<div class="yrw-modal-actions">
<button type="button" class="yrw-modal-platform yrw-modal-platform-google" data-platform="google">
<svg class="yrw-modal-platform-icon" viewBox="0 0 24 24" aria-hidden="true">
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84C6.71 7.31 9.14 5.38 12 5.38z"/>
</svg>
<span class="yrw-modal-platform-label" data-i18n="modal_btn_google"></span>
</button>
<button type="button" class="yrw-modal-platform yrw-modal-platform-tripadvisor" data-platform="tripadvisor">
<svg class="yrw-modal-platform-icon" viewBox="0 0 24 24" aria-hidden="true">
<circle cx="12" cy="12" r="11" fill="#34E0A1"/>
<circle cx="8.2" cy="12.6" r="2.7" fill="#000"/>
<circle cx="15.8" cy="12.6" r="2.7" fill="#000"/>
<circle cx="8.2" cy="12.6" r="0.9" fill="#fff"/>
<circle cx="15.8" cy="12.6" r="0.9" fill="#fff"/>
</svg>
<span class="yrw-modal-platform-label" data-i18n="modal_btn_tripadvisor"></span>
</button>
<button type="button" class="yrw-modal-platform yrw-modal-platform-trustpilot" data-platform="trustpilot">
<svg class="yrw-modal-platform-icon" viewBox="0 0 24 24" aria-hidden="true">
<path fill="#00B67A" d="M12 2l2.6 7.6h8l-6.5 4.7 2.5 7.7L12 17.3 5.4 22l2.5-7.7L1.4 9.6h8L12 2z"/>
</svg>
<span class="yrw-modal-platform-label" data-i18n="modal_btn_trustpilot"></span>
</button>
</div>
</div>
</div>
<script>
/* ─────────────────────────────────────────
YARA Review Widget — translations + core logic
Locales: es, en, de. Default locale is baked at build time
(build.sh substitutes en per output file).
───────────────────────────────────────── */
/* ── Configuration ─────────────────────── */
const N8N_BASE = "https://webhook.dev.flapconsulting.com/webhook";
const GOOGLE_REVIEW_URL = "https://g.page/r/CacB6K-byvcCEBM/review";
const TRIPADVISOR_REVIEW_URL = "https://www.tripadvisor.com/UserReviewEdit-g580293-d25413859-YARA_Portals-Calvia_Majorca_Balearic_Islands.html";
const TRUSTPILOT_REVIEW_URL = "https://www.trustpilot.com/evaluate/yarapuertoportals.com";
const PLATFORM_URLS = {
google: GOOGLE_REVIEW_URL,
tripadvisor: TRIPADVISOR_REVIEW_URL,
trustpilot: TRUSTPILOT_REVIEW_URL
};
const TYPE_SPEED_MS = 12;
const CHAR_MAX = 1500;
const DEFAULT_LOCALE = "en";
/* ── Translations ──────────────────────── */
const TRANSLATIONS = {
es: {
html_lang: "es",
doc_title: "Comparte tu Experiencia | YARA Restaurant Puerto Portals",
page_eyebrow: "Tu opinión importa",
page_title: "Comparte tu Experiencia",
page_subtitle: "Tu feedback nos ayuda a seguir creando momentos únicos. Cuéntanos qué te ha parecido tu visita a YARA.",
step1_label: "¿Qué destacarías?",
step2_label: "Escribe tu reseña",
step3_label: "Publica tu reseña",
tags: [
{ id: "comida", label: "Comida" },
{ id: "servicio", label: "Servicio" },
{ id: "ambiente", label: "Ambiente" },
{ id: "presentacion", label: "Presentación" },
{ id: "ubicacion", label: "Ubicación" },
{ id: "sushi", label: "Sushi" },
{ id: "degustacion", label: "Degustación" },
{ id: "vinos", label: "Vinos" }
],
textarea_placeholder: "Cuéntanos tu experiencia en YARA...",
char_max_label: "/ 1500",
ai_writing: "IA escribiendo…",
rewrite_label: "¿Quieres afinarla?",
rewrite_placeholder: "ej. hazla más corta, menciona la terraza…",
rewrite_btn: "Reescribir",
cta_btn: "Publica tu reseña",
cta_hint: "Elige Google, TripAdvisor o Trustpilot en el siguiente paso.",
copied: "¡Copiado!",
modal_eyebrow: "Listo para publicar",
modal_title: "¡Tu reseña está lista!",
modal_subtitle: "Elige dónde publicarla",
modal_instruction: "Tu texto ya se ha copiado. Pégalo con Ctrl+V (o Cmd+V en Mac) en la página que se abrirá.",
modal_btn_google: "Google",
modal_btn_tripadvisor: "TripAdvisor",
modal_btn_trustpilot: "Trustpilot",
modal_copy_btn: "Copiar",
modal_copied_status: "¡Copiado!",
footer_eyebrow: "Gracias por tu visita",
footer_title: "Esperamos verte pronto",
btn_book: "Reservar Mesa",
btn_menus: "Ver Menús",
lang_es: "Español",
lang_en: "English",
lang_de: "Deutsch"
},
en: {
html_lang: "en",
doc_title: "Share Your Experience | YARA Restaurant Puerto Portals",
page_eyebrow: "Your opinion matters",
page_title: "Share Your Experience",
page_subtitle: "Your feedback helps us continue creating unique moments. Tell us about your visit to YARA.",
step1_label: "What stood out?",
step2_label: "Write your review",
step3_label: "Publish your review",
tags: [
{ id: "comida", label: "Food" },
{ id: "servicio", label: "Service" },
{ id: "ambiente", label: "Ambience" },
{ id: "presentacion", label: "Presentation" },
{ id: "ubicacion", label: "Location" },
{ id: "sushi", label: "Sushi" },
{ id: "degustacion", label: "Tasting Menu" },
{ id: "vinos", label: "Wines" }
],
textarea_placeholder: "Tell us about your experience at YARA...",
char_max_label: "/ 1500",
ai_writing: "AI is writing…",
rewrite_label: "Want to refine it?",
rewrite_placeholder: "e.g. make it shorter, mention the terrace…",
rewrite_btn: "Rewrite",
cta_btn: "Publish your review",
cta_hint: "Choose Google, TripAdvisor or Trustpilot in the next step.",
copied: "Copied!",
modal_eyebrow: "Ready to publish",
modal_title: "Your review is ready!",
modal_subtitle: "Choose where to publish it",
modal_instruction: "Your text has been copied. Paste it with Ctrl+V (or Cmd+V on Mac) on the page that will open.",
modal_btn_google: "Google",
modal_btn_tripadvisor: "TripAdvisor",
modal_btn_trustpilot: "Trustpilot",
modal_copy_btn: "Copy",
modal_copied_status: "Copied!",
footer_eyebrow: "Thank you for visiting",
footer_title: "We hope to see you soon",
btn_book: "Book a Table",
btn_menus: "View Menus",
lang_es: "Español",
lang_en: "English",
lang_de: "Deutsch"
},
de: {
html_lang: "de",
doc_title: "Teilen Sie Ihre Erfahrung | YARA Restaurant Puerto Portals",
page_eyebrow: "Ihre Meinung zählt",
page_title: "Teilen Sie Ihre Erfahrung",
page_subtitle: "Ihr Feedback hilft uns, weiterhin einzigartige Momente zu schaffen. Erzählen Sie uns von Ihrem Besuch bei YARA.",
step1_label: "Was hat Sie beeindruckt?",
step2_label: "Schreiben Sie Ihre Bewertung",
step3_label: "Bewertung veröffentlichen",
tags: [
{ id: "comida", label: "Essen" },
{ id: "servicio", label: "Service" },
{ id: "ambiente", label: "Atmosphäre" },
{ id: "presentacion", label: "Präsentation" },
{ id: "ubicacion", label: "Lage" },
{ id: "sushi", label: "Sushi" },
{ id: "degustacion", label: "Verkostungsmenü" },
{ id: "vinos", label: "Weine" }
],
textarea_placeholder: "Erzählen Sie uns von Ihrem Erlebnis bei YARA...",
char_max_label: "/ 1500",
ai_writing: "KI schreibt…",
rewrite_label: "Möchten Sie sie anpassen?",
rewrite_placeholder: "z. B. kürzer machen, die Terrasse erwähnen…",
rewrite_btn: "Neu schreiben",
cta_btn: "Bewertung veröffentlichen",
cta_hint: "Wählen Sie Google, TripAdvisor oder Trustpilot im nächsten Schritt.",
copied: "Kopiert!",
modal_eyebrow: "Bereit zum Veröffentlichen",
modal_title: "Ihre Bewertung ist fertig!",
modal_subtitle: "Wählen Sie, wo Sie sie veröffentlichen möchten",
modal_instruction: "Ihr Text wurde kopiert. Fügen Sie ihn mit Strg+V (oder Cmd+V auf Mac) auf der Seite ein, die sich öffnet.",
modal_btn_google: "Google",
modal_btn_tripadvisor: "TripAdvisor",
modal_btn_trustpilot: "Trustpilot",
modal_copy_btn: "Kopieren",
modal_copied_status: "Kopiert!",
footer_eyebrow: "Danke für Ihren Besuch",
footer_title: "Wir hoffen, Sie bald wiederzusehen",
btn_book: "Tisch reservieren",
btn_menus: "Menüs ansehen",
lang_es: "Español",
lang_en: "English",
lang_de: "Deutsch"
}
};
/* ── Widget IIFE ───────────────────────── */
(function () {
var root = document.currentScript && document.currentScript.closest('.yara-review-widget');
var widget = root || document.querySelectorAll('.yara-review-widget')[
document.querySelectorAll('.yara-review-widget').length - 1
];
if (!widget) return;
var baseDefault = TRANSLATIONS[DEFAULT_LOCALE] ? DEFAULT_LOCALE : 'es';
var currentLocale = baseDefault;
var currentTags = [];
var typingTimer = null;
/* ── locale init ─────────────────────── */
function detectLocale() {
var params = new URLSearchParams(window.location.search);
var fromQuery = params.get('locale');
if (fromQuery && TRANSLATIONS[fromQuery]) return fromQuery;
var path = window.location.pathname.toLowerCase();
if (path.startsWith('/en/') || path === '/en') return 'en';
if (path.startsWith('/de/') || path === '/de') return 'de';
if (path.startsWith('/es/') || path === '/es') return 'es';
return baseDefault;
}
/* ── translations ────────────────────── */
function applyTranslations() {
var t = TRANSLATIONS[currentLocale];
if (t.html_lang) document.documentElement.lang = t.html_lang;
if (t.doc_title) document.title = t.doc_title;
widget.querySelectorAll('[data-i18n]').forEach(function (el) {
var key = el.getAttribute('data-i18n');
if (t[key] !== undefined && typeof t[key] === 'string') {
el.textContent = t[key];
}
});
widget.querySelectorAll('[data-i18n-attr]').forEach(function (el) {
var spec = el.getAttribute('data-i18n-attr');
var parts = spec.split(':');
var attr = parts[0];
var key = parts[1];
if (attr && key && t[key] !== undefined) {
el.setAttribute(attr, t[key]);
}
});
var grid = widget.querySelector('#yrwTagGrid');
grid.innerHTML = '';
t.tags.forEach(function (tag) {
var el = document.createElement('button');
el.type = 'button';
el.className = 'yrw-tag';
el.dataset.tagId = tag.id;
el.textContent = tag.label;
el.addEventListener('click', function () { selectTag(el); });
grid.appendChild(el);
});
widget.querySelectorAll('.yrw-lang-btn').forEach(function (btn) {
btn.classList.toggle('active', btn.dataset.locale === currentLocale);
});
}
/* ── tag selection ───────────────────── */
function selectTag(el) {
var wasSelected = el.classList.contains('selected');
widget.querySelectorAll('.yrw-tag.selected').forEach(function (t) { t.classList.remove('selected'); });
if (!wasSelected) el.classList.add('selected');
currentTags = Array.from(widget.querySelectorAll('.yrw-tag.selected'))
.map(function (t) { return { id: t.dataset.tagId, label: t.textContent.trim() }; });
var rewrite = widget.querySelector('#yrwRewrite');
if (currentTags.length > 0) {
rewrite.classList.add('visible');
generateReview();
} else {
var ta = widget.querySelector('#yrwReviewText');
ta.value = '';
updateCharCount();
widget.querySelector('#yrwAiBadge').classList.remove('visible');
rewrite.classList.remove('visible');
}
}
/* ── typewriter ──────────────────────── */
function typeText(text) {
clearTimeout(typingTimer);
var ta = widget.querySelector('#yrwReviewText');
var badge = widget.querySelector('#yrwAiBadge');
badge.classList.add('visible');
ta.value = '';
updateCharCount();
var i = 0;
(function step() {
if (i <= text.length) {
ta.value = text.slice(0, i++);
updateCharCount();
typingTimer = setTimeout(step, TYPE_SPEED_MS);
} else {
badge.classList.remove('visible');
}
})();
}
/* ── character counter ───────────────── */
function updateCharCount() {
var ta = widget.querySelector('#yrwReviewText');
var counter = widget.querySelector('#yrwCharCount');
if (counter) counter.textContent = String(ta.value.length);
}
/* ── n8n calls ───────────────────────── */
function generateReview() {
if (!currentTags.length) return;
var badge = widget.querySelector('#yrwAiBadge');
badge.classList.add('visible');
fetch(N8N_BASE + '/generate-yara-review', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
category_selected: currentTags.map(function (t) { return t.label; }).join(', '),
locale: currentLocale
})
})
.then(function (r) { return r.json(); })
.then(function (data) {
var text = data.generated_review || data.cleaned_review || data.text || "Error generating review.";
typeText(text);
})
.catch(function (err) {
console.error('[yara-review-widget] generate error:', err);
badge.classList.remove('visible');
});
}
function doRewrite() {
var instruction = widget.querySelector('#yrwRewriteInput').value.trim();
var currentText = widget.querySelector('#yrwReviewText').value.trim();
if (!currentText || !instruction) return;
var badge = widget.querySelector('#yrwAiBadge');
var rewriteBtn = widget.querySelector('#yrwRewriteBtn');
badge.classList.add('visible');
var originalBtnText = rewriteBtn.innerText;
rewriteBtn.innerText = '…';
fetch(N8N_BASE + '/revise-yara-review', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
original_text: currentText,
user_feedback: instruction,
locale: currentLocale
})
})
.then(function (r) { return r.json(); })
.then(function (data) {
var text = data.revised_review || data.cleaned_review || data.text || "Error revising review.";
typeText(text);
widget.querySelector('#yrwRewriteInput').value = '';
})
.catch(function (err) {
console.error('[yara-review-widget] revise error:', err);
badge.classList.remove('visible');
})
.finally(function () {
rewriteBtn.innerText = originalBtnText;
});
}
/* ── publish modal ───────────────────── */
var copyStatusTimer = null;
function copyToClipboard(text) {
// Try the modern Clipboard API first; fall back to a temp textarea for
// older browsers / non-secure contexts. Returns a Promise that resolves
// on success and rejects on failure (both branches).
if (navigator.clipboard && navigator.clipboard.writeText) {
return navigator.clipboard.writeText(text);
}
return new Promise(function (resolve, reject) {
try {
var ta = document.createElement('textarea');
ta.value = text;
ta.setAttribute('readonly', '');
ta.style.position = 'absolute';
ta.style.left = '-9999px';
document.body.appendChild(ta);
ta.select();
var ok = document.execCommand('copy');
document.body.removeChild(ta);
ok ? resolve() : reject(new Error('execCommand copy failed'));
} catch (err) { reject(err); }
});
}
function flashCopiedStatus() {
var status = widget.querySelector('#yrwModalCopyStatus');
var copyBtn = widget.querySelector('#yrwModalCopyBtn');
if (!status) return;
clearTimeout(copyStatusTimer);
status.classList.add('visible');
if (copyBtn) copyBtn.classList.add('copied');
copyStatusTimer = setTimeout(function () {
status.classList.remove('visible');
if (copyBtn) copyBtn.classList.remove('copied');
}, 1800);
}
function openPublishModal() {
var text = widget.querySelector('#yrwReviewText').value;
if (!text.trim()) return;
var modal = widget.querySelector('#yrwPublishModal');
var preview = widget.querySelector('#yrwModalPreview');
preview.textContent = text;
modal.classList.add('visible');
modal.setAttribute('aria-hidden', 'false');
// Copy-on-open so the user can immediately paste if they go straight
// to a platform button without clicking the in-modal copy button.
copyToClipboard(text).then(flashCopiedStatus).catch(function (err) {
console.error('[yara-review-widget] clipboard error on open:', err);
});
}
function closePublishModal() {
var modal = widget.querySelector('#yrwPublishModal');
modal.classList.remove('visible');
modal.setAttribute('aria-hidden', 'true');
clearTimeout(copyStatusTimer);
var status = widget.querySelector('#yrwModalCopyStatus');
var copyBtn = widget.querySelector('#yrwModalCopyBtn');
if (status) status.classList.remove('visible');
if (copyBtn) copyBtn.classList.remove('copied');
}
function publishTo(platform) {
var text = widget.querySelector('#yrwReviewText').value;
if (!text.trim()) return;
var url = PLATFORM_URLS[platform] || GOOGLE_REVIEW_URL;
// Always re-copy fresh on every click — even repeat clicks after a
// platform window was already opened. Clipboard may have been
// overwritten (user activity in the other tab, another copy, etc.).
copyToClipboard(text)
.then(function () { flashCopiedStatus(); })
.catch(function (err) { console.error('[yara-review-widget] clipboard error:', err); })
.finally(function () {
window.open(url, '_blank', 'noopener');
});
}
/* ── wire up ─────────────────────────── */
function init() {
currentLocale = detectLocale();
applyTranslations();
var ta = widget.querySelector('#yrwReviewText');
ta.addEventListener('input', updateCharCount);
updateCharCount();
widget.querySelector('#yrwRewriteBtn').addEventListener('click', doRewrite);
widget.querySelector('#yrwRewriteInput').addEventListener('keydown', function (e) {
if (e.key === 'Enter') { e.preventDefault(); doRewrite(); }
});
widget.querySelector('#yrwCtaBtn').addEventListener('click', openPublishModal);
// Modal: close + platform + copy-again
widget.querySelectorAll('[data-yrw-modal-close]').forEach(function (el) {
el.addEventListener('click', closePublishModal);
});
widget.querySelector('#yrwModalCopyBtn').addEventListener('click', function () {
var text = widget.querySelector('#yrwReviewText').value;
if (!text.trim()) return;
copyToClipboard(text).then(flashCopiedStatus).catch(function (err) {
console.error('[yara-review-widget] clipboard error:', err);
});
});
widget.querySelectorAll('.yrw-modal-platform').forEach(function (btn) {
btn.addEventListener('click', function () { publishTo(btn.dataset.platform); });
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
var modal = widget.querySelector('#yrwPublishModal');
if (modal && modal.classList.contains('visible')) closePublishModal();
}
});
widget.querySelectorAll('.yrw-lang-btn').forEach(function (btn) {
btn.addEventListener('click', function () {
var locale = btn.dataset.locale;
if (!TRANSLATIONS[locale] || locale === currentLocale) return;
currentLocale = locale;
currentTags = [];
ta.value = '';
updateCharCount();
widget.querySelector('#yrwRewrite').classList.remove('visible');
widget.querySelector('#yrwAiBadge').classList.remove('visible');
applyTranslations();
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
</section>
<!-- ─────────────────────────────────────────
YARA Google Reviews Display Widget — Bricks Code element
Paste the built file from dist/ into a Bricks "Code" element with
"Execute Code" OFF. build.sh inlines CSS, JS, reviews JSON, and
locale labels between the markers below.
───────────────────────────────────────── -->
<section class="yara-page-content yara-bricks-module yara-google-reviews">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
/* YARA Google Reviews Display Widget — scoped styles.
Aligned with styles/tokens.css (authoritative Yara design system).
Every selector is prefixed .yara-google-reviews to prevent bleed. */
.yara-google-reviews {
/* Canonical Yara tokens (mirrored here so the widget is self-contained
when pasted into Bricks without tokens.css in <head>). */
--ygr-bg: #0D1B0E;
--ygr-surface: #152617;
--ygr-surface-2: #1C3220;
--ygr-gold: #A2916B;
--ygr-gold-dark: #8A7A59;
--ygr-text: #FFFFFF;
--ygr-muted: rgba(255, 255, 255, 0.68);
--ygr-border: rgba(162, 145, 107, 0.3);
--ygr-star: #E0B65A;
--ygr-radius: 4px;
font-family: 'Montserrat', system-ui, -apple-system, sans-serif;
color: var(--ygr-text);
background: var(--ygr-bg);
padding: 75px 24px;
box-sizing: border-box;
width: 100%;
}
.yara-google-reviews *,
.yara-google-reviews *::before,
.yara-google-reviews *::after { box-sizing: border-box; }
/* ── Header badge ───────────────────────── */
.yara-google-reviews .ygr-header {
display: flex; align-items: center; justify-content: center;
gap: 32px; flex-wrap: wrap;
margin: 0 auto 12px; max-width: 1100px;
}
.yara-google-reviews .ygr-google-badge {
display: inline-flex; align-items: center; gap: 10px;
background: transparent;
border: 1px solid var(--ygr-border);
border-radius: 999px; padding: 10px 20px;
}
.yara-google-reviews .ygr-g-logo { width: 20px; height: 20px; }
.yara-google-reviews .ygr-badge-label {
font-weight: 500; font-size: 14px; letter-spacing: 0.05em;
color: var(--ygr-text);
}
.yara-google-reviews .ygr-rating-block {
display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
}
.yara-google-reviews .ygr-rating-value {
font-size: 32px; font-weight: 300;
color: var(--ygr-gold); line-height: 1;
letter-spacing: 0.02em;
}
.yara-google-reviews .ygr-rating-stars {
display: inline-flex; gap: 2px; font-size: 20px;
color: var(--ygr-star); letter-spacing: 1px;
}
.yara-google-reviews .ygr-star--half {
background: linear-gradient(90deg, var(--ygr-star) 50%, rgba(224, 182, 90, 0.3) 50%);
-webkit-background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent;
}
.yara-google-reviews .ygr-rating-count {
font-size: 13px; color: var(--ygr-muted);
font-weight: 500;
}
.yara-google-reviews .ygr-rating-count strong {
color: var(--ygr-text); font-weight: 600;
}
.yara-google-reviews .ygr-ribbon {
display: inline-flex; align-items: center;
background: transparent; color: var(--ygr-gold);
font-size: 12px; font-weight: 500;
text-transform: uppercase; letter-spacing: 0.18em;
padding: 8px 16px;
border: 1px solid var(--ygr-gold);
border-radius: var(--ygr-radius);
}
/* ── Eyebrow + heading (Yara pattern) ───── */
.yara-google-reviews .ygr-eyebrow {
display: block; text-align: center;
font-size: 12px; font-weight: 500;
letter-spacing: 0.18em; text-transform: uppercase;
color: var(--ygr-gold);
margin: 32px 0 14px;
}
.yara-google-reviews .ygr-heading {
text-align: center;
font-family: 'Montserrat', sans-serif;
font-size: clamp(28px, 3.5vw, 45px);
font-weight: 300;
letter-spacing: 0.05em;
text-transform: uppercase;
line-height: 1.4;
color: var(--ygr-text);
margin: 0 auto 48px;
max-width: 900px;
}
/* ── Carousel ──────────────────────────── */
.yara-google-reviews .ygr-carousel {
position: relative; max-width: 1200px;
margin: 0 auto; padding: 0 56px;
}
.yara-google-reviews .ygr-track {
display: flex; gap: 20px;
overflow-x: auto; scroll-snap-type: x mandatory;
scroll-behavior: smooth;
scrollbar-width: none; -ms-overflow-style: none;
padding: 4px 4px 20px;
}
.yara-google-reviews .ygr-track::-webkit-scrollbar { display: none; }
.yara-google-reviews .ygr-card {
flex: 0 0 calc((100% - 40px) / 3);
min-width: 280px; max-width: 400px;
background: var(--ygr-surface);
border: 1px solid var(--ygr-border);
border-radius: var(--ygr-radius);
padding: 28px 26px 24px;
scroll-snap-align: start;
display: flex; flex-direction: column; gap: 16px;
transition: border-color 0.3s ease, background 0.3s ease, transform 0.3s ease;
}
.yara-google-reviews .ygr-card:hover {
transform: translateY(-3px);
border-color: var(--ygr-gold);
background: var(--ygr-surface-2);
}
.yara-google-reviews .ygr-card-head {
display: flex; align-items: center; gap: 12px;
}
.yara-google-reviews .ygr-avatar {
width: 44px; height: 44px; border-radius: 50%;
background: var(--ygr-gold); color: var(--ygr-bg);
font-family: inherit;
font-weight: 600; font-size: 18px;
letter-spacing: 0.02em;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
}
.yara-google-reviews .ygr-meta {
display: flex; flex-direction: column; min-width: 0; flex: 1;
}
.yara-google-reviews .ygr-author {
font-weight: 600; font-size: 14px;
letter-spacing: 0.02em;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
color: var(--ygr-text);
}
.yara-google-reviews .ygr-date {
font-size: 12px; color: var(--ygr-muted);
margin-top: 4px; font-weight: 500;
letter-spacing: 0.03em;
}
.yara-google-reviews .ygr-card-g {
width: 18px; height: 18px; flex-shrink: 0; opacity: 0.9;
}
.yara-google-reviews .ygr-card-stars {
color: var(--ygr-star); font-size: 15px; letter-spacing: 2px;
}
.yara-google-reviews .ygr-text {
font-size: 15px; line-height: 1.6;
color: var(--ygr-text); margin: 0;
font-weight: 500;
display: -webkit-box; -webkit-box-orient: vertical;
-webkit-line-clamp: 6; overflow: hidden;
}
.yara-google-reviews .ygr-card--expanded .ygr-text {
display: block; -webkit-line-clamp: unset; overflow: visible;
}
.yara-google-reviews .ygr-toggle {
background: none; border: none;
color: var(--ygr-gold);
font-family: inherit; font-size: 11px; font-weight: 500;
text-transform: uppercase; letter-spacing: 0.18em;
cursor: pointer; padding: 0; align-self: flex-start;
transition: color 0.2s ease;
}
.yara-google-reviews .ygr-toggle:hover { color: var(--ygr-text); }
/* ── Nav arrows ───────────────────────── */
.yara-google-reviews .ygr-nav {
position: absolute; top: 50%; transform: translateY(-50%);
width: 44px; height: 44px; border-radius: 50%;
background: transparent;
border: 1px solid var(--ygr-border);
color: var(--ygr-gold); cursor: pointer;
display: flex; align-items: center; justify-content: center;
transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease, transform 0.3s ease;
z-index: 2;
}
.yara-google-reviews .ygr-nav:hover {
background: var(--ygr-gold); color: var(--ygr-bg);
border-color: var(--ygr-gold);
transform: translateY(-50%) scale(1.05);
}
.yara-google-reviews .ygr-nav:disabled {
opacity: 0.3; cursor: not-allowed;
transform: translateY(-50%);
background: transparent; color: var(--ygr-muted);
border-color: var(--ygr-border);
}
.yara-google-reviews .ygr-nav svg { width: 18px; height: 18px; }
.yara-google-reviews .ygr-nav--prev { left: 0; }
.yara-google-reviews .ygr-nav--next { right: 0; }
/* ── Dots ─────────────────────────────── */
.yara-google-reviews .ygr-dots {
display: flex; gap: 10px; justify-content: center;
margin: 18px 0 40px;
}
.yara-google-reviews .ygr-dot {
width: 7px; height: 7px; border-radius: 50%;
background: transparent;
border: 1px solid var(--ygr-border);
padding: 0; cursor: pointer;
transition: background 0.3s ease, transform 0.3s ease, border-color 0.3s ease;
}
.yara-google-reviews .ygr-dot:hover { border-color: var(--ygr-gold); }
.yara-google-reviews .ygr-dot--active {
background: var(--ygr-gold); border-color: var(--ygr-gold);
transform: scale(1.3);
}
/* ── CTA (Yara outline button) ────────── */
.yara-google-reviews .ygr-cta-wrap {
display: flex; justify-content: center; margin-top: 8px;
}
.yara-google-reviews .ygr-cta {
display: inline-flex; align-items: center; gap: 12px;
background: transparent; color: var(--ygr-gold);
font-family: inherit;
font-weight: 500; font-size: 12px;
text-transform: uppercase; letter-spacing: 0.18em;
padding: 16px 40px;
border: 1px solid var(--ygr-gold);
border-radius: var(--ygr-radius);
text-decoration: none;
transition: background 0.3s ease, color 0.3s ease;
}
.yara-google-reviews .ygr-cta:hover {
background: var(--ygr-gold); color: var(--ygr-bg);
}
/* ── Responsive ───────────────────────── */
@media (max-width: 960px) {
.yara-google-reviews { padding: 48px 24px; }
.yara-google-reviews .ygr-card { flex: 0 0 calc((100% - 20px) / 2); }
}
@media (max-width: 640px) {
.yara-google-reviews { padding: 40px 16px; }
.yara-google-reviews .ygr-header { gap: 20px; }
.yara-google-reviews .ygr-carousel { padding: 0 12px; }
.yara-google-reviews .ygr-nav { width: 38px; height: 38px; }
.yara-google-reviews .ygr-nav--prev { left: -6px; }
.yara-google-reviews .ygr-nav--next { right: -6px; }
.yara-google-reviews .ygr-card { flex: 0 0 85%; min-width: 260px; }
}
</style>
<!-- ── Header badge ─────────────────────────── -->
<header class="ygr-header">
<div class="ygr-google-badge">
<svg class="ygr-g-logo" viewBox="0 0 48 48" aria-hidden="true">
<path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
<path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
<path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
<path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
</svg>
<span class="ygr-badge-label">Google</span>
</div>
<div class="ygr-rating-block">
<span class="ygr-rating-value" id="ygrRatingValue">4.6</span>
<div class="ygr-rating-stars" aria-hidden="true">
<span class="ygr-star">★</span><span class="ygr-star">★</span><span class="ygr-star">★</span><span class="ygr-star">★</span><span class="ygr-star ygr-star--half">★</span>
</div>
<span class="ygr-rating-count">
<span data-i18n="basedOn"></span>
<strong id="ygrRatingCount">200+</strong>
<span data-i18n="reviews"></span>
</span>
</div>
<div class="ygr-ribbon" data-i18n="topRated"></div>
</header>
<span class="ygr-eyebrow" data-i18n="eyebrow"></span>
<h2 class="ygr-heading" data-i18n="heading"></h2>
<!-- ── Carousel ─────────────────────────── -->
<div class="ygr-carousel">
<button type="button" class="ygr-nav ygr-nav--prev" id="ygrPrev" data-i18n-attr="aria-label:prev">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6"/></svg>
</button>
<div class="ygr-track" id="ygrTrack">
<!-- cards injected by widget.js -->
</div>
<button type="button" class="ygr-nav ygr-nav--next" id="ygrNext" data-i18n-attr="aria-label:next">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 18 15 12 9 6"/></svg>
</button>
</div>
<div class="ygr-dots" id="ygrDots" role="tablist" aria-label="reviews pagination"></div>
<!-- ── CTA ─────────────────────────── -->
<div class="ygr-cta-wrap">
<a class="ygr-cta" id="ygrCta" href="#" target="_blank" rel="noopener" data-i18n="writeReview"></a>
</div>
<script>
window.__YGR_CONFIG__ = {"googleReviewUrl": "https://g.page/r/CacB6K-byvcCEBM/review", "ratingValue": "4.6", "ratingCount": "200+"};
</script>
<script>
window.__YGR_REVIEWS__ = [{"author_name": "James Walker", "author_initial": "J", "date_iso": "2025-09-20", "rating": 5, "text": "Outstanding dinner at YARA. The Japanese-Mediterranean fusion works beautifully. Robata was perfectly grilled, sashimi ultra fresh. Attentive service without being overbearing. Already planning our return visit."}, {"author_name": "Emma Thompson", "author_initial": "E", "date_iso": "2025-09-08", "rating": 5, "text": "Incredible evening on the terrace overlooking the marina. The tasting menu was a journey — the black cod miso and bluefin tuna were showstoppers. Thoughtful wine pairings. Absolute gem in Puerto Portals."}, {"author_name": "Oliver Smith", "author_initial": "O", "date_iso": "2025-08-25", "rating": 5, "text": "Elegant room, impeccable plating, bold flavors. The wagyu bao and spicy tuna uramaki were standouts. Staff were knowledgeable and warm. Pricey but worth every euro for a special occasion."}, {"author_name": "Charlotte Davies", "author_initial": "C", "date_iso": "2025-08-10", "rating": 5, "text": "Celebrated our anniversary here and it exceeded expectations. The sommelier's recommendations were spot on. Atmosphere is refined yet relaxed. Crab salad and hamachi usuzukuri will live rent-free in my head."}, {"author_name": "Henry Brooks", "author_initial": "H", "date_iso": "2025-07-27", "rating": 5, "text": "Top-tier experience from first to last bite. Presentation is art. Chef came over to explain the dishes — clear passion for the craft. Cocktails are inventive. Easily one of the best dinners in Mallorca this year."}, {"author_name": "Sophie Mitchell", "author_initial": "S", "date_iso": "2025-07-11", "rating": 5, "text": "Sunset terrace views are unreal, food is even better. The menu balances authentic Japanese technique with local ingredients. Service team handled our dietary needs without a hitch. Book well in advance."}, {"author_name": "William Clarke", "author_initial": "W", "date_iso": "2025-06-19", "rating": 5, "text": "Every course landed. The niguiri selection is excellent and the robatayaki has great smoke and char. Dessert was creative, not an afterthought. A proper fine-dining night without being stuffy."}, {"author_name": "Isabella Wright", "author_initial": "I", "date_iso": "2025-06-02", "rating": 5, "text": "Genuinely special restaurant. Thoughtful service, beautiful room, and food that remembers to be fun. The tataki and the seasonal tempura stole the show. Cocktail program is strong too."}, {"author_name": "George Harris", "author_initial": "G", "date_iso": "2025-05-15", "rating": 5, "text": "Came with a group of eight and the kitchen did not drop a plate. Timing between courses was perfect, flavors precise. Dry-aged fish dishes were exceptional. Will happily recommend to anyone visiting Portals."}, {"author_name": "Grace Edwards", "author_initial": "G", "date_iso": "2025-04-28", "rating": 5, "text": "A Mallorca highlight. The room has presence, the terrace has the view, and the food has the skill to match. Standouts: toro tartare, miso cod, yuzu dessert. We will be back next summer for sure."}];
</script>
<script>
window.__YGR_LOCALE__ = {"localeCode": "en", "labels": {"eyebrow": "Guest reviews", "heading": "What our guests say", "basedOn": "Based on", "reviews": "reviews", "topRated": "Top Rated Service", "readMore": "Read more", "readLess": "Read less", "writeReview": "Write a review", "prev": "Previous", "next": "Next"}};
</script>
<script>
/* YARA Google Reviews Display Widget — runtime.
Reads globals injected by build.sh:
window.__YGR_CONFIG__ — { googleReviewUrl, ratingValue, ratingCount }
window.__YGR_REVIEWS__ — array of curated review objects
window.__YGR_LOCALE__ — { localeCode, labels{...} }
No external libs. No network calls at runtime. */
(function () {
'use strict';
function log(event, payload) {
try {
var line = { widget: 'ygr', event: event };
for (var k in payload) if (Object.prototype.hasOwnProperty.call(payload, k)) line[k] = payload[k];
console.info('[ygr]', JSON.stringify(line));
} catch (_) { /* never throw from logger */ }
}
function el(tag, attrs, children) {
var node = document.createElement(tag);
if (attrs) {
for (var k in attrs) {
if (k === 'class') node.className = attrs[k];
else if (k === 'text') node.textContent = attrs[k];
else if (k === 'html') node.innerHTML = attrs[k];
else node.setAttribute(k, attrs[k]);
}
}
if (children) for (var i = 0; i < children.length; i++) if (children[i]) node.appendChild(children[i]);
return node;
}
function formatDate(iso, localeCode) {
try {
var d = new Date(iso);
if (isNaN(d.getTime())) return '';
return new Intl.DateTimeFormat(localeCode, { year: 'numeric', month: 'long' }).format(d);
} catch (_) { return iso || ''; }
}
function initialFor(name) {
if (!name) return '?';
var trimmed = String(name).trim();
return trimmed ? trimmed.charAt(0).toUpperCase() : '?';
}
function starsHtml(rating) {
var full = Math.max(0, Math.min(5, Math.round(rating || 0)));
return new Array(full + 1).join('★') + new Array(6 - full).join('☆');
}
function applyI18n(root, labels) {
var nodes = root.querySelectorAll('[data-i18n]');
for (var i = 0; i < nodes.length; i++) {
var key = nodes[i].getAttribute('data-i18n');
if (labels[key]) nodes[i].textContent = labels[key];
}
var attrNodes = root.querySelectorAll('[data-i18n-attr]');
for (var j = 0; j < attrNodes.length; j++) {
var spec = attrNodes[j].getAttribute('data-i18n-attr');
var parts = spec.split(':');
if (parts.length === 2 && labels[parts[1]]) attrNodes[j].setAttribute(parts[0], labels[parts[1]]);
}
}
function buildCard(review, labels, localeCode) {
var author = el('div', { class: 'ygr-author', text: review.author_name || '—' });
var date = el('div', { class: 'ygr-date', text: formatDate(review.date_iso, localeCode) });
var meta = el('div', { class: 'ygr-meta' }, [author, date]);
var avatar = el('div', { class: 'ygr-avatar', 'aria-hidden': 'true', text: review.author_initial || initialFor(review.author_name) });
var gLogo = el('div', { class: 'ygr-card-g', html: '<svg viewBox="0 0 48 48" aria-hidden="true"><path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/><path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/><path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/><path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/></svg>' });
var head = el('div', { class: 'ygr-card-head' }, [avatar, meta, gLogo]);
var stars = el('div', { class: 'ygr-card-stars', 'aria-label': (review.rating || 5) + ' / 5', text: starsHtml(review.rating || 5) });
var text = el('p', { class: 'ygr-text', text: review.text || '' });
var card = el('article', { class: 'ygr-card' }, [head, stars, text]);
// only add "read more" if text is long enough to likely clamp
if ((review.text || '').length > 220) {
var toggle = el('button', { type: 'button', class: 'ygr-toggle', text: labels.readMore });
toggle.addEventListener('click', function () {
var expanded = card.classList.toggle('ygr-card--expanded');
toggle.textContent = expanded ? labels.readLess : labels.readMore;
});
card.appendChild(toggle);
}
return card;
}
function init() {
var root = document.querySelector('.yara-google-reviews');
if (!root) { log('init_abort', { outcome: 'failure', error_code: 'root_missing' }); return; }
var config = window.__YGR_CONFIG__ || {};
var reviews = window.__YGR_REVIEWS__ || [];
var locale = window.__YGR_LOCALE__ || { localeCode: 'en', labels: {} };
var labels = locale.labels || {};
applyI18n(root, labels);
// Config-driven surface bits
var ratingVal = root.querySelector('#ygrRatingValue');
var ratingCount = root.querySelector('#ygrRatingCount');
var cta = root.querySelector('#ygrCta');
if (ratingVal && config.ratingValue) ratingVal.textContent = config.ratingValue;
if (ratingCount && config.ratingCount) ratingCount.textContent = config.ratingCount;
if (cta && config.googleReviewUrl) cta.setAttribute('href', config.googleReviewUrl);
var track = root.querySelector('#ygrTrack');
var dotsContainer = root.querySelector('#ygrDots');
var prevBtn = root.querySelector('#ygrPrev');
var nextBtn = root.querySelector('#ygrNext');
if (!track || !reviews.length) {
log('init_abort', { outcome: 'failure', error_code: 'no_reviews', count: reviews.length });
return;
}
// render cards
for (var i = 0; i < reviews.length; i++) {
track.appendChild(buildCard(reviews[i], labels, locale.localeCode));
}
// build dots (one per card)
var dots = [];
for (var d = 0; d < reviews.length; d++) {
(function (idx) {
var btn = el('button', { type: 'button', class: 'ygr-dot', role: 'tab', 'aria-label': 'review ' + (idx + 1) });
btn.addEventListener('click', function () { scrollToCard(idx); });
dotsContainer.appendChild(btn);
dots.push(btn);
})(d);
}
function cardWidth() {
var first = track.querySelector('.ygr-card');
if (!first) return 0;
var style = window.getComputedStyle(track);
var gap = parseInt(style.columnGap || style.gap || '20', 10) || 20;
return first.getBoundingClientRect().width + gap;
}
function scrollToCard(idx) {
var w = cardWidth();
track.scrollTo({ left: idx * w, behavior: 'smooth' });
}
function currentIndex() {
var w = cardWidth();
if (!w) return 0;
return Math.round(track.scrollLeft / w);
}
function updateUi() {
var idx = currentIndex();
for (var i = 0; i < dots.length; i++) {
dots[i].classList.toggle('ygr-dot--active', i === idx);
}
var maxIdx = reviews.length - 1;
if (prevBtn) prevBtn.disabled = idx <= 0;
if (nextBtn) nextBtn.disabled = idx >= maxIdx;
}
if (prevBtn) prevBtn.addEventListener('click', function () { scrollToCard(Math.max(0, currentIndex() - 1)); });
if (nextBtn) nextBtn.addEventListener('click', function () { scrollToCard(Math.min(reviews.length - 1, currentIndex() + 1)); });
track.addEventListener('scroll', function () {
window.requestAnimationFrame(updateUi);
});
// keyboard nav when carousel is focused
root.querySelector('.ygr-carousel').addEventListener('keydown', function (ev) {
if (ev.key === 'ArrowLeft') { ev.preventDefault(); scrollToCard(Math.max(0, currentIndex() - 1)); }
if (ev.key === 'ArrowRight') { ev.preventDefault(); scrollToCard(Math.min(reviews.length - 1, currentIndex() + 1)); }
});
window.addEventListener('resize', updateUi);
updateUi();
log('init_success', { outcome: 'success', locale: locale.localeCode, count: reviews.length });
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
</section>
The tranquillity of a boutique house.
The exclusive benefits of Hotel Es Port.