feat(site): horário sem barra de dias em cima, CrossFit ganha contactos

- Horário de aulas (home e CrossFit, mesmo componente partilhado): removida
  a barra de botões Seg/Ter/Qua/Qui/Sex/Sab acima da grelha — o próprio
  cabeçalho do dia (já existente) passa a ser sempre visível (antes só em
  desktop, 3 dias) e ganha setas ‹ › ao lado do nome do dia para mudar de
  dia em ecrãs de telemóvel/tablet. No desktop (3 dias lado a lado) as
  setas ficam escondidas — clicar no dia lateral já muda o dia em foco.
  CSS/JS morto do botão-filtro removido.
- Informações e contactos extraído para site/includes/contactos.php,
  partilhado pela home e pelo CrossFit — a página CrossFit ganha a mesma
  secção de contactos (localização, telefone, e-mail, horário de
  funcionamento, mapa, formulário) em vez de só linkar de volta à home.
  Hero e restantes secções do CrossFit não foram tocados.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
andreandClaude Sonnet 5 committed 2026-09-26 08:19:12 +01:00
1 parent 5478952171
commit 65182eecd6
6 files changed
+230 -207

No files matched your search

+32 -43
View File
@@ -2699,7 +2699,6 @@ h3.contact-title {
width: 100% !important;
}
.schedule-export .schedule-day-filter,
.schedule-export .schedule-scroll-hint {
display: none !important;
}
@@ -2730,43 +2729,6 @@ h3.contact-title {
align-items: center !important;
}
/* ── Schedule day filter — one day on screen, picker bar to switch ─────────────
On screen the schedule shows a single day with a clickable day-picker bar (the
same behaviour the phone had, now at every width). The JPG export ignores all
of this — it re-enables the full-week grid via .schedule-export — so the
downloaded image is always the complete week. */
.schedule-day-filter {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
margin: 0 0 1.75rem;
}
.sdf-btn {
border: 1px solid var(--fitme-surface-2);
background: #fff;
color: var(--fitme-text-soft);
font-weight: 700;
font-size: .9rem;
padding: 9px 20px;
min-height: 44px;
border-radius: var(--fitme-radius);
cursor: pointer;
transition: border-color var(--fitme-transition), color var(--fitme-transition);
}
.sdf-btn:hover {
border-color: var(--fitme-accent);
color: var(--fitme-text);
}
.sdf-btn.active {
background: var(--fitme-accent);
border-color: var(--fitme-accent);
color: var(--fitme-dark);
}
/* On-screen single-day layout — one centred column showing the chosen day's
classes in time order, keeping the Manhã / Tarde dividers as section breaks.
Scoped to :not(.schedule-export) so the capture clone keeps the full week. */
@@ -2819,12 +2781,39 @@ h3.contact-title {
display: none !important;
}
/* Single-day mode: the active day-filter button already names the day being
shown — drop the redundant title above the grid. Kept for the 3-day desktop
view (.is-3day), where the header also distinguishes three different days
and the side ones stay clickable. */
/* Single-day mode: no day-filter bar any more (audit UX pass) — the header
itself is the only day switcher, so it always shows, with prev/next arrows
beside the day name. Kept plain (no arrows) for the 3-day desktop view
(.is-3day), where the side days are already directly clickable. */
.schedule:not(.schedule-export):not(.is-3day) .schedule_dayhead {
display: none !important;
gap: 10px;
align-items: center;
}
.schedule.is-3day .sched-nav,
.schedule-export .sched-nav {
display: none;
}
.sched-nav {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border: none;
border-radius: var(--fitme-radius);
background: var(--fitme-surface-2);
color: var(--fitme-text-soft);
font-size: 1rem;
cursor: pointer;
transition: background var(--fitme-transition), color var(--fitme-transition);
flex: 0 0 auto;
}
.sched-nav:hover {
background: var(--fitme-accent);
color: var(--fitme-dark);
}
.schedule-scroll-hint {
+24 -22
View File
@@ -377,26 +377,28 @@ document.addEventListener('DOMContentLoaded', function () {
window.addEventListener('scroll', function () { if (panelOpen() && !floating.matches && !opening) closePanel(); }, { passive: true });
});
/* ── Schedule day filter ─────────────────────────────────────────────────────
The on-screen schedule shows the chosen day (phones) or that day centred
between the day before and after (desktop ≥992px); a day-picker bar switches
days (each header + class cell is tagged with data-day). Defaults to today
when the gym is open that day, otherwise the first day. The JPG export is
unaffected — its clone removes every .sched-hide and re-renders the full week. */
/* ── Schedule day switcher ────────────────────────────────────────────────────
The on-screen schedule shows the chosen day (phones/tablets) or that day
centred between the day before and after (desktop ≥992px, .is-3day); each
header + class cell is tagged with data-day. There is no separate day-picker
bar — the header itself is the switcher: prev/next arrows beside the day
name on phones/tablets, and clicking a side day's header on desktop.
Defaults to today when the gym is open that day, otherwise the first day.
The JPG export is unaffected — its clone removes every .sched-hide and
re-renders the full week. */
document.addEventListener('DOMContentLoaded', function () {
// Works on the home page (#horario) and the CrossFit page (bare .schedule).
var scope = document.querySelector('.schedule');
if (!scope) return;
var filter = scope.querySelector('.schedule-day-filter');
var dayheads = scope.querySelectorAll('.schedule_dayhead');
var cells = scope.querySelectorAll('.schedule_cell, .schedule_dayhead');
if (!filter || !cells.length) return;
var buttons = filter.querySelectorAll('.sdf-btn');
if (!dayheads.length) return;
var labels = scope.querySelectorAll('.schedule_section_label');
// Desktop shows three days — the chosen one centred between the day before
// and the day after (wrapping round the week); phones show just the one.
var wide = window.matchMedia('(min-width: 992px)');
var week = Array.prototype.map.call(buttons, function (b) { return b.getAttribute('data-day'); });
var week = Array.prototype.map.call(dayheads, function (h) { return h.getAttribute('data-day'); });
var current = null;
// Toggle classes (not inline display) — .sched-hide carries !important so it
@@ -418,9 +420,6 @@ document.addEventListener('DOMContentLoaded', function () {
if (three && col !== 2) c.classList.add('sched-side');
}
});
buttons.forEach(function (b) {
b.classList.toggle('active', b.getAttribute('data-day') === day);
});
// Show a Manhã / Tarde divider only when a visible day has a class in that block.
labels.forEach(function (lbl) {
var sec = lbl.getAttribute('data-section');
@@ -431,25 +430,28 @@ document.addEventListener('DOMContentLoaded', function () {
});
}
filter.addEventListener('click', function (e) {
var btn = e.target.closest('.sdf-btn');
if (btn) showDay(btn.getAttribute('data-day'));
});
// Clicking a side day's header centres it.
function step(dir) {
var i = week.indexOf(current), n = week.length;
if (i === -1 || !n) return;
showDay(week[(i + dir + n) % n]);
}
scope.addEventListener('click', function (e) {
if (e.target.closest('.sched-prev')) { step(-1); return; }
if (e.target.closest('.sched-next')) { step(1); return; }
// Clicking a side day's header (desktop 3-day view) centres it.
var head = e.target.closest('.schedule_dayhead.sched-side');
if (head) showDay(head.getAttribute('data-day'));
});
var reflow = function () { if (current) showDay(current); };
if (wide.addEventListener) wide.addEventListener('change', reflow); else if (wide.addListener) wide.addListener(reflow);
// Pre-select today when the schedule has a button for it (PT day names),
// Pre-select today when the schedule has a header for it (PT day names),
// otherwise the first day.
var ptDays = { 1: 'segunda', 2: 'terça', 3: 'quarta', 4: 'quinta', 5: 'sexta', 6: 'sábado' };
var today = ptDays[new Date().getDay()];
var todayBtn = today ? filter.querySelector('.sdf-btn[data-day="' + today + '"]') : null;
var start = todayBtn || buttons[0];
if (start) showDay(start.getAttribute('data-day'));
var start = (today && week.indexOf(today) !== -1) ? today : week[0];
if (start) showDay(start);
});
/* ── Contact form: stretch the message box to meet the map ───────────────────
+27
View File
@@ -38,6 +38,33 @@ echo view('crossfit', $data);
echo view('crossfit_badge', $data);
echo view('gallery', $data);
echo view('horario', $data);
// ── Informações e contactos ───────────────────────────────────────────────────
// Same partial as the home page (site/includes/contactos.php), so visitors get
// the gym's contact info + form without leaving the CrossFit page.
$gid = (int) $gym['id'];
$on = fn(string $key) => gym_section_on($gym, $key);
$whours = $on('workhours') ? site_work_hours($pdo, $gid) : [];
$work_hours_days = ['Segunda-Sexta' => 'segunda', 'Sábado' => 'sabado', 'Domingo/Feriados' => 'domingo'];
usort($whours, fn($x, $y) => strcmp((string) $x['open'], (string) $y['open']));
$assunto = '';
if (isset($_GET['assunto'])) {
$assunto = $_GET['assunto'];
if ($assunto == 'pt'): $assunto = 'Informações - Personal Trainer';
elseif ($assunto == 'pt_cf'): $assunto = 'Informações - Personal Trainer CrossFit';
elseif ($assunto == 'insc'): $assunto = 'Informações - Inscrição no Ginásio';
elseif ($assunto == 'aula'): $assunto = 'Informações - Aula Experimental';
elseif ($assunto == 'nut'): $assunto = 'Informações - Nutricionista';
elseif ($assunto == 'fis'): $assunto = 'Informações - Fisioterapia';
endif;
}
echo view('contactos', [
'whours' => $whours,
'work_hours_days' => $work_hours_days,
'assunto' => $assunto,
'show' => $on('contactos'),
]);
echo view('map', $data);
echo view('ad_footer', $data);
echo view('footer', $data);
+136
View File
@@ -0,0 +1,136 @@
<?php
// Informações e contactos — partilhado entre a home (site/index.php) e o
// CrossFit (site/crossfit.php), para as duas páginas darem sempre a mesma
// informação de contacto em vez de reencaminhar para a home só para isso.
// $data: whours, work_hours_days (Horário de Funcionamento, opcional),
// assunto (pré-preenche "Razão" no formulário), show (gym_section_on, default true).
$whours = $whours ?? [];
$work_hours_days = $work_hours_days ?? ['Segunda-Sexta' => 'segunda', 'Sábado' => 'sabado', 'Domingo/Feriados' => 'domingo'];
$assunto = $assunto ?? '';
$show = $show ?? true;
$session = session();
?>
<?php if ($show): ?>
<?php /* contacts-bg.jpg never existed — outside2.jpg is the current photo in img/contactos/ */ ?>
<section id="contactos" class="contact-section set-bg" data-setbg="<?= base_url('site/assets/img/contactos/outside2.jpg') ?>" aria-labelledby="contactos-title">
<div class="container">
<?php if (($_GET['agendar'] ?? '') === 'ok'): ?>
<div class="alert alert-success text-center" role="alert">
Pedido enviado! Entraremos em contacto para confirmar a tua aula experimental.
</div>
<?php elseif (($_GET['agendar'] ?? '') === 'erro'): ?>
<div class="alert alert-danger text-center" role="alert">
Não foi possível enviar o pedido. Verifica os dados e tenta novamente.
</div>
<?php endif; ?>
<div class="row">
<div class="col-lg-4">
<h2 id="contactos-title" class="contact-title text-center text-md-left">Informações</h2>
<div class="contact-info-warp text-center text-md-left">
<h3>Localização</h3>
<div class="contact-info text-center text-md-left">
<i class="fas fa-map-marker-alt"></i>
<div class="cf-text">
<a href="<?= htmlspecialchars($info['google_maps_link'] ?? '') ?>" target="_blank" rel="noopener">
<p><?= htmlspecialchars($info['adress'] ?? '') ?></p>
</a>
</div>
</div>
</div>
<div class="contact-info-warp text-center text-md-left">
<h3>Telefone</h3>
<div class="contact-info text-center text-md-left">
<i class="fas fa-phone-volume"></i>
<div class="cf-text">
<a href="tel:<?= htmlspecialchars(site_tel($info['phone'] ?? '')) ?>">
<p><?= htmlspecialchars($info['phone'] ?? '') ?></p>
</a>
<?php if (($info['telephone'] ?? '') !== ''): ?>
<a href="tel:<?= htmlspecialchars(site_tel($info['telephone'])) ?>">
<p><?= htmlspecialchars($info['telephone']) ?></p>
</a>
<?php endif; ?>
</div>
</div>
</div>
<div class="contact-info-warp text-center text-md-left">
<h3>E-mail</h3>
<div class="contact-info">
<i class="fas fa-envelope"></i>
<div class="cf-text">
<a href="mailto:<?= htmlspecialchars($info['email'] ?? '') ?>">
<p><?= htmlspecialchars($info['email'] ?? '') ?></p>
</a>
<a href="#contactos">
<p><?= htmlspecialchars($info['website'] ?? '') ?></p>
</a>
</div>
</div>
</div>
<?php if ($whours): ?>
<div class="contact-info-warp text-center text-md-left">
<h3>Horário de Funcionamento</h3>
<dl class="wh-grid wh-grid-inline">
<?php foreach ($work_hours_days as $key => $value): ?>
<div class="wh-day">
<dt><?= htmlspecialchars($key) ?></dt>
<?php $any = false; foreach ($whours as $h): if ($h['week_day'] == $value): $any = true; ?>
<dd><?= htmlspecialchars(substr($h['open'], 0, 5) . ' – ' . substr($h['close'], 0, 5)) ?></dd>
<?php endif; endforeach; ?>
<?php if (!$any): ?><dd class="wh-closed">Encerrado</dd><?php endif; ?>
</div>
<?php endforeach; ?>
</dl>
</div>
<?php endif; ?>
<?php // Mapa compacto, ao lado das informações ?>
<div class="map map-side" id="map">
<?php
// Defer the Google Maps iframe until the user scrolls near it.
$maps_embed = $info['google_maps'] ?? '';
if (strpos($maps_embed, 'loading=') === false) {
$maps_embed = str_replace('<iframe ', '<iframe loading="lazy" ', $maps_embed);
}
echo map_click_to_load(clean_map_embed($maps_embed), (string) ($info['google_maps_link'] ?? ''));
?>
</div>
</div>
<div class="col-lg-8">
<div id="contact-form">
<h3 class="contact-title text-center text-md-left">Envia-nos uma mensagem</h3>
<?php if (isset($_GET['enviado'])): ?>
<div class="alert alert-success" role="alert">
Mensagem enviada com sucesso! Entraremos em contacto brevemente.
</div>
<?php endif; ?>
<form class="contact-form" method="post" action="https://forms.zohopublic.eu/fitme30701/form/FormulriodeContacto/formperma/NiwsTjwWGX2sgpZIXqE5otyAn8D5nuRQjpYDuGkS2wM/htmlRecords/submit">
<input type="hidden" name="zf_redirect_url" value="<?= base_url('?enviado=1#contact-form') ?>">
<div class="row">
<div class="col-md-6">
<input type="text" required name="SingleLine" placeholder="Nome" aria-label="Nome">
</div>
<div class="col-md-6">
<input type="text" required name="PhoneNumber_countrycode" placeholder="Telemóvel" aria-label="Telemóvel">
</div>
</div>
<input type="text" name="SingleLine1" placeholder="Razão" required value="<?= htmlspecialchars($assunto) ?>" aria-label="Razão do contacto">
<textarea name="MultiLine" placeholder="Mensagem" required aria-label="Mensagem"></textarea>
<?php if ($session->getFlashdata('email') !== null): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?= $session->getFlashdata('email') ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<label class="consent-check">
<?php /* no name attribute: gates submission client-side, not stored in Zoho */ ?>
<input type="checkbox" required>
<span>Li e aceito a <a href="<?= base_url('home/privacidade') ?>" target="_blank" rel="noopener">Política de Privacidade</a>.</span>
</label>
<button class="site-btn" id="btn_email" type="submit">Enviar Mensagem</button>
</form>
</div>
</div>
</div>
</div>
</section>
<?php endif; ?>
+2 -6
View File
@@ -43,11 +43,6 @@ foreach ($rows as $d) { $grid[$d['dia_semana']][$d['hora_inicio']][] = $d; }
</div>
</div>
<?php endif; ?>
<div class="schedule-day-filter" role="tablist" aria-label="Filtrar horário por dia">
<?php foreach ($week_day as $key => $value): ?>
<button type="button" class="sdf-btn<?= $value === $default_day ? ' active' : '' ?>" role="tab" data-day="<?= htmlspecialchars($value) ?>"><?= $key ?></button>
<?php endforeach; ?>
</div>
<div class="row">
<div class="col">
<div class="schedule_container">
@@ -57,7 +52,9 @@ foreach ($rows as $d) { $grid[$d['dia_semana']][$d['hora_inicio']][] = $d; }
<?php endforeach; ?>
<?php $col = 0; foreach ($week_day as $key => $value): $col++; ?>
<div class="schedule_dayhead<?= $value === $default_day ? '' : ' sched-hide' ?>" data-day="<?= htmlspecialchars($value) ?>" style="grid-column:<?= $col ?>;grid-row:1;">
<button type="button" class="sched-nav sched-prev" aria-label="Dia anterior"><i class="fas fa-chevron-left" aria-hidden="true"></i></button>
<div class="rounded_frame"><?= $key ?></div>
<button type="button" class="sched-nav sched-next" aria-label="Dia seguinte"><i class="fas fa-chevron-right" aria-hidden="true"></i></button>
</div>
<?php endforeach; ?>
<?php foreach ($sections as $sec): ?>
@@ -138,7 +135,6 @@ foreach ($rows as $d) { $grid[$d['dia_semana']][$d['hora_inicio']][] = $d; }
el.classList.remove('fade-in');
el.classList.remove('revealed');
});
var f = board.querySelector('.schedule-day-filter'); if (f) f.parentNode.removeChild(f);
var b = board.querySelector('.schedule-dl-btn'); if (b) { var w = b.closest('div'); (w || b).parentNode.removeChild(w || b); }
Array.prototype.forEach.call(board.querySelectorAll('.sched-hide'), function (c) { c.classList.remove('sched-hide'); });
+9 -136
View File
@@ -272,12 +272,6 @@ if (!in_array($default_day, $week_values, true)) { $default_day = $week_values[0
</div>
</div>
</div>
<!-- Day picker: click a day to view just its classes -->
<div class="schedule-day-filter" role="tablist" aria-label="Filtrar horário por dia">
<?php foreach ($week_day as $key => $value): ?>
<button type="button" class="sdf-btn<?= $value === $default_day ? ' active' : '' ?>" role="tab" data-day="<?= htmlspecialchars($value) ?>"><?= $key ?></button>
<?php endforeach; ?>
</div>
<?php
// ── Build a time-aligned grid ──────────────────────────────────────────
// Distinct start times become CSS-grid rows and days become columns, so
@@ -323,7 +317,9 @@ if (!in_array($default_day, $week_values, true)) { $default_day = $week_values[0
<?php endforeach; ?>
<?php $col = 0; foreach ($week_day as $key => $value): $col++; ?>
<div class="schedule_dayhead<?= $value === $default_day ? '' : ' sched-hide' ?>" data-day="<?= htmlspecialchars($value) ?>" style="grid-column:<?= $col ?>;grid-row:1;">
<button type="button" class="sched-nav sched-prev" aria-label="Dia anterior"><i class="fas fa-chevron-left" aria-hidden="true"></i></button>
<div class="rounded_frame"><?= $key ?></div>
<button type="button" class="sched-nav sched-next" aria-label="Dia seguinte"><i class="fas fa-chevron-right" aria-hidden="true"></i></button>
</div>
<?php endforeach; ?>
<?php // Manhã / Tarde dividers spanning the full width above each block. ?>
@@ -424,9 +420,8 @@ if (!in_array($default_day, $week_values, true)) { $default_day = $week_values[0
el.classList.remove('fade-in');
el.classList.remove('revealed');
});
var f = board.querySelector('.schedule-day-filter'); if (f) f.parentNode.removeChild(f);
var b = doc.getElementById('scheduleDownload'); if (b) { var w = b.closest('div'); (w || b).parentNode.removeChild(w || b); }
// Un-hide every day (mobile day-filter may have hidden 5 of 6).
// Un-hide every day (single-day mode may have hidden 5 of 6).
Array.prototype.forEach.call(board.querySelectorAll('.sched-hide'), function (c) { c.classList.remove('sched-hide'); });
var stamp = doc.createElement('p');
@@ -578,135 +573,13 @@ if (isset($_GET['assunto'])) {
elseif ($assunto == 'fis'): $assunto = 'Informações - Fisioterapia';
endif;
}
$session = session();
?>
<?php if ($on('contactos')): ?>
<?php /* contacts-bg.jpg never existed — outside2.jpg is the current photo in img/contactos/ */ ?>
<section id="contactos" class="contact-section set-bg" data-setbg="<?= base_url('site/assets/img/contactos/outside2.jpg') ?>" aria-labelledby="contactos-title">
<div class="container">
<?php if (($_GET['agendar'] ?? '') === 'ok'): ?>
<div class="alert alert-success text-center" role="alert">
Pedido enviado! Entraremos em contacto para confirmar a tua aula experimental.
</div>
<?php elseif (($_GET['agendar'] ?? '') === 'erro'): ?>
<div class="alert alert-danger text-center" role="alert">
Não foi possível enviar o pedido. Verifica os dados e tenta novamente.
</div>
<?php endif; ?>
<div class="row">
<div class="col-lg-4">
<h2 id="contactos-title" class="contact-title text-center text-md-left">Informações</h2>
<div class="contact-info-warp text-center text-md-left">
<h3>Localização</h3>
<div class="contact-info text-center text-md-left">
<i class="fas fa-map-marker-alt"></i>
<div class="cf-text">
<a href="<?= htmlspecialchars($info['google_maps_link'] ?? '') ?>" target="_blank" rel="noopener">
<p><?= htmlspecialchars($info['adress'] ?? '') ?></p>
</a>
</div>
</div>
</div>
<div class="contact-info-warp text-center text-md-left">
<h3>Telefone</h3>
<div class="contact-info text-center text-md-left">
<i class="fas fa-phone-volume"></i>
<div class="cf-text">
<a href="tel:<?= htmlspecialchars(site_tel($info['phone'] ?? '')) ?>">
<p><?= htmlspecialchars($info['phone'] ?? '') ?></p>
</a>
<?php if (($info['telephone'] ?? '') !== ''): ?>
<a href="tel:<?= htmlspecialchars(site_tel($info['telephone'])) ?>">
<p><?= htmlspecialchars($info['telephone']) ?></p>
</a>
<?php endif; ?>
</div>
</div>
</div>
<div class="contact-info-warp text-center text-md-left">
<h3>E-mail</h3>
<div class="contact-info">
<i class="fas fa-envelope"></i>
<div class="cf-text">
<a href="mailto:<?= htmlspecialchars($info['email'] ?? '') ?>">
<p><?= htmlspecialchars($info['email'] ?? '') ?></p>
</a>
<a href="#contactos">
<p><?= htmlspecialchars($info['website'] ?? '') ?></p>
</a>
</div>
</div>
</div>
<?php if ($whours): ?>
<div class="contact-info-warp text-center text-md-left">
<h3>Horário de Funcionamento</h3>
<dl class="wh-grid wh-grid-inline">
<?php foreach ($work_hours_days as $key => $value): ?>
<div class="wh-day">
<dt><?= htmlspecialchars($key) ?></dt>
<?php $any = false; foreach ($whours as $h): if ($h['week_day'] == $value): $any = true; ?>
<dd><?= htmlspecialchars(substr($h['open'], 0, 5) . ' – ' . substr($h['close'], 0, 5)) ?></dd>
<?php endif; endforeach; ?>
<?php if (!$any): ?><dd class="wh-closed">Encerrado</dd><?php endif; ?>
</div>
<?php endforeach; ?>
</dl>
</div>
<?php endif; ?>
<?php // Mapa compacto, ao lado das informações ?>
<div class="map map-side" id="map">
<?php
// Defer the Google Maps iframe until the user scrolls near it.
$maps_embed = $info['google_maps'] ?? '';
if (strpos($maps_embed, 'loading=') === false) {
$maps_embed = str_replace('<iframe ', '<iframe loading="lazy" ', $maps_embed);
}
echo map_click_to_load(clean_map_embed($maps_embed), (string) ($info['google_maps_link'] ?? ''));
?>
</div>
</div>
<div class="col-lg-8">
<div id="contact-form">
<h3 class="contact-title text-center text-md-left">Envia-nos uma mensagem</h3>
<?php if (isset($_GET['enviado'])): ?>
<div class="alert alert-success" role="alert">
Mensagem enviada com sucesso! Entraremos em contacto brevemente.
</div>
<?php endif; ?>
<form class="contact-form" method="post" action="https://forms.zohopublic.eu/fitme30701/form/FormulriodeContacto/formperma/NiwsTjwWGX2sgpZIXqE5otyAn8D5nuRQjpYDuGkS2wM/htmlRecords/submit">
<input type="hidden" name="zf_redirect_url" value="<?= base_url('?enviado=1#contact-form') ?>">
<div class="row">
<div class="col-md-6">
<input type="text" required name="SingleLine" placeholder="Nome" aria-label="Nome">
</div>
<div class="col-md-6">
<input type="text" required name="PhoneNumber_countrycode" placeholder="Telemóvel" aria-label="Telemóvel">
</div>
</div>
<input type="text" name="SingleLine1" placeholder="Razão" required value="<?= htmlspecialchars($assunto) ?>" aria-label="Razão do contacto">
<textarea name="MultiLine" placeholder="Mensagem" required aria-label="Mensagem"></textarea>
<?php if ($session->getFlashdata('email') !== null): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?= $session->getFlashdata('email') ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?php endif; ?>
<label class="consent-check">
<?php /* no name attribute: gates submission client-side, not stored in Zoho */ ?>
<input type="checkbox" required>
<span>Li e aceito a <a href="<?= base_url('home/privacidade') ?>" target="_blank" rel="noopener">Política de Privacidade</a>.</span>
</label>
<button class="site-btn" id="btn_email" type="submit">Enviar Mensagem</button>
</form>
</div>
</div>
</div>
</div>
</section>
echo view('contactos', [
'whours' => $whours,
'work_hours_days' => $work_hours_days,
'assunto' => $assunto,
'show' => $on('contactos'),
]);
<?php endif; /* contactos */ ?>
<?php
// ── Personal Trainer CTA ────────────────────────────────────────────────────────
if ($on('personal_trainer')):
?>