Organismos
Bloques grandes con identidad propia: el topbar, el launcher, el panel de ayuda, el tablero Kanban, el encabezado y el pie del sitio.
Sidebar
Navegación principal: workspace, acción destacada, secciones plegables y el usuario al pie.
starter/components/sidebar.php
<?php include 'components/sidebar.php'; ?> <!-- 1º include del <body> -->
<!-- El menú se declara en config.php, no en el markup: -->
$GM_NAV = [
['type' => 'links', 'items' => [
['label' => 'Dashboard', 'icon' => 'fa-th-large', 'href' => 'index.php'],
]],
['type' => 'group', 'id' => 'grpOperacion', 'title' => 'Operación', 'items' => [
['label' => 'Listado', 'icon' => 'fa-list', 'href' => 'pages/listado.php'],
]],
];
localStorage) y pasa a cajón con overlay en móvil. Acá se muestra contenida; en la app ocupa el alto completo.Topbar
60 px de alto: hamburguesa y migas a la izquierda, buscador al centro, launcher, ayuda, campana y avatar a la derecha.
starter/components/header.php
<?php
$GM_BREADCRUMB = ['Comercial', 'Ofertas']; // App › Comercial › Ofertas
include 'components/header.php'; // trae launcher + ayuda + tema
?>
$GM_BREADCRUMB, no del markup.Launcher de sistemas
El menú waffle que salta entre los 13 sistemas del ecosistema. Autocontenido en un archivo.
starter/components/gm_launcher.php
<div class="topbar-right">
<?php include __DIR__ . '/gm_launcher.php'; ?>
...
</div>
$GM_SISTEMAS en este archivo y se copia a las apps.Panel de ayuda
Ayuda de la pantalla actual. Se incluye una vez desde el header y aparece en todas las páginas.
starter/components/help.php
// components/help.php — una entrada por pantalla, con el archivo como clave
$helpContent = [
'ofertas.php' => [
'titulo' => 'Ofertas',
'resumen' => 'Cotizaciones a clientes: cabecera, líneas y consecutivo.',
'puntos' => [
'El código se genera solo al guardar.',
'El IVA depende del ámbito del servicio.',
],
'tip' => 'Una oferta enviada a revisión queda congelada.',
],
];
Modal
Para una acción corta que no merece pantalla propia. Si tiene más de un paso, hacé una pantalla.
styles/app-shell.css
<div class="modal-content" style="max-width:520px">
<div class="modal-header">
<h5 class="modal-title">Nuevo cliente</h5>
<button type="button" class="btn-close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Nombre</label>
<input class="form-control" placeholder="Razón social">
</div>
<div class="mb-0">
<label class="form-label">Cédula jurídica</label>
<input class="form-control" placeholder="3-101-000000">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-gm-secondary">Cancelar</button>
<button class="btn btn-gm-primary">Guardar</button>
</div>
</div>
Tablero Kanban
Columna por estado y tarjeta por registro; se arrastra entre columnas y se guarda solo.
GrupoMecsaProyectos/pages/kanban.php
<div class="kanban-board" style="display:flex;gap:1rem;align-items:flex-start">
<div class="kanban-col">
<div class="stage-header">
<span>En ejecución</span>
<span class="badge bg-primary rounded-pill">2</span>
</div>
<div class="kanban-items">
<div class="kanban-card" style="border-left-color:#013483">
<div class="d-flex justify-content-between align-items-start mb-1">
<div class="fw-bold text-dark-gm" style="font-size:.95rem;">Mantenimiento planta norte</div>
<span class="badge bg-light text-secondary border small">#1042</span>
</div>
<div class="small text-secondary mb-1"><i class="fas fa-tag me-1"></i> Mantenimiento</div>
<div class="small text-muted mb-2"><i class="fas fa-user me-1"></i> Constructora del Sur</div>
<div class="small text-muted"><i class="fas fa-calendar-plus me-1"></i> Ingreso: 12/08/2026</div>
</div>
<div class="kanban-card" style="border-left-color:#013483">
<div class="d-flex justify-content-between align-items-start mb-1">
<div class="fw-bold text-dark-gm" style="font-size:.95rem;">Instalación de bombas</div>
<span class="badge bg-light text-secondary border small">#1043</span>
</div>
<div class="small text-muted mb-2"><i class="fas fa-user me-1"></i> Agroindustrias CR</div>
</div>
</div>
</div>
<div class="kanban-col">
<div class="stage-header">
<span>Pendiente informe</span>
<span class="badge bg-warning rounded-pill">1</span>
</div>
<div class="kanban-items">
<div class="kanban-card" style="border-left-color:#f7941d">
<div class="d-flex justify-content-between align-items-start mb-1">
<div class="fw-bold text-dark-gm" style="font-size:.95rem;">Inspección anual</div>
<span class="badge bg-light text-secondary border small">#1039</span>
</div>
<div class="small text-muted mb-2"><i class="fas fa-user me-1"></i> Municipalidad de Alajuela</div>
</div>
</div>
</div>
</div>
Cabecera de ficha
Encabezado del detalle de un registro: volver, identificador, título, chips y acciones.
GrupoMecsaProyectos/pages/proyecto.php
<div class="pd-head">
<div class="pd-head-main">
<a href="#" class="pd-back" title="Volver a la lista"><i class="fas fa-arrow-left"></i></a>
<div>
<div class="pd-eyebrow">Proyecto #1042 · Tarea 88431</div>
<h1 class="pd-title">Mantenimiento planta norte</h1>
<div class="pd-chips">
<span class="pd-chip pd-chip-state">En ejecución</span>
<span class="pd-chip">Mantenimiento industrial</span>
</div>
</div>
</div>
<div class="pd-head-actions">
<button class="pd-btn"><i class="fas fa-pen"></i> Editar</button>
<button class="pd-btn pd-btn-primary"><i class="fas fa-file-invoice"></i> Facturar</button>
</div>
</div>
Carrusel de portada
La apertura de la home: video de fondo por diapositiva, titular y dos llamados a la acción.
GrupoMecsa/index.php
<div id="carouselHome" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active carousel-item-gm">
<!-- En producción acá va el <iframe> de Vimeo; se simula con un fondo plano -->
<div class="video-background-wrapper" style="position:absolute;inset:0;background:#112542;"></div>
<div class="carousel-caption carousel-caption-gm d-none d-md-block">
<h2 class="h2 h2-gm animate-text">Torres libres de rayos son una realidad</h2>
<h3 class="h1 h1-gm animate-text">PARARRAYOS PARA TORRES DE <br> TELECOMUNICACIÓN</h3>
<div>
<a href="#" class="btn btn-primary btn-gm-primary">Conocer Más</a>
<a href="#" class="btn btn-outline-light btn-gm-outline-light">Contáctenos</a>
</div>
</div>
</div>
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#carouselHome" data-bs-slide="prev">
<span class="carousel-control-prev-icon"></span><span class="visually-hidden">Anterior</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#carouselHome" data-bs-slide="next">
<span class="carousel-control-next-icon"></span><span class="visually-hidden">Siguiente</span>
</button>
</div>
<iframe> de Vimeo en .video-background-wrapper (acá va en plano para poder verlo). El video es decorativo: sin sonido, en bucle y sin controles, y el texto nunca depende de él.Tarjeta de colaborador
El equipo en /nosotros. El orden lo define el campo orden del departamento en KoraCMS.
GrupoMecsa/components/Colaboradores.php
<div class="row" style="max-width:560px">
<div class="col-6 empleado-card mt-gm-colaboradores">
<div class="card text-center mb-3 card-gm-primary border-0 h-100">
<div class="card-body card-body-gm-primary d-flex flex-column align-items-center">
<div class="avatar-container">
<img src="../../assets/ph-avatar.svg" alt="Colaborador" loading="lazy" decoding="async">
</div>
<h5 class="h4 h4-gm mt-4 mb-1">María Rodríguez</h5>
<p class="p-gm text-light mb-2">Jefatura de Operaciones</p>
</div>
</div>
</div>
<div class="col-6 empleado-card mt-gm-colaboradores">
<div class="card text-center mb-3 card-gm-primary border-0 h-100">
<div class="card-body card-body-gm-primary d-flex flex-column align-items-center">
<div class="avatar-container">
<img src="../../assets/ph-avatar.svg" alt="Colaborador" loading="lazy" decoding="async">
</div>
<h5 class="h4 h4-gm mt-4 mb-1">Jorge Sánchez</h5>
<p class="p-gm text-light mb-2">Ingeniería de Proyectos</p>
</div>
</div>
</div>
</div>
.mt-gm-colaboradores, que reserva ese espacio. Sin esa clase la foto se corta.Testimonios
Carrusel de testimonios de clientes: dos por diapositiva en escritorio, uno en móvil.
GrupoMecsa/components/testimonios.php
<section class="py-5">
<div class="container text-center">
<h2 class="mb-4 text-dark-gm">
Testimonio <span class="text-primary-gm fw-bold">de nuestros clientes</span>
</h2>
<div id="carouselTestimonios" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<div class="row justify-content-center g-4 row-carousel-testimonios-gm">
<div class="col-md-6">
<div class="card border-0 h-100">
<div class="card-body p-4 card-body-gm">
<blockquote class="blockquote mb-4">
<p class="mb-4 text-secondary">“ Resolvieron la protección de toda la planta en tiempo récord y con el informe al día. ”</p>
<footer class="blockquote-footer mt-3">
<strong>María Rodríguez</strong><br>
<span class="text-primary-gm">Jefatura de Mantenimiento, Constructora del Sur</span>
</footer>
</blockquote>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card border-0 h-100">
<div class="card-body p-4 card-body-gm">
<blockquote class="blockquote mb-4">
<p class="mb-4 text-secondary">“ La demarcación quedó impecable y no tuvimos que cerrar la vía más de un día. ”</p>
<footer class="blockquote-footer mt-3">
<strong>Jorge Sánchez</strong><br>
<span class="text-primary-gm">Dirección de Obras, Municipalidad de Alajuela</span>
</footer>
</blockquote>
</div>
</div>
</div>
</div>
</div>
</div>
<button class="carousel-control-prev carousel-control-gm" type="button" data-bs-target="#carouselTestimonios" data-bs-slide="prev">
<span class="carousel-control-prev-icon bg-primary rounded-circle p-2"></span>
<span class="visually-hidden">Anterior</span>
</button>
<button class="carousel-control-next carousel-control-gm" type="button" data-bs-target="#carouselTestimonios" data-bs-slide="next">
<span class="carousel-control-next-icon bg-primary rounded-circle p-2"></span>
<span class="visually-hidden">Siguiente</span>
</button>
</div>
</div>
</section>
d-none d-md-block y d-block d-md-none) porque cambia la cantidad de tarjetas por diapositiva. El contenido sale de la tabla testimoniales.Acordeón de servicios
Navegación lateral de la página de servicios: categorías plegables con sus subcategorías.
GrupoMecsa/pages/servicios.php
<div class="accordion submenu-gm-servicios" id="accordionCategorias" style="max-width:360px">
<div class="accordion-item submenu-gm-servicios-categorias">
<h2 class="accordion-header" id="heading-electrico">
<button class="accordion-button accordion-button-gm-categorias" type="button"
data-bs-toggle="collapse" data-bs-target="#cat-electrico"
aria-expanded="true" aria-controls="cat-electrico">
Eléctrico
</button>
</h2>
<div id="cat-electrico" class="accordion-collapse collapse show"
aria-labelledby="heading-electrico" data-bs-parent="#accordionCategorias">
<div class="accordion-body p-0">
<ul class="list-group list-group-flush">
<li class="list-group-item list-group-item-gm active">
<a class="text-decoration-none text-white" href="#">Protección contra rayos</a>
</li>
<li class="list-group-item list-group-item-gm">
<a class="text-decoration-none" href="#">Puestas a tierra</a>
</li>
<li class="list-group-item list-group-item-gm">
<a class="text-decoration-none" href="#">Estudios eléctricos</a>
</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item submenu-gm-servicios-categorias">
<h2 class="accordion-header" id="heading-vial">
<button class="accordion-button accordion-button-gm-categorias collapsed" type="button"
data-bs-toggle="collapse" data-bs-target="#cat-vial"
aria-expanded="false" aria-controls="cat-vial">
Vialidad
</button>
</h2>
<div id="cat-vial" class="accordion-collapse collapse"
aria-labelledby="heading-vial" data-bs-parent="#accordionCategorias">
<div class="accordion-body p-0">
<ul class="list-group list-group-flush">
<li class="list-group-item list-group-item-gm">
<a class="text-decoration-none" href="#">Asfalto</a>
</li>
<li class="list-group-item list-group-item-gm">
<a class="text-decoration-none" href="#">Demarcación</a>
</li>
</ul>
</div>
</div>
</div>
</div>
.submenu-gm-servicios en el acordeón (es la que fuerza el fondo blanco y el texto #495057 de los ítems) y .submenu-gm-servicios-categorias en cada .accordion-item (borde, sombra y radio). Sin la primera gana .list-group-item-gm, que pinta todo navy.Hero del blog
La apertura del blog: titular, bajada e ilustración a la derecha.
GrupoMecsa/pages/blog.php
<div class="gm-hero-new">
<div class="container-lg">
<div class="row align-items-center">
<div class="col-md-6">
<h1 class="gm-hero-title-new display-4 mb-3">Grupo Mecsa Blog</h1>
<p class="gm-hero-subtitle-new lead mb-4">Descubre las últimas noticias, consejos y tendencias sobre protección eléctrica y pararrayos.</p>
</div>
<div class="col-md-6 text-center d-none d-md-block">
<img src="../../assets/ph-wide.svg" alt="Ilustración" class="img-fluid gm-hero-image" style="max-height:260px;">
</div>
</div>
</div>
</div>
Galería de producto
Imagen principal en 4:3 y miniaturas debajo. Las miniaturas también aceptan video.
GrupoMecsa/ecommerce/product.php
<div class="product-gallery d-flex flex-column align-items-start gap-3" style="max-width:460px">
<div class="ratio ratio-4x3 bg-light d-flex align-items-center justify-content-center w-100">
<img src="../../assets/ph-square.svg" alt="Producto" style="object-fit:contain;">
</div>
<div class="d-flex gap-2 mt-3 flex-row justify-content-center flex-wrap w-100">
<img src="../../assets/ph-square.svg" class="product-gallery-thumb" alt="Miniatura 1" width="72" height="72">
<img src="../../assets/ph-square.svg" class="product-gallery-thumb" alt="Miniatura 2" width="72" height="72">
<img src="../../assets/ph-square.svg" class="product-gallery-thumb" alt="Miniatura 3" width="72" height="72">
</div>
</div>
object-fit: contain a propósito: los productos vienen con fondos y proporciones distintas y recortarlos deja fotos mutiladas.Carrusel de clientes
La cinta de logos de «Casos de Éxito». Los logos se duplican para que el desplazamiento sea continuo.
GrupoMecsa/components/CarrucelReferencias.php
<section class="clientes pb-5 pt-2">
<div class="container">
<h4 class="h2 h2-gm text-primary-gm text-primary-title-gm mb-4 mt-4">Caso de Éxitos</h4>
<div class="logo-slider mb-5">
<div class="logos-track">
<div class="logo-item"><img src="../../assets/ph-avatar.svg" alt="Cliente" title="Cliente"></div>
<div class="logo-item"><img src="../../assets/ph-avatar.svg" alt="Cliente" title="Cliente"></div>
<div class="logo-item"><img src="../../assets/ph-avatar.svg" alt="Cliente" title="Cliente"></div>
<div class="logo-item"><img src="../../assets/ph-avatar.svg" alt="Cliente" title="Cliente"></div>
<div class="logo-item"><img src="../../assets/ph-avatar.svg" alt="Cliente" title="Cliente"></div>
<div class="logo-item"><img src="../../assets/ph-avatar.svg" alt="Cliente" title="Cliente"></div>
</div>
</div>
</div>
</section>
array_merge($clientes, $clientes)): así la animación puede reiniciar sin que se note el corte. Los logos salen del bucket clientes de Supabase.Dashboard
La composición completa de la portada de una app: indicadores, accesos rápidos y listas resumen.
GrupoMecsaProyectos/pages/dashboard.php
<div class="dashboard-premium">
<div class="row g-3 mb-4">
<div class="col-6 col-lg-3">
<div class="card kpi-card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center">
<div class="icon-circle bg-primary bg-opacity-10 text-primary me-3"><i class="fas fa-diagram-project"></i></div>
<div><div class="kpi-label">Activos</div><div class="kpi-value" data-counter="24">0</div></div>
</div>
</div>
</div>
<div class="col-6 col-lg-3">
<div class="card kpi-card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center">
<div class="icon-circle bg-warning bg-opacity-10 text-warning me-3"><i class="fas fa-clock"></i></div>
<div><div class="kpi-label">Pendientes</div><div class="kpi-value text-warning" data-counter="7">0</div></div>
</div>
</div>
</div>
<div class="col-6 col-lg-3">
<div class="card kpi-card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center">
<div class="icon-circle bg-success bg-opacity-10 text-success me-3"><i class="fas fa-circle-check"></i></div>
<div><div class="kpi-label">Completados</div><div class="kpi-value text-success" data-counter="112">0</div></div>
</div>
</div>
</div>
<div class="col-6 col-lg-3">
<div class="card kpi-card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center">
<div class="icon-circle bg-info bg-opacity-10 text-info me-3"><i class="fas fa-file-invoice-dollar"></i></div>
<div><div class="kpi-label">Facturado</div><div class="kpi-value">₡8.4M</div></div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-4">
<div class="col-6 col-md-3">
<a href="#" class="action-card text-decoration-none gm-hover-lift">
<div class="action-icon bg-primary bg-opacity-10 text-primary"><i class="fa fa-hard-hat"></i></div>
<span class="action-label">Nuevo Proyecto</span>
</a>
</div>
<div class="col-6 col-md-3">
<a href="#" class="action-card text-decoration-none gm-hover-lift">
<div class="action-icon bg-success bg-opacity-10 text-success"><i class="fa fa-user-plus"></i></div>
<span class="action-label">Agregar Cliente</span>
</a>
</div>
<div class="col-6 col-md-3">
<a href="#" class="action-card text-decoration-none gm-hover-lift">
<div class="action-icon bg-warning bg-opacity-10 text-warning"><i class="fa fa-file-invoice-dollar"></i></div>
<span class="action-label">Facturar</span>
</a>
</div>
<div class="col-6 col-md-3">
<a href="#" class="action-card text-decoration-none gm-hover-lift">
<div class="action-icon bg-info bg-opacity-10 text-info"><i class="fa fa-chart-line"></i></div>
<span class="action-label">Ver reportes</span>
</a>
</div>
</div>
<div class="card summary-card border-0 shadow-sm gm-hover-lift">
<div class="card-header d-flex justify-content-between align-items-center">
<h6>Proyectos Recientes</h6>
<a href="#" class="btn btn-white btn-sm rounded-pill px-3 border shadow-sm fw-bold">Ver todos</a>
</div>
<div class="card-body">
<div class="gm-stagger">
<div class="list-item-premium">
<div class="item-info">
<span class="item-title">Mantenimiento planta norte</span>
<span class="item-subtitle">Constructora del Sur</span>
</div>
<div class="item-meta"><span class="item-value text-primary-gm">En ejecución</span></div>
</div>
<div class="list-item-premium">
<div class="item-info">
<span class="item-title">Instalación de bombas</span>
<span class="item-subtitle">Agroindustrias CR</span>
</div>
<div class="item-meta"><span class="item-value text-primary-gm">Pendiente informe</span></div>
</div>
</div>
</div>
</div>
</div>
Hero de servicios
La franja de apertura de las páginas internas: fondo navy y el título de la sección.
GrupoMecsa/pages/servicios.php
<div class="hero-servicios-gm">
<h1 class="h1-gm fw-semi-bold titulo-servicios-gm">Protección contra rayos</h1>
</div>
Mapa de cobertura
El mapa de /nosotros con un punto por país donde Grupo Mecsa tiene trabajos.
GrupoMecsa/pages/nosotros.php
<div class="map-container">
<img src="../../assets/ph-wide.svg" alt="Mapa de cobertura Grupo Mecsa" class="img-fluid w-100">
<div class="point mexico" title="México"></div>
<div class="point guatemala" title="Guatemala"></div>
<div class="point honduras" title="Honduras"></div>
<div class="point nicaragua" title="Nicaragua"></div>
<div class="point costa-rica" title="Costa Rica"></div>
<div class="point panama" title="Panamá"></div>
</div>
.point.costa-rica) en porcentajes sobre la imagen. Si cambiás el mapa hay que reubicarlos todos.