Files
andre 725f5b9c26 feat(site): per-gym SEO texts, event gym selector, hero/navbar polish
- Generated SEO titles/descriptions per gym; gym seo fields override each on its own
- Keywords, author, og:site_name, favicon and JSON-LD logo follow the gym
- CrossFit page 404s before output for gyms without a box
- Events: «Mostrar em» gym selector; event page uses its gym's brand
- Hero event button text colour from the event colour (--ev-on)
- Navbar bottom line stops at the other-gym head (--switch-w)
- Diagonal hero blur mask; remove the trial link
2026-10-26 12:15:37 +00:00

129 lines
8.1 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// Per-page metadata, keyed by $current_uri_2. The canonical URL comes from
// base_url(), which matches the https + non-www redirect in .htaccess.
$page = $current_uri_2 ?? '';
// Titles ~50-60 characters, descriptions ~140-160 (what Google shows).
$gym_meta = current_gym();
$gym_name = ($gym_meta['name'] ?? '') !== '' ? $gym_meta['name'] : 'FitMe 3070';
$is_main = (int) ($gym_meta['id'] ?? 1) === 1;
$loc = ($gym_meta['locality'] ?? '') !== '' ? $gym_meta['locality'] : 'Mira';
$loc_in = (preg_match('/^(zona|rua|avenida|praia|vila)/iu', $loc) ? 'na ' : 'em ') . $loc; // «na Zona Industrial de Mira»
$page_meta = [
'' => $is_main
? ['FitMe 3070 — Ginásio e CrossFit em Mira',
'Ginásio em Mira (Lagoa) com musculação, CrossFit, aulas de grupo, personal trainers, nutrição e fisioterapia. Vê horários e mensalidades e inscreve-te!']
// Other gyms without their own texts get them from name and locality, never the 3070 ones.
: [$gym_name . ' — Ginásio ' . $loc_in,
$gym_name . ', ginásio ' . $loc_in . ': musculação, cardio e treino funcional com acompanhamento. Vê horários e mensalidades e inscreve-te já!'],
'crossfit' => $is_main
? ['CrossFit 3070 — Box de CrossFit em Mira | FitMe 3070',
'Box de CrossFit em Mira (Lagoa) com treinadores certificados e aulas para todos os níveis, do primeiro treino à competição. Vê o horário e treina connosco!']
: ['CrossFit — Box ' . $loc_in . ' | ' . $gym_name,
'Box de CrossFit do ' . $gym_name . ' ' . $loc_in . ': aulas para todos os níveis com treinadores certificados. Vê o horário e treina connosco!'],
'privacidade' => ['Política de Privacidade | ' . $gym_name,
'Política de privacidade do ' . $gym_name . ': que dados pessoais recolhemos, para quê, durante quanto tempo e como exerces os teus direitos (RGPD).'],
'termos' => ['Termos e Condições | ' . $gym_name,
'Termos e condições de utilização do site do ' . $gym_name . ': pedidos de inscrição, preços, horários, eventos e limites de responsabilidade.'],
];
// The gym's own SEO title/description (Gestão → Ginásios) win on its homepage, each on its own.
if ($page === '') {
if (($gym_meta['seo_title'] ?? '') !== '') $page_meta[''][0] = $gym_meta['seo_title'];
if (($gym_meta['seo_description'] ?? '') !== '') $page_meta[''][1] = $gym_meta['seo_description'];
}
// Favicon: the gym's own logo when it is an SVG.
$gym_logo_rel = gym_logo($gym_meta);
$favicon_rel = str_ends_with(strtolower($gym_logo_rel), '.svg') ? $gym_logo_rel : 'site/assets/img/logos/logo_black.svg';
// A dynamic page (e.g. an event landing page) can set its own title/description
// and canonical via these globals before requiring _open.php.
[$page_title, $page_desc] = $GLOBALS['_page_meta_override'] ?? $page_meta[$page] ?? $page_meta[''];
$canonical = $GLOBALS['_canonical_override'] ?? match ($page) {
'' => gym_url($gym_meta),
'crossfit' => gym_url($gym_meta, 'crossfit'),
default => base_url('home/' . $page),
};
?><!doctype html>
<html lang="pt">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="<?= htmlspecialchars($page_desc) ?>">
<meta name="keywords" content="<?= htmlspecialchars('ginásio, fitness, musculação, personal trainer, ' . (!empty($gym_meta['has_crossfit']) ? 'crossfit, box, ' : '') . mb_strtolower($loc) . ', ' . $gym_name) ?>">
<meta name="author" content="<?= htmlspecialchars($gym_name) ?>">
<meta name="theme-color" content="<?= htmlspecialchars(($gym_meta['brand_color'] ?? '') !== '' ? $gym_meta['brand_color'] : '#C2D500') ?>">
<title><?= htmlspecialchars($page_title) ?></title>
<link rel="canonical" href="<?= htmlspecialchars($canonical) ?>">
<?php if (seo_noindex()): /* staging copies (e.g. fitme.andmore.pt) must not compete with the real domain */ ?>
<meta name="robots" content="noindex, nofollow">
<?php endif; ?>
<!-- Preconnect to external origins -->
<link rel="dns-prefetch" href="https://forms.zohopublic.eu">
<?php if (($current_uri_2 ?? '') === ''): ?>
<!-- Homepage hero is the LCP image — start fetching it before the CSS/JS -->
<?php $pre_hero = ($gym_meta['hero_image'] ?? '') !== '' ? $gym_meta['hero_image'] : 'site/assets/img/home.jpg';
// A .jxl hero (img_css_bg) is preloaded only where JXL is supported (type=); the rest fetch the fallback from the CSS. ?>
<link rel="preload" as="image" fetchpriority="high" href="<?= htmlspecialchars(base_url($pre_hero)) ?>"<?= img_fallback_rel($pre_hero) !== '' ? ' type="image/jxl"' : '' ?>>
<?php endif; ?>
<!-- Fonts (Roboto self-hosted — see @font-face in style.css) -->
<!-- Vendor: Bootstrap 5.3.3 -->
<?= link_tag('assets/vendor/bootstrap5/css/bootstrap.min.css') ?>
<!-- Icons: Font Awesome 6 from the shared platform-tools pack (local FA 5.13.1 as fallback) -->
<?= fit_icon_assets(['fontawesome'], ['site/assets/fonts/fontawesome/css/all.css']) ?>
<!-- App styles (Bootstrap overrides) -->
<?= link_tag('site/assets/css/style.css') ?>
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="<?= htmlspecialchars(base_url($favicon_rel)) ?>">
<link rel="apple-touch-icon" sizes="180x180" href="<?= base_url('site/assets/img/logos/apple-touch-icon.png') ?>">
<!-- Open Graph / link previews -->
<meta property="og:url" content="<?= htmlspecialchars($canonical) ?>">
<meta property="og:type" content="<?= htmlspecialchars($GLOBALS['_og_type_override'] ?? 'website') ?>">
<meta property="og:site_name" content="<?= htmlspecialchars($gym_name) ?>">
<meta property="og:locale" content="pt_PT">
<meta property="og:title" content="<?= htmlspecialchars($page_title) ?>">
<meta property="og:description" content="<?= htmlspecialchars($page_desc) ?>">
<meta property="og:image" content="<?= htmlspecialchars($GLOBALS['_og_image_override'] ?? base_url('site/assets/img/og-image.jpg')) ?>">
<?php if (empty($GLOBALS['_og_image_override'])): /* default banner is a known 1200×630; a per-page image isn't, so skip the dimension hints */ ?>
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<?php endif; ?>
<meta name="twitter:card" content="summary_large_image">
<!-- Structured data -->
<?= view('seo') ?>
<?php if (($brand_css = gym_brand_css($gym_meta)) !== ''): ?><style><?= $brand_css ?></style><?php endif; ?>
<?php include dirname(__DIR__, 2) . '/includes/env_ribbon.php'; // moldura de aviso fora de produção ?>
</head>
<body>
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
<!-- Grain for the h1/h2 lettering (style.css, "Noise texture"): fractal noise -> grey ->
squeezed to 88-100% brightness, multiplied into the glyphs and clipped to their shape. -->
<filter id="pt-noise" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB">
<feTurbulence type="fractalNoise" baseFrequency="1.98" numOctaves="1" seed="7" result="n"/>
<feColorMatrix in="n" type="saturate" values="0" result="g"/>
<feComponentTransfer in="g" result="grain">
<feFuncR type="linear" slope=".123" intercept=".877"/>
<feFuncG type="linear" slope=".123" intercept=".877"/>
<feFuncB type="linear" slope=".123" intercept=".877"/>
<feFuncA type="linear" slope="0" intercept="1"/>
</feComponentTransfer>
<feBlend in="SourceGraphic" in2="grain" mode="multiply" result="mix"/>
<feComposite in="mix" in2="SourceGraphic" operator="in"/>
</filter>
<!-- Liquid glass for the navbar backdrop (style.css .header-section, Chromium):
soft low-frequency noise, blurred so it has no grain, drives a displacement
that bends the page behind the bar like uneven glass. -->
<filter id="nav-glass" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB">
<feTurbulence type="fractalNoise" baseFrequency=".008 .04" numOctaves="2" seed="3" result="t"/>
<feGaussianBlur in="t" stdDeviation="2" result="map"/>
<feDisplacementMap in="SourceGraphic" in2="map" scale="38" xChannelSelector="R" yChannelSelector="G"/>
</filter>
</svg>
<a href="#conteudo" class="skip-link">Saltar para o conteúdo</a>