Files
portifolio-evandrodevbr/app/components/showcase/pinned-reel.css
T
evandro c7d09d63d4
CI / audit (push) Successful in 10m38s
CI / verify (push) Successful in 13m10s
CI / deploy (push) Successful in 1m50s
CI / e2e (push) Successful in 18m48s
feat(site): blocos 3D da marca, novas ofertas e hero mais abrangente
Hero e movimento
- cena 3D em tempo real com os blocos da marca (kit feito no Blender, 3d/blocos.py):
  marca, tela, integrações e dados; segue a rolagem, tem vida em repouso, reage ao
  cursor e, parada no topo, passa sozinha pelos estados com legenda
- três níveis de aparelho (full, lite, static); sem WebGL fica o pôster
- título novo: "Sites e sistemas sob medida para o jeito que a sua empresa trabalha."
- faixa de clientes com o ramo, linha do primeiro passo, hero alinhado às seções
- rótulos digitados, entrada dos blocos, cortina de blocos entre páginas e capa
  compartilhada entre a lista de cases e o case

Ofertas
- atendimento no WhatsApp com IA e nota fiscal para a reforma tributária
  (páginas de serviço, cartões da home, formulário e aviso com data de validade)
- página /ia atualizada, com fontes
- peças 3D em loop nos cartões e topos de página (3d/pecas.py)

Design
- hover por papel (bloco nos botões, barra nos links), rótulos sem caixa alta
- um destino por rótulo: "Falar sobre meu projeto" leva ao formulário,
  "Chamar no WhatsApp" abre o WhatsApp
- logo montado em blocos, 404 redesenhado, grades sem célula vazia
- ícones do PWA e manifesto novos

Specs em specs/, contexto de design em DESIGN.md e PRODUCT.md.
2026-11-10 03:48:25 -03:00

413 lines
8.7 KiB
CSS

