feat(equipa): o cartão de detalhe nasce da foto, estica e segue o rato
O cartão abre por cima da própria foto (mesmo sítio, tamanho e inclinação), endireita-se e estica para o lado — para a esquerda nas fotos da ponta direita — com o texto a entrar por trás. Aberto, inclina-se em 3D e desliza uns píxeis na direção do rato; ao sair volta a dobrar-se para dentro da foto. No telemóvel voa da foto para o centro (FLIP) e volta ao fechar. - clip-path em px calculados no JS: o Chrome não interpola inset() com calc(100% - …) e o recorte saltava em vez de esticar. - O cartão é posicionado sem transição antes de abrir; senão a transição do estado fechado arrancava primeiro e a abertura partia de valores antigos. - Tira o «Saber mais» do cartão da equipa. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1614bf327b
commit
90ee2a93bc
3 files changed
+237
-84
No files matched your search
+103
-48
@@ -2962,7 +2962,7 @@ section[id] {
|
||||
|
||||
/* The name used to be the tile's last child and got its size from the
|
||||
`.trainer-item>*:last-child` rule; pin it so it stays the same whether or
|
||||
not a title / "Saber mais" follows it. */
|
||||
not a title follows it. */
|
||||
.trainers-section .trainer-item h4 {
|
||||
font-size: .9rem;
|
||||
}
|
||||
@@ -2987,80 +2987,105 @@ section[id] {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.trainer-more {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: .55rem;
|
||||
font-size: .72rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: .12em;
|
||||
text-transform: uppercase;
|
||||
color: var(--fitme-ash);
|
||||
transition: color var(--fitme-transition);
|
||||
/* On tiles with a card, the card IS the hover effect: keep the photo in its
|
||||
resting tilt (the card starts from exactly that pose) and hide it while the
|
||||
card covers it, bringing it back only once the card has folded back in. */
|
||||
#powerRotation>div:nth-child(odd) .trainer-item.has-detail .trainer-pic:hover { transform: rotate(-3deg); }
|
||||
#powerRotation>div:nth-child(even) .trainer-item.has-detail .trainer-pic:hover { transform: rotate(3deg); }
|
||||
|
||||
.trainer-item.has-detail .trainer-pic {
|
||||
transition: transform .4s cubic-bezier(.34, 1.56, .64, 1), box-shadow var(--fitme-transition), visibility 0s linear .38s;
|
||||
}
|
||||
|
||||
.trainer-more i { font-size: .65rem; }
|
||||
|
||||
.trainer-item.has-detail:hover .trainer-more,
|
||||
.trainer-item.has-detail:focus-visible .trainer-more,
|
||||
.trainer-item.has-detail.is-open .trainer-more {
|
||||
color: var(--fitme-accent);
|
||||
.trainer-item.has-detail.is-open .trainer-pic {
|
||||
visibility: hidden;
|
||||
transition-delay: 0s;
|
||||
}
|
||||
|
||||
@media (max-width: 767px), (hover: none) {
|
||||
/* Touch: the card flies to the centre, so leave the tile photo in place. */
|
||||
.trainer-item.has-detail.is-open .trainer-pic { visibility: visible; }
|
||||
}
|
||||
|
||||
/* ── Detail card: grows out of the tile's own photo ─────────────────────────
|
||||
main.js places the card so its photo column sits exactly on the tile photo
|
||||
(--td-x/--td-y, size --td-pw/--td-ph) with the tile's tilt (--td-rot). Closed,
|
||||
the clip-path shows only that photo square, so it reads as the tile itself;
|
||||
opening straightens it and unclips it sideways — the card "stretches" out of
|
||||
the photo and the text fades in behind the edge. .td-flip mirrors it for
|
||||
right-hand tiles (photo on the right, card stretching left). While open, the
|
||||
mouse position feeds a small 3D tilt + drift (--td-rx/--td-ry/--td-mx/--td-my).
|
||||
Both transform lists have the same functions so they interpolate. */
|
||||
.trainer-detail {
|
||||
position: absolute;
|
||||
z-index: 30;
|
||||
top: -10px;
|
||||
left: 50%;
|
||||
width: min(560px, calc(100vw - 32px));
|
||||
transform: translateX(calc(-50% + var(--td-shift, 0px))) translateY(8px);
|
||||
left: var(--td-x, 0px);
|
||||
top: var(--td-y, 0px);
|
||||
width: var(--td-w, 560px);
|
||||
min-height: var(--td-ph, 200px);
|
||||
display: flex;
|
||||
text-align: left;
|
||||
background: #fff;
|
||||
color: var(--fitme-text);
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid var(--fitme-accent);
|
||||
box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
|
||||
overflow: hidden;
|
||||
/* px from main.js: Chrome won't interpolate inset() with calc(100% - …). */
|
||||
clip-path: inset(0px var(--td-cx, 360px) var(--td-cy, 0px) 0px round 2px);
|
||||
transform-origin: calc(var(--td-pw, 200px) / 2) calc(var(--td-ph, 200px) / 2);
|
||||
transform: perspective(900px) rotateX(0deg) rotateY(0deg) translate(0px, 0px) rotate(var(--td-rot, 0deg));
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
transition: opacity .2s ease, transform .2s ease, visibility 0s linear .2s;
|
||||
transition:
|
||||
clip-path .38s cubic-bezier(.4, 0, .2, 1),
|
||||
transform .38s cubic-bezier(.4, 0, .2, 1),
|
||||
opacity .12s ease .26s,
|
||||
visibility 0s linear .38s;
|
||||
}
|
||||
|
||||
.trainer-detail.td-flip {
|
||||
flex-direction: row-reverse;
|
||||
clip-path: inset(0px 0px var(--td-cy, 0px) var(--td-cx, 360px) round 2px);
|
||||
transform-origin: calc(100% - var(--td-pw, 200px) / 2) calc(var(--td-ph, 200px) / 2);
|
||||
}
|
||||
|
||||
.trainer-item.is-open .trainer-detail {
|
||||
clip-path: inset(0px 0px 0px 0px round 6px);
|
||||
transform: perspective(900px) rotateX(var(--td-rx, 0deg)) rotateY(var(--td-ry, 0deg)) translate(var(--td-mx, 0px), var(--td-my, 0px)) rotate(0deg);
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
transform: translateX(calc(-50% + var(--td-shift, 0px))) translateY(0);
|
||||
transition: opacity .2s ease, transform .2s ease, visibility 0s;
|
||||
transition:
|
||||
clip-path .55s cubic-bezier(.2, .9, .25, 1),
|
||||
transform .6s cubic-bezier(.34, 1.35, .64, 1),
|
||||
opacity 0s,
|
||||
visibility 0s;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.trainer-item.has-detail:hover .trainer-detail,
|
||||
.trainer-item.has-detail:focus-within .trainer-detail {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
transform: translateX(calc(-50% + var(--td-shift, 0px))) translateY(0);
|
||||
transition: opacity .2s ease .12s, transform .2s ease .12s, visibility 0s .12s;
|
||||
}
|
||||
.trainer-item.has-detail:hover { z-index: 31; }
|
||||
/* After the opening move, follow the mouse with a short, soft transition. */
|
||||
.trainer-item.is-open .trainer-detail.td-tracking {
|
||||
transition:
|
||||
clip-path .55s cubic-bezier(.2, .9, .25, 1),
|
||||
transform .18s ease-out,
|
||||
opacity 0s,
|
||||
visibility 0s;
|
||||
}
|
||||
|
||||
.trainer-item.is-open { z-index: 31; }
|
||||
|
||||
.td-photo {
|
||||
flex: 0 0 38%;
|
||||
max-width: 210px;
|
||||
background: var(--fitme-dark-2);
|
||||
flex: 0 0 var(--td-pw, 200px);
|
||||
box-sizing: border-box;
|
||||
border: 4px solid var(--fitme-accent);
|
||||
background: var(--fitme-surface-2);
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.td-photo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 220px;
|
||||
min-height: calc(var(--td-ph, 200px) - 8px);
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
display: block;
|
||||
@@ -3071,6 +3096,17 @@ section[id] {
|
||||
padding: 18px 22px 18px 20px;
|
||||
max-height: 340px;
|
||||
overflow-y: auto;
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
transition: opacity .12s ease, transform .12s ease;
|
||||
}
|
||||
|
||||
.td-flip .td-body { transform: translateX(10px); }
|
||||
|
||||
.trainer-item.is-open .td-body {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
transition: opacity .3s ease .18s, transform .35s ease .18s;
|
||||
}
|
||||
|
||||
.td-name {
|
||||
@@ -3148,20 +3184,32 @@ section[id] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Touch / small screens: a centred fixed card over a dimmed page. */
|
||||
/* Touch / small screens: a centred fixed card over a dimmed page. The "grows
|
||||
out of the tile" move is done in main.js with a FLIP animation instead of
|
||||
the clip-path, since the card no longer sits on top of the tile. */
|
||||
@media (max-width: 767px), (hover: none) {
|
||||
.trainer-detail {
|
||||
.trainer-detail,
|
||||
.trainer-detail.td-flip {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
top: var(--td-top, 20vh);
|
||||
left: 16px;
|
||||
right: 16px;
|
||||
width: auto;
|
||||
max-width: 560px;
|
||||
min-height: 0;
|
||||
margin: 0 auto;
|
||||
transform: translateY(-46%);
|
||||
max-height: calc(100vh - 48px);
|
||||
flex-direction: row;
|
||||
clip-path: none;
|
||||
transform: none;
|
||||
transform-origin: top left;
|
||||
transition: opacity .15s ease, visibility 0s linear .15s;
|
||||
}
|
||||
.trainer-item.is-open .trainer-detail {
|
||||
clip-path: none;
|
||||
transform: none;
|
||||
transition: opacity 0s, visibility 0s;
|
||||
}
|
||||
.trainer-item.is-open .trainer-detail { transform: translateY(-50%); }
|
||||
.trainer-item.is-open:after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
@@ -3170,17 +3218,24 @@ section[id] {
|
||||
background: rgba(0, 0, 0, .55);
|
||||
}
|
||||
.td-close { display: block; }
|
||||
.td-photo { flex-basis: 34%; }
|
||||
.td-photo { flex-basis: 34%; border-width: 3px; }
|
||||
.td-photo img { min-height: 180px; }
|
||||
.td-body { max-height: calc(100vh - 48px); padding: 16px 16px 16px 14px; }
|
||||
.td-body,
|
||||
.td-flip .td-body { max-height: calc(100vh - 48px); padding: 16px 16px 16px 14px; transform: none; }
|
||||
}
|
||||
|
||||
@media (max-width: 339px) {
|
||||
.trainer-detail { flex-direction: column; }
|
||||
.trainer-detail,
|
||||
.trainer-detail.td-flip { flex-direction: column; }
|
||||
.td-photo { max-width: none; flex-basis: auto; }
|
||||
.td-photo img { min-height: 0; height: 160px; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.trainer-detail,
|
||||
.trainer-item.is-open .trainer-detail { transition-duration: .01ms !important; }
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════════════════════════════════════
|
||||
Mensalidades — cartões de preço
|
||||
══════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
+134
-35
@@ -224,36 +224,105 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
});
|
||||
|
||||
|
||||
/* ── Equipa: horizontal detail card ─────────────────────────────────────────
|
||||
Desktop (fine pointer) opens it on hover purely in CSS; this handles tap /
|
||||
click / keyboard (.is-open, one at a time; Esc, the × and outside clicks
|
||||
close) and nudges the absolutely-positioned card sideways (--td-shift) so an
|
||||
edge column's card stays inside the viewport. On small / touch screens the
|
||||
CSS turns it into a centred fixed card, so no shift is applied there. */
|
||||
/* ── Equipa: detail card that grows out of the tile ─────────────────────────
|
||||
Desktop: hover (or keyboard focus) opens. The card is laid exactly over the
|
||||
tile photo, tilted like it, and the CSS unclips/straightens it sideways —
|
||||
to the right, or to the left (.td-flip) when there is no room. While open,
|
||||
the mouse drives a small 3D tilt + drift so it handles like a real card.
|
||||
Touch / narrow screens: tap opens a centred fixed card; a FLIP animation
|
||||
makes it fly out of (and back into) the tile photo. Esc, ×, outside tap close. */
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var items = document.querySelectorAll('.trainer-item.has-detail');
|
||||
if (!items.length) return;
|
||||
var floating = window.matchMedia('(max-width: 767px), (hover: none)');
|
||||
var reduced = window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||
var section = document.getElementById('equipa');
|
||||
var CARD_W = 560, GUTTER = 16;
|
||||
|
||||
function tileRot(item) {
|
||||
// #powerRotation tilts odd tiles -3deg and even ones +3deg (style.css).
|
||||
var col = item.parentElement, i = 0;
|
||||
while (col && (col = col.previousElementSibling)) i++;
|
||||
return (i % 2 === 1) ? 3 : -3;
|
||||
}
|
||||
|
||||
// Desktop placement: photo column on top of the tile photo.
|
||||
function place(item) {
|
||||
var card = item.querySelector('.trainer-detail');
|
||||
if (!card) return;
|
||||
card.style.setProperty('--td-shift', '0px');
|
||||
if (floating.matches) return;
|
||||
var r = card.getBoundingClientRect();
|
||||
var itemR = item.getBoundingClientRect();
|
||||
// Where the card would sit centred on the item (ignoring its current transform).
|
||||
var left = itemR.left + itemR.width / 2 - r.width / 2;
|
||||
var right = left + r.width;
|
||||
var gutter = 16, vw = document.documentElement.clientWidth, shift = 0;
|
||||
if (left < gutter) shift = gutter - left;
|
||||
else if (right > vw - gutter) shift = (vw - gutter) - right;
|
||||
card.style.setProperty('--td-shift', Math.round(shift) + 'px');
|
||||
var pic = item.querySelector('.trainer-pic');
|
||||
if (!card || !pic || floating.matches) return;
|
||||
var vw = document.documentElement.clientWidth;
|
||||
var w = Math.min(CARD_W, vw - GUTTER * 2);
|
||||
var pw = pic.offsetWidth, ph = pic.offsetHeight;
|
||||
var picLeft = item.getBoundingClientRect().left + pic.offsetLeft; // untransformed
|
||||
var flip = picLeft + w > vw - GUTTER; // no room on the right
|
||||
var x = flip ? pic.offsetLeft + pw - w : pic.offsetLeft;
|
||||
card.classList.toggle('td-flip', flip);
|
||||
card.style.setProperty('--td-w', w + 'px');
|
||||
card.style.setProperty('--td-pw', pw + 'px');
|
||||
card.style.setProperty('--td-ph', ph + 'px');
|
||||
card.style.setProperty('--td-x', x + 'px');
|
||||
card.style.setProperty('--td-y', pic.offsetTop + 'px');
|
||||
card.style.setProperty('--td-rot', tileRot(item) + 'deg');
|
||||
// Closed clip = just the photo square, in px (see style.css).
|
||||
card.style.setProperty('--td-cx', Math.max(0, w - pw) + 'px');
|
||||
card.style.setProperty('--td-cy', Math.max(0, card.offsetHeight - ph) + 'px');
|
||||
}
|
||||
var section = document.getElementById('equipa');
|
||||
|
||||
function resetTilt(card) {
|
||||
['--td-rx', '--td-ry', '--td-mx', '--td-my'].forEach(function (p) { card.style.removeProperty(p); });
|
||||
card.classList.remove('td-tracking');
|
||||
}
|
||||
|
||||
// Touch: fly the fixed card out of / back into the tile photo.
|
||||
function flip(item, opening) {
|
||||
var card = item.querySelector('.trainer-detail');
|
||||
var pic = item.querySelector('.trainer-pic');
|
||||
if (!card || !pic || !card.animate || reduced.matches) return null;
|
||||
var p = pic.getBoundingClientRect(), c = card.getBoundingClientRect();
|
||||
if (!c.width || !c.height) return null;
|
||||
var from = 'translate(' + (p.left - c.left) + 'px,' + (p.top - c.top) + 'px) scale(' +
|
||||
(p.width / c.width) + ',' + (p.height / c.height) + ') rotate(' + tileRot(item) + 'deg)';
|
||||
var frames = [{ transform: from, opacity: .4 }, { transform: 'none', opacity: 1 }];
|
||||
return card.animate(opening ? frames : frames.reverse(), {
|
||||
duration: opening ? 420 : 280,
|
||||
easing: opening ? 'cubic-bezier(.2,.9,.25,1)' : 'cubic-bezier(.4,0,1,1)'
|
||||
});
|
||||
}
|
||||
|
||||
function setOpen(item, open) {
|
||||
item.classList.toggle('is-open', open);
|
||||
var card = item.querySelector('.trainer-detail');
|
||||
var wasOpen = item.classList.contains('is-open');
|
||||
if (open === wasOpen) return;
|
||||
clearTimeout(item._tdTrack);
|
||||
if (open) {
|
||||
if (floating.matches) {
|
||||
item.classList.add('is-open');
|
||||
card.style.setProperty('--td-top', Math.max(24, (window.innerHeight - card.offsetHeight) / 2) + 'px');
|
||||
flip(item, true);
|
||||
} else {
|
||||
// Jump (no transition) to the closed pose on top of the tile, commit
|
||||
// it, then open — otherwise the closed state's own transition would
|
||||
// start moving from the previous pose and the opening would begin
|
||||
// from that half-way value instead of from the photo.
|
||||
card.style.transition = 'none';
|
||||
place(item);
|
||||
void card.offsetWidth;
|
||||
card.style.transition = '';
|
||||
void card.offsetWidth;
|
||||
item.classList.add('is-open');
|
||||
item._tdTrack = setTimeout(function () { card.classList.add('td-tracking'); }, 600);
|
||||
}
|
||||
} else {
|
||||
resetTilt(card);
|
||||
var anim = floating.matches ? flip(item, false) : null;
|
||||
if (anim) anim.onfinish = function () { item.classList.remove('is-open'); syncSection(); };
|
||||
else item.classList.remove('is-open');
|
||||
}
|
||||
item.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
syncSection();
|
||||
}
|
||||
function syncSection() {
|
||||
if (section) section.classList.toggle('has-open-detail', !!section.querySelector('.trainer-item.is-open'));
|
||||
}
|
||||
function closeAll(except) {
|
||||
@@ -261,35 +330,65 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
|
||||
items.forEach(function (item) {
|
||||
item.addEventListener('mouseenter', function () { place(item); });
|
||||
item.addEventListener('focus', function () { place(item); });
|
||||
var card = item.querySelector('.trainer-detail');
|
||||
|
||||
// Desktop hover: short intent delay so sweeping across the grid
|
||||
// doesn't fire every card.
|
||||
item.addEventListener('mouseenter', function () {
|
||||
if (floating.matches) return;
|
||||
clearTimeout(item._tdHover);
|
||||
item._tdHover = setTimeout(function () { closeAll(item); setOpen(item, true); }, 90);
|
||||
});
|
||||
item.addEventListener('mouseleave', function () {
|
||||
if (floating.matches) return;
|
||||
clearTimeout(item._tdHover);
|
||||
setOpen(item, false);
|
||||
});
|
||||
|
||||
// Tilt + drift toward the pointer while open (desktop only).
|
||||
item.addEventListener('mousemove', function (e) {
|
||||
if (floating.matches || reduced.matches || !item.classList.contains('is-open')) return;
|
||||
var r = card.getBoundingClientRect();
|
||||
var dx = Math.max(-1, Math.min(1, (e.clientX - (r.left + r.width / 2)) / (r.width / 2)));
|
||||
var dy = Math.max(-1, Math.min(1, (e.clientY - (r.top + r.height / 2)) / (r.height / 2)));
|
||||
card.style.setProperty('--td-ry', (dx * 5).toFixed(2) + 'deg');
|
||||
card.style.setProperty('--td-rx', (-dy * 5).toFixed(2) + 'deg');
|
||||
card.style.setProperty('--td-mx', (dx * 6).toFixed(1) + 'px');
|
||||
card.style.setProperty('--td-my', (dy * 6).toFixed(1) + 'px');
|
||||
});
|
||||
|
||||
item.addEventListener('focus', function () {
|
||||
if (!floating.matches) { closeAll(item); setOpen(item, true); }
|
||||
});
|
||||
item.addEventListener('blur', function (e) {
|
||||
if (!floating.matches && !item.contains(e.relatedTarget)) setOpen(item, false);
|
||||
});
|
||||
|
||||
// Tap / click (touch, or a desktop click on a closed tile).
|
||||
item.addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
if (e.target.closest('.td-close')) { setOpen(item, false); return; }
|
||||
// Clicks inside an open card (e.g. scrolling / selecting text) keep it open.
|
||||
if (e.target.closest('.trainer-detail') && item.classList.contains('is-open')) return;
|
||||
// Tapping the dimmed backdrop (the item's ::after) closes it.
|
||||
if (floating.matches && item.classList.contains('is-open') && !e.target.closest('.trainer-detail')) {
|
||||
setOpen(item, false); return;
|
||||
var inCard = !!e.target.closest('.trainer-detail');
|
||||
if (item.classList.contains('is-open')) {
|
||||
// Tapping the dimmed backdrop (the item's ::after) closes it;
|
||||
// taps inside the card (scroll, select) keep it open.
|
||||
if (floating.matches && !inCard) setOpen(item, false);
|
||||
return;
|
||||
}
|
||||
closeAll(item);
|
||||
place(item);
|
||||
setOpen(item, !item.classList.contains('is-open'));
|
||||
setOpen(item, true);
|
||||
});
|
||||
item.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') {
|
||||
if (e.target !== item) return;
|
||||
if (e.key === 'Escape') { setOpen(item, false); item.blur(); }
|
||||
else if ((e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') && e.target === item) {
|
||||
e.preventDefault();
|
||||
closeAll(item); place(item);
|
||||
setOpen(item, !item.classList.contains('is-open'));
|
||||
} else if (e.key === 'Escape') {
|
||||
setOpen(item, false);
|
||||
item.blur();
|
||||
}
|
||||
});
|
||||
});
|
||||
document.addEventListener('click', function () { closeAll(null); });
|
||||
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeAll(null); });
|
||||
window.addEventListener('resize', function () { closeAll(null); }, { passive: true });
|
||||
});
|
||||
|
||||
/* ── Mensalidades: pre-select the clicked card in the sign-up modal ───────── */
|
||||
|
||||
@@ -140,7 +140,6 @@ if ($team):
|
||||
<h4><?= htmlspecialchars((string) $d['name']) ?></h4>
|
||||
<?php if ($t_title !== ''): ?><p class="trainer-title"><?= htmlspecialchars($t_title) ?></p><?php endif; ?>
|
||||
<?php if ($t_detail): ?>
|
||||
<span class="trainer-more" aria-hidden="true">Saber mais <i class="fas fa-plus"></i></span>
|
||||
<div class="trainer-detail" id="<?= $t_id ?>">
|
||||
<button type="button" class="td-close" aria-label="Fechar">×</button>
|
||||
<div class="td-photo"><img loading="lazy" src="<?= $t_img ?>" alt=""></div>
|
||||
|
||||
Reference in new issue
Block a user