447 lines
10 KiB
CSS
447 lines
10 KiB
CSS
.app-shell {
|
|
display: grid;
|
|
grid-template-columns: var(--sidebar-width) 1fr;
|
|
min-height: 100vh;
|
|
transition: grid-template-columns var(--transition-base);
|
|
}
|
|
|
|
.app-shell.is-collapsed {
|
|
grid-template-columns: var(--sidebar-width-collapsed) 1fr;
|
|
}
|
|
|
|
/* Sidebar */
|
|
.sidebar {
|
|
background: var(--bg-sidebar);
|
|
border-right: 1px solid var(--sidebar-border);
|
|
padding: var(--space-5) var(--space-3);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-4);
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
/* Duração/curva própria (não var(--transition-base)) só pra entrada em
|
|
portal.html vinda do login (ver .pid-entering-sidebar abaixo) — um
|
|
slide precisa de mais tempo e uma curva com "chegada" suave pra ler
|
|
como um movimento de verdade, não um fade genérico de microinteração. */
|
|
transition: opacity 420ms ease, transform 420ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
}
|
|
|
|
.sidebar__brand {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 92px;
|
|
padding: var(--space-2) 0 var(--space-3);
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.sidebar__brand:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 2px;
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
/* Duas marcas em cima uma da outra (não uma só redimensionada) — a horizontal
|
|
tem texto (ilegível encolhida), o ícone é só o símbolo quadrado. O
|
|
cross-fade (opacity + scale) entre elas, em vez de trocar o `src` na hora,
|
|
é o que deixa a transição expandir/recolher fluida em vez de brusca. */
|
|
.sidebar__logo {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
object-fit: contain;
|
|
transition: opacity var(--transition-base), transform var(--transition-base);
|
|
}
|
|
|
|
.sidebar__logo--full {
|
|
width: 250px;
|
|
max-width: 96%;
|
|
height: auto;
|
|
opacity: 1;
|
|
transform: translate(-50%, -50%) scale(1);
|
|
}
|
|
|
|
.sidebar__logo--icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
opacity: 0;
|
|
transform: translate(-50%, -50%) scale(0.85);
|
|
}
|
|
|
|
.sidebar__nav {
|
|
flex: 1;
|
|
}
|
|
|
|
.nav-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-1);
|
|
}
|
|
|
|
.nav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-3);
|
|
width: 100%;
|
|
padding: var(--space-3);
|
|
border-radius: var(--radius-sm);
|
|
color: var(--sidebar-text-secondary);
|
|
font-size: 0.88rem;
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
text-align: left;
|
|
transition: background var(--transition-fast), color var(--transition-fast);
|
|
}
|
|
|
|
.nav-item svg {
|
|
color: var(--sidebar-text-muted);
|
|
transition: color var(--transition-fast);
|
|
}
|
|
|
|
.nav-item:hover {
|
|
background: var(--sidebar-hover-bg);
|
|
color: var(--sidebar-text-primary);
|
|
}
|
|
|
|
.nav-item:hover svg {
|
|
color: var(--accent);
|
|
}
|
|
|
|
.nav-item.is-active {
|
|
background: var(--accent);
|
|
color: var(--accent-contrast);
|
|
}
|
|
|
|
.nav-item.is-active svg {
|
|
color: var(--accent-contrast);
|
|
}
|
|
|
|
.nav-item__label {
|
|
flex: 1;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.nav-item__chevron {
|
|
transition: transform var(--transition-fast);
|
|
color: var(--sidebar-text-muted);
|
|
}
|
|
|
|
.nav-group.is-open > .nav-group__toggle .nav-item__chevron {
|
|
transform: rotate(90deg);
|
|
}
|
|
|
|
.nav-group__list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-1);
|
|
padding: var(--space-1) 0 var(--space-2) var(--space-6);
|
|
max-height: 0;
|
|
overflow: hidden;
|
|
visibility: hidden;
|
|
pointer-events: none;
|
|
transition: max-height var(--transition-base);
|
|
}
|
|
|
|
.nav-group.is-open > .nav-group__list {
|
|
visibility: visible;
|
|
pointer-events: auto;
|
|
max-height: 340px;
|
|
}
|
|
|
|
.nav-subitem {
|
|
display: block;
|
|
padding: var(--space-2) var(--space-3);
|
|
border-radius: var(--radius-sm);
|
|
color: var(--sidebar-text-muted);
|
|
font-size: 0.82rem;
|
|
text-align: left;
|
|
}
|
|
|
|
.nav-subitem:hover {
|
|
background: var(--sidebar-hover-bg);
|
|
color: var(--sidebar-text-primary);
|
|
}
|
|
|
|
.nav-subitem.is-active {
|
|
color: var(--accent);
|
|
font-weight: 700;
|
|
}
|
|
|
|
/* Subgrupo dentro de um módulo (3º nível: módulo > subgrupo > ferramenta) */
|
|
.nav-subgroup__toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--space-2);
|
|
width: 100%;
|
|
font-weight: 700;
|
|
border-bottom: 1px solid var(--sidebar-border);
|
|
border-radius: 0;
|
|
}
|
|
|
|
.nav-subgroup.is-open > .nav-subgroup__toggle .nav-item__chevron {
|
|
transform: rotate(90deg);
|
|
}
|
|
|
|
.nav-subgroup__list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-1);
|
|
padding: var(--space-1) 0 var(--space-2) var(--space-4);
|
|
max-height: 0;
|
|
overflow: hidden;
|
|
visibility: hidden;
|
|
pointer-events: none;
|
|
transition: max-height var(--transition-base);
|
|
}
|
|
|
|
.nav-subgroup.is-open > .nav-subgroup__list {
|
|
visibility: visible;
|
|
pointer-events: auto;
|
|
max-height: 160px;
|
|
}
|
|
|
|
.nav-subitem--tool {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
}
|
|
|
|
.nav-subitem--tool svg {
|
|
color: var(--sidebar-text-muted);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Linha com o item de navegação + botão de favoritar. padding-right aqui
|
|
repõe o padding-right que o .nav-item perde visualmente ao ser encurtado
|
|
por flex:1 — sem isso, o .fav-toggle fica encostado na borda da sidebar,
|
|
enquanto itens sem estrela (ex.: toggles de grupo) mantêm o respiro do
|
|
próprio padding do .nav-item. */
|
|
.nav-item-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-1);
|
|
padding-right: var(--space-3);
|
|
}
|
|
|
|
.nav-item-row > .nav-item,
|
|
.nav-item-row > .nav-subitem {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.fav-toggle {
|
|
flex-shrink: 0;
|
|
width: 28px;
|
|
height: 28px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--sidebar-text-muted);
|
|
border-radius: var(--radius-sm);
|
|
transition: color var(--transition-fast), background var(--transition-fast);
|
|
}
|
|
|
|
.fav-toggle:hover {
|
|
color: var(--gold-400);
|
|
background: var(--sidebar-hover-bg);
|
|
}
|
|
|
|
.fav-toggle.is-favorite {
|
|
color: var(--gold-400);
|
|
}
|
|
|
|
.fav-toggle.is-favorite svg {
|
|
fill: currentColor;
|
|
}
|
|
|
|
.badge-restricted {
|
|
font-size: 0.62rem;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
color: var(--gold-400);
|
|
border: 1px solid var(--gold-400);
|
|
border-radius: var(--radius-pill);
|
|
padding: 1px 6px;
|
|
}
|
|
|
|
/* Sidebar colapsada: esconde texto, mantém ícones */
|
|
.app-shell.is-collapsed .nav-item__label,
|
|
.app-shell.is-collapsed .nav-item__chevron,
|
|
.app-shell.is-collapsed .nav-group__list,
|
|
.app-shell.is-collapsed .badge-restricted,
|
|
.app-shell.is-collapsed .fav-toggle {
|
|
display: none;
|
|
}
|
|
|
|
.app-shell.is-collapsed .nav-item {
|
|
justify-content: center;
|
|
}
|
|
|
|
.app-shell.is-collapsed .sidebar__logo--full {
|
|
opacity: 0;
|
|
transform: translate(-50%, -50%) scale(0.85);
|
|
}
|
|
|
|
.app-shell.is-collapsed .sidebar__logo--icon {
|
|
opacity: 1;
|
|
transform: translate(-50%, -50%) scale(1);
|
|
}
|
|
|
|
.app-shell.is-collapsed .nav-item-row {
|
|
padding-right: 0;
|
|
}
|
|
|
|
/* Tooltip com o nome da ferramenta, mostrado ao passar o mouse sobre um
|
|
ícone da sidebar colapsada. Fica em document.body (não dentro de
|
|
.sidebar, que tem overflow-x:hidden e cortaria um elemento posicionado
|
|
fora da faixa da sidebar), posicionado via JS com getBoundingClientRect. */
|
|
.sidebar-tooltip {
|
|
position: fixed;
|
|
transform: translateY(-50%);
|
|
z-index: 60;
|
|
padding: var(--space-1) var(--space-3);
|
|
background: var(--bg-sidebar);
|
|
color: var(--sidebar-text-primary);
|
|
border: 1px solid var(--sidebar-border);
|
|
border-radius: var(--radius-sm);
|
|
font-size: 0.82rem;
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
box-shadow: var(--shadow-md);
|
|
pointer-events: none;
|
|
animation: pidFadeIn var(--transition-fast);
|
|
}
|
|
|
|
/* Topbar */
|
|
.topbar {
|
|
height: var(--topbar-height);
|
|
background: var(--bg-topbar);
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-4);
|
|
padding: 0 var(--space-5);
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 20;
|
|
}
|
|
|
|
.topbar__title {
|
|
font-size: 1rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.03em;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.topbar__actions {
|
|
margin-left: auto;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
}
|
|
|
|
.main-content {
|
|
min-width: 0;
|
|
transition: opacity var(--transition-base);
|
|
}
|
|
|
|
/* Ligado só em portal.html, só quando se chega ali logo depois da animação
|
|
de intro do login (ver portal-reveal.js) — duas classes, removidas em
|
|
sequência pelo JS: primeiro a sidebar desliza de fora da tela (esquerda
|
|
pra direita, `translateX(-100%)` — toda a largura dela, não um deslize
|
|
sutil de poucos pixels, pra realmente ler como "a barra lateral surgindo"
|
|
em vez de só um fade) até a posição final; só depois o resto do app
|
|
aparece num fade (`pid-entering`), em vez das duas coisas surgirem ao
|
|
mesmo tempo. */
|
|
html.pid-entering-sidebar .sidebar {
|
|
opacity: 0;
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
html.pid-entering .main-content {
|
|
opacity: 0;
|
|
}
|
|
|
|
.page-content {
|
|
padding: var(--space-5);
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
animation: pidFadeSlideUp 180ms ease;
|
|
}
|
|
|
|
/* Telas de aplicação (tudo exceto o "Principal"/portal.html) aproveitam melhor
|
|
o espaço entre a sidebar e a borda da tela — a home continua com o limite
|
|
padrão de 1200px, mais confortável de leitura pra uma grade de favoritos. */
|
|
.page-content.page-content--wide {
|
|
max-width: 1600px;
|
|
}
|
|
|
|
@media (max-width: 1024px) {
|
|
.app-shell {
|
|
grid-template-columns: var(--sidebar-width-collapsed) 1fr;
|
|
}
|
|
|
|
.app-shell .nav-item__label,
|
|
.app-shell .nav-item__chevron,
|
|
.app-shell .nav-group__list,
|
|
.app-shell .badge-restricted,
|
|
.app-shell .fav-toggle {
|
|
display: none;
|
|
}
|
|
|
|
.app-shell .nav-item {
|
|
justify-content: center;
|
|
}
|
|
|
|
.app-shell .nav-item-row {
|
|
padding-right: 0;
|
|
}
|
|
|
|
.app-shell .sidebar__logo--full {
|
|
opacity: 0;
|
|
transform: translate(-50%, -50%) scale(0.85);
|
|
}
|
|
|
|
.app-shell .sidebar__logo--icon {
|
|
opacity: 1;
|
|
transform: translate(-50%, -50%) scale(1);
|
|
}
|
|
|
|
.app-shell.is-expanded-mobile {
|
|
grid-template-columns: var(--sidebar-width) 1fr;
|
|
}
|
|
|
|
.app-shell.is-expanded-mobile .sidebar__logo--full {
|
|
opacity: 1;
|
|
transform: translate(-50%, -50%) scale(1);
|
|
}
|
|
|
|
.app-shell.is-expanded-mobile .sidebar__logo--icon {
|
|
opacity: 0;
|
|
transform: translate(-50%, -50%) scale(0.85);
|
|
}
|
|
|
|
.app-shell.is-expanded-mobile .nav-item__label,
|
|
.app-shell.is-expanded-mobile .nav-item__chevron,
|
|
.app-shell.is-expanded-mobile .badge-restricted {
|
|
display: inline;
|
|
}
|
|
|
|
.app-shell.is-expanded-mobile .fav-toggle {
|
|
display: flex;
|
|
}
|
|
|
|
.app-shell.is-expanded-mobile .nav-item-row {
|
|
padding-right: var(--space-3);
|
|
}
|
|
|
|
.app-shell.is-expanded-mobile .nav-group.is-open .nav-group__list {
|
|
display: flex;
|
|
}
|
|
}
|