/*
* Vitrine animada (ProjectReel mostra a fixa quando ela cabe): o site de cada projeto rola dentro de um notebook (e de
* um celular ao lado) conforme a página rola. Carrega sob demanda, junto com PinnedReel; o casco `.ds-reel`, a legenda
* e os botões vêm de reel-list.css.
*/
.ds-reel-pin {
position: sticky;
top: var(--site-header-height);
height: var(--pin-h);
overflow: clip;
}
.ds-reel-grid {
display: grid;
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto minmax(0, 1fr);
gap: var(--sp-4);
height: 100%;
padding-block: var(--sp-4);
}
@media (min-width: 1024px) {
.ds-reel-grid {
grid-template-columns: minmax(190px, 250px) minmax(0, 1fr);
grid-template-rows: minmax(0, 1fr);
gap: var(--sp-10);
padding-block: var(--sp-6);
}
}
/* lista de projetos: faixa rolável na horizontal em telas pequenas, coluna no desktop */
.ds-reel-index {
min-width: 0;
& > ol {
display: flex;
gap: 8px;
list-style: none;
overflow-x: auto;
scrollbar-width: none;
}
& > ol::-webkit-scrollbar {
display: none;
}
}
.ds-reel-pick {
position: relative;
display: flex;
align-items: center;
gap: 10px;
min-height: 44px;
padding: 0 14px;
font-family: var(--font-display);
font-size: 15px;
font-weight: 500;
white-space: nowrap;
color: rgba(255, 255, 255, 0.72);
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.12);
cursor: pointer;
transition:
color 0.25s ease,
border-color 0.25s ease,
background 0.25s ease;
@media (hover: hover) {
&:hover {
color: #fff;
border-color: rgba(255, 255, 255, 0.3);
}
}
&[aria-current="true"] {
color: #fff;
border-color: rgba(255, 255, 255, 0.6);
background: rgba(255, 255, 255, 0.08);
}
}
.ds-reel-hint {
display: none;
align-items: center;
flex-wrap: wrap;
gap: 6px 10px;
margin-bottom: var(--sp-5);
font-family: var(--font-mono);
font-size: 13px;
letter-spacing: 0.02em;
color: var(--slash);
}
.ds-reel-hint-line {
width: 24px;
height: 1px;
background: var(--slash);
}
.ds-reel-hint-sub {
width: 100%;
color: rgba(255, 255, 255, 0.5);
}
.ds-reel-num {
font-family: var(--font-mono);
font-size: 12px;
color: var(--slash);
&::before {
content: attr(data-n);
}
}
.ds-reel-name {
display: none;
}
/* trechos do projeto em cena: só no desktop, onde sobra coluna; cada um leva ao ponto do vídeo */
.ds-reel-chapters {
display: none;
list-style: none;
margin: 6px 0 10px 16px;
border-left: 1px solid rgba(255, 255, 255, 0.12);
}
.ds-reel-chapter-pick {
display: flex;
align-items: center;
width: 100%;
min-height: 32px;
gap: 10px;
padding: 0 12px;
border: 0;
background: transparent;
font-family: var(--font-display);
font-size: 15px;
line-height: 1.25;
color: rgba(255, 255, 255, 0.66);
text-align: left;
@media (hover: hover) {
&:hover {
color: #fff;
}
}
&[aria-current="true"] {
color: var(--slash);
}
}
.ds-reel-chapter-num {
font-family: var(--font-mono);
font-size: 12px;
color: rgba(255, 255, 255, 0.5);
}
.ds-reel-chapter-pick[aria-current="true"] .ds-reel-chapter-num {
color: var(--slash);
}
@media (min-width: 1024px) {
.ds-reel-chapters {
display: grid;
}
}
@media (min-width: 1024px) and (pointer: coarse) {
.ds-reel-chapter-pick {
min-height: 44px;
}
}
.ds-reel-bar {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 2px;
background: var(--slash);
transform: scaleX(var(--p, 0));
transform-origin: left;
opacity: 0;
}
.ds-reel-pick[aria-current="true"] .ds-reel-bar {
opacity: 1;
}
/* Projeto fora de cena, sob o ponteiro ou no foco: uma barra verde entra pela esquerda, no lugar onde o projeto em
cena mostra o progresso (.ds-reel-bar). Com movimento reduzido a vitrine fixa nem existe (reel-list.css). */
.ds-reel-pick:not([aria-current="true"])::before {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 2px;
background: var(--slash);
transform: scaleX(0);
transform-origin: left;
transition: transform 280ms var(--ease-out);
pointer-events: none;
}
.ds-reel-pick:not([aria-current="true"]):focus-visible::before {
transform: none;
}
@media (hover: hover) {
.ds-reel-pick:not([aria-current="true"]):hover::before {
transform: none;
}
}
@media (min-width: 640px) {
.ds-reel-name {
display: inline;
}
}
@media (min-width: 1024px) {
.ds-reel-hint {
display: flex;
}
.ds-reel-index > ol {
flex-direction: column;
gap: 6px;
overflow: visible;
}
.ds-reel-pick {
width: 100%;
gap: 14px;
padding: 0 16px;
}
}
.ds-reel-stage {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 0;
/* a barra de cookies (fixa na base da janela, na primeira visita) não cobre a legenda e os botões: o palco perde a
altura dela (--cookie-bar-height, publicada por GoogleAnalytics enquanto a barra está aberta) */
margin-bottom: var(--cookie-bar-height, 0px);
/* o palco mede a si mesmo: o conjunto cresce até o limite da largura ou da altura que sobrar */
container-type: size;
}
/* aparelhos + legenda, um bloco só: a legenda tem sempre a mesma largura e o mesmo canto esquerdo do notebook */
.ds-reel-set {
--caption-h: 13.75rem; /* reserva da legenda: título, trecho atual e botões */
--gap: var(--sp-6);
display: flex;
flex-direction: column;
gap: var(--gap);
/* cabe na largura (com folga para o celular sobrepor) e na altura (legenda + barra de 30px + tela 16:10) */
width: min(90cqw, calc((100cqh - var(--caption-h) - var(--gap) - 30px) * 1.6));
min-width: min(100%, 320px);
}
/* as camadas de aparelhos (uma por projeto) ficam empilhadas na mesma célula: o bloco tem a altura de um par */
.ds-reel-devices {
position: relative;
display: grid;
}
.ds-reel-set {
isolation: isolate;
}
/* a troca de projeto é um fade cruzado só dos aparelhos (a legenda é um bloco à parte, abaixo): sem deslocamento.
Quem não está em cena já é inert (sem foco, sem clique, fora da busca) e aria-hidden. */
.ds-reel-layer {
--accent: var(--slash);
position: relative;
grid-area: 1 / 1;
min-width: 0;
opacity: 0;
pointer-events: none;
transition: opacity 0.45s ease;
&[data-state="active"] {
opacity: 1;
pointer-events: auto;
}
/* brilho na cor do site, atrás dos aparelhos */
&::before {
content: "";
position: absolute;
inset: -4% 0;
z-index: -1;
background: radial-gradient(
55% 60% at 50% 50%,
color-mix(in srgb, var(--accent) 42%, transparent),
transparent 72%
);
opacity: 0.45;
pointer-events: none;
}
}
.ds-reel-laptop {
width: 100%;
}
.ds-reel-screen {
position: relative;
aspect-ratio: 16 / 10;
overflow: hidden;
background: #0a0a0a;
/* o pôster (imagem otimizada) fica embaixo; o vídeo, depois dele no DOM, cobre-o ao carregar */
& > img,
& > video {
position: absolute;
inset: 0;
display: block;
width: 100%;
height: 100%;
object-fit: cover;
object-position: top;
}
}
.ds-reel-phone {
position: absolute;
right: -5%;
bottom: -3%;
width: 22%;
min-width: 88px;
}
.ds-reel-chapter {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px 12px;
margin-top: 4px;
font-size: 15px;
color: #fff;
& strong {
font-weight: 600;
}
}
.ds-reel-chapter-count {
font-family: var(--font-mono);
font-size: 12px;
letter-spacing: 0.06em;
color: var(--slash);
}
.ds-reel-ticks {
display: flex;
gap: 4px;
list-style: none;
& li {
width: 18px;
height: 3px;
background: rgba(255, 255, 255, 0.18);
transition: background 0.3s ease;
}
& li[data-state="done"] {
background: rgba(204, 255, 0, 0.5);
}
& li[data-state="now"] {
background: var(--slash);
}
}
/* desktop com pouca altura: a lista de projetos e os trechos têm de caber na altura do pin */
@media (min-width: 1024px) and (max-height: 700px) {
.ds-reel-hint {
display: none;
}
}
@media (min-width: 1024px) and (max-height: 700px) and (pointer: fine) {
.ds-reel-pick {
min-height: 38px;
}
.ds-reel-chapter-pick {
min-height: 28px;
}
}
/* janela curta: a reserva da legenda encolhe e o notebook ganha tela */
@media (min-width: 640px) and (max-height: 700px) {
.ds-reel-set {
--caption-h: 10.5rem;
}
}
/* Anel de foco do índice (sempre sobre fundo escuro): lima, por dentro, porque a faixa rolável e a lista de trechos
cortam o que passa da borda. Sem o halo do anel duplo global (globals.css): ele é para fundos claros. */
:is(.ds-reel-pick, .ds-reel-chapter-pick):focus-visible {
outline: 2px solid var(--slash);
outline-offset: -4px;
box-shadow: none;
}