Merge branch 'main' of github.com:elmer-20/direccion_de_admision_2026
commit
fb154d38dc
@ -0,0 +1,313 @@
|
||||
<!-- src/components/web/CarrerasSection.vue -->
|
||||
<template>
|
||||
<section class="carreras-section">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<a-typography-title :level="2" class="title">
|
||||
Carreras del Área: {{ areaNombre }}
|
||||
</a-typography-title>
|
||||
|
||||
<a-typography-paragraph class="subtitle">
|
||||
Información del programa, grado/título y resumen
|
||||
</a-typography-paragraph>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-divider class="divider" />
|
||||
|
||||
<div v-if="filteredCarreras.length">
|
||||
<a-row :gutter="[18, 18]">
|
||||
<a-col
|
||||
v-for="carrera in filteredCarreras"
|
||||
:key="carrera.id"
|
||||
:xs="24"
|
||||
:sm="12"
|
||||
:lg="8"
|
||||
class="career-col"
|
||||
>
|
||||
<!-- CINTA ARRIBA: ÁREA -->
|
||||
<a-badge-ribbon :text="areaNombre" color="blue">
|
||||
<a-card
|
||||
hoverable
|
||||
class="card"
|
||||
:bodyStyle="{ padding: '12px' }"
|
||||
@click="goToDetail(carrera)"
|
||||
>
|
||||
<!-- ✅ SOLO 1 IMAGEN: COVER -->
|
||||
<template v-if="getImage(carrera)" #cover>
|
||||
<div
|
||||
class="cover"
|
||||
:style="{ backgroundImage: `url(${getImage(carrera)})` }"
|
||||
>
|
||||
<div class="cover-overlay" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Contenido -->
|
||||
<div class="content">
|
||||
<a-typography-title
|
||||
:level="5"
|
||||
class="card-title"
|
||||
:content="carrera.escuela_profesional"
|
||||
:ellipsis="{ rows: 2, tooltip: carrera.escuela_profesional }"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
<!-- ✅ Resumen: 20 palabras + 4 filas -->
|
||||
<a-typography-paragraph
|
||||
class="desc"
|
||||
:title="carrera.resumen_general || 'Sin resumen registrado.'"
|
||||
>
|
||||
{{ truncateWords(carrera.resumen_general || "Sin resumen registrado.", 20) }}
|
||||
</a-typography-paragraph>
|
||||
|
||||
<div class="actions">
|
||||
<a-button type="link" class="read-more" @click.stop="goToDetail(carrera)">
|
||||
Ver detalle
|
||||
<ArrowRightOutlined />
|
||||
</a-button>
|
||||
|
||||
<a-tag color="gold" class="tag-soft">
|
||||
{{ (carrera.perfil_de_egresado?.competencias_especificas?.length || 0) }} CE
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-badge-ribbon>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
|
||||
<a-empty
|
||||
v-else
|
||||
:description="`No hay carreras registradas para el área: ${areaNombre}.`"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from "vue"
|
||||
import { ArrowRightOutlined } from "@ant-design/icons-vue"
|
||||
import { useRouter } from "vue-router"
|
||||
|
||||
import { carreras } from "../../../../data/carreras"
|
||||
import { areas } from "../../../../data/areas"
|
||||
|
||||
const props = defineProps({
|
||||
areaId: { type: Number, default: 1 },
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const areaNombre = computed(() => {
|
||||
const a = (areas ?? []).find((x) => Number(x.id) === Number(props.areaId))
|
||||
return a?.nombre || `Área ${props.areaId}`
|
||||
})
|
||||
|
||||
const filteredCarreras = computed(() => {
|
||||
return (carreras ?? []).filter((c) => Number(c.areaId) === Number(props.areaId))
|
||||
})
|
||||
|
||||
// ✅ más robusto: ignora "/", "#", etc.
|
||||
const getImage = (c) => {
|
||||
const invalid = new Set(["/", "#", "null", "undefined"])
|
||||
const candidates = [c?.imagen, c?.imagen1, c?.imagen2, c?.imagen3]
|
||||
.map((x) => (x ?? "").toString().trim())
|
||||
.filter(Boolean)
|
||||
return candidates.find((src) => !invalid.has(src)) || ""
|
||||
}
|
||||
|
||||
const goToDetail = (carrera) => {
|
||||
router.push({ name: "ingenierias-detalle", params: { id: carrera.id } })
|
||||
}
|
||||
|
||||
const truncateWords = (text, limit = 20) => {
|
||||
if (!text) return ""
|
||||
const words = String(text).trim().split(/\s+/).filter(Boolean)
|
||||
if (words.length <= limit) return words.join(" ")
|
||||
return words.slice(0, limit).join(" ") + "..."
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ✅ AntDV “puro” + institucional: fondo limpio, bordes suaves */
|
||||
.carreras-section {
|
||||
padding: 64px 0;
|
||||
background: #f5f7fa; /* parecido a layout antd */
|
||||
}
|
||||
|
||||
/* ✅ Forzar Times en TODO (incluye AntDV interno) */
|
||||
.carreras-section :deep(*) {
|
||||
font-family: "Times New Roman", Times, serif !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
width: 100%;
|
||||
max-width: 880px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 !important;
|
||||
font-weight: 900;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
letter-spacing: -0.2px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 8px 0 0 !important;
|
||||
color: rgba(0, 0, 0, 0.60);
|
||||
line-height: 1.6;
|
||||
font-size: 0.98rem;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 16px 0 22px !important;
|
||||
}
|
||||
|
||||
/* columnas parejitas */
|
||||
.career-col {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* ✅ Card más “institucional” (antd-like) */
|
||||
.card {
|
||||
width: 100%;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #f0f0f0;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
transition: box-shadow 0.18s ease, transform 0.18s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* ✅ Cover más pequeño y limpio */
|
||||
.cover {
|
||||
position: relative;
|
||||
height: 140px;
|
||||
background-size: cover; /* moderno */
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #fafafa;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.cover-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
}
|
||||
|
||||
/* pill institucional */
|
||||
.pill {
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
bottom: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 7px 10px;
|
||||
border-radius: 999px;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
background: rgba(22, 119, 255, 0.45); /* azul antd */
|
||||
border: 1px solid rgba(255, 255, 255, 0.22);
|
||||
backdrop-filter: blur(6px);
|
||||
font-size: 0.86rem;
|
||||
font-weight: 700;
|
||||
max-width: calc(100% - 24px);
|
||||
}
|
||||
|
||||
.pill-strong { font-weight: 900; }
|
||||
.pill-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ✅ contenido con “actions” abajo siempre */
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* título compacto */
|
||||
.card-title {
|
||||
margin: 0 !important;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 900;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
min-height: 44px; /* empareja cards */
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tag-soft {
|
||||
border-radius: 999px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.meta-text {
|
||||
color: rgba(0, 0, 0, 0.70);
|
||||
line-height: 1.35;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
/* ✅ 20 palabras + 4 filas (clamp) */
|
||||
.desc {
|
||||
margin: 0 !important;
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
line-height: 1.55;
|
||||
font-size: 0.94rem;
|
||||
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
|
||||
min-height: calc(1.55em * 4);
|
||||
}
|
||||
|
||||
/* acciones abajo */
|
||||
.actions {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.read-more {
|
||||
padding: 0;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.carreras-section {
|
||||
padding: 52px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,542 @@
|
||||
<!-- src/components/web/CarreraDetalle.vue -->
|
||||
<template>
|
||||
<NavbarModerno />
|
||||
|
||||
<section class="detalle-section">
|
||||
<div class="container">
|
||||
<!-- Topbar en card -->
|
||||
<a-card class="topbar-card" :bodyStyle="{ padding: '12px 14px' }">
|
||||
<div class="topbar">
|
||||
<a-space>
|
||||
<a-button @click="goBack">Volver</a-button>
|
||||
<a-breadcrumb class="crumb">
|
||||
<a-breadcrumb-item>Áreas</a-breadcrumb-item>
|
||||
<a-breadcrumb-item>{{ areaNombre }}</a-breadcrumb-item>
|
||||
<a-breadcrumb-item>{{ carrera?.escuela_profesional || "Detalle" }}</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
</a-space>
|
||||
|
||||
<a-space v-if="carrera">
|
||||
<a-tag color="blue" class="tag-soft">{{ areaNombre }}</a-tag>
|
||||
<a-tag color="gold" class="tag-soft">{{ competencias.length }} CE</a-tag>
|
||||
</a-space>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- 404 -->
|
||||
<a-result
|
||||
v-if="!carrera"
|
||||
status="404"
|
||||
title="Carrera no encontrada"
|
||||
sub-title="No existe un registro con el ID solicitado."
|
||||
class="mt16"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="goBack">Volver</a-button>
|
||||
</template>
|
||||
</a-result>
|
||||
|
||||
<div v-else>
|
||||
<!-- HERO -->
|
||||
<a-card class="hero-card" :bodyStyle="{ padding: 0 }">
|
||||
<div
|
||||
v-if="selectedImage"
|
||||
class="hero"
|
||||
:style="{ backgroundImage: `url(${selectedImage})` }"
|
||||
@click="openPreview(selectedImage)"
|
||||
title="Ver imagen completa"
|
||||
>
|
||||
<div class="hero-overlay"></div>
|
||||
|
||||
<div class="hero-inner">
|
||||
<div class="hero-tags">
|
||||
<a-tag color="blue" class="tag-soft">{{ areaNombre }}</a-tag>
|
||||
<a-tag color="geekblue" class="tag-soft">{{ carrera.facultad || "Sin facultad" }}</a-tag>
|
||||
<a-tag color="purple" class="tag-soft">{{ competencias.length }} CE</a-tag>
|
||||
</div>
|
||||
|
||||
<a-typography-title :level="2" class="hero-title">
|
||||
{{ carrera.escuela_profesional }}
|
||||
</a-typography-title>
|
||||
|
||||
<a-typography-paragraph class="hero-sub">
|
||||
<b>Programa:</b> {{ carrera.programa_de_estudios || "No registrado" }}
|
||||
<span class="sep">•</span>
|
||||
<b>Grado:</b> {{ carrera.grado_academico || "No registrado" }}
|
||||
</a-typography-paragraph>
|
||||
|
||||
<div class="hero-actions">
|
||||
<a-button type="primary" @click.stop="openPreview(selectedImage)">
|
||||
Ver imagen
|
||||
</a-button>
|
||||
<a-button @click.stop="scrollTo('#seccion-competencias')">
|
||||
Ir a competencias
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<div class="hero-hint">Click en la imagen para ampliar</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fallback sin imagen -->
|
||||
<div v-else class="hero-empty">
|
||||
<div class="hero-empty-inner">
|
||||
<a-typography-title :level="2" class="hero-title-dark">
|
||||
{{ carrera.escuela_profesional }}
|
||||
</a-typography-title>
|
||||
<a-alert type="info" show-icon message="Sin imágenes registradas." />
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
|
||||
<!-- Layout principal -->
|
||||
<a-row :gutter="[18, 18]" class="main">
|
||||
<!-- IZQUIERDA -->
|
||||
<a-col :xs="24" :lg="8">
|
||||
<!-- Galería -->
|
||||
<a-card class="card sticky" :bodyStyle="{ padding: '12px' }">
|
||||
<div class="card-head">
|
||||
<div class="accent"></div>
|
||||
<a-typography-title :level="5" class="h5">Galería</a-typography-title>
|
||||
</div>
|
||||
|
||||
<div class="thumbs" v-if="secondaryImages.length">
|
||||
<button
|
||||
v-for="(img, idx) in secondaryImages"
|
||||
:key="idx"
|
||||
class="thumb"
|
||||
type="button"
|
||||
@click="selectImage(img)"
|
||||
title="Seleccionar"
|
||||
>
|
||||
<div class="thumb-inner" @click.stop="openPreview(img)" title="Ver completa">
|
||||
<a-image :src="img" :preview="false" class="thumb-img" />
|
||||
<div class="thumb-badge">Ampliar</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<a-empty v-else description="No hay imágenes secundarias." class="empty-small" />
|
||||
|
||||
<a-divider class="mini-divider" />
|
||||
|
||||
<a-space style="width:100%">
|
||||
<a-button block type="primary" @click="openPreview(selectedImage)" :disabled="!selectedImage">
|
||||
Ver principal
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-card>
|
||||
|
||||
<!-- Ficha -->
|
||||
<a-card class="card mt16" :bodyStyle="{ padding: '12px' }">
|
||||
<div class="card-head">
|
||||
<div class="accent"></div>
|
||||
<a-typography-title :level="5" class="h5">Ficha rápida</a-typography-title>
|
||||
</div>
|
||||
|
||||
<a-descriptions size="small" :column="1" bordered>
|
||||
<a-descriptions-item label="Área">{{ areaNombre }}</a-descriptions-item>
|
||||
<a-descriptions-item label="Facultad">{{ carrera.facultad || "No registrada" }}</a-descriptions-item>
|
||||
<a-descriptions-item label="Programa">{{ carrera.programa_de_estudios || "No registrado" }}</a-descriptions-item>
|
||||
<a-descriptions-item label="Título">{{ carrera.titulo_profesional || "No registrado" }}</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
<!-- DERECHA -->
|
||||
<a-col :xs="24" :lg="16">
|
||||
<!-- Resumen -->
|
||||
<a-card class="card" :bodyStyle="{ padding: '14px' }">
|
||||
<div class="card-head big">
|
||||
<div class="accent"></div>
|
||||
<a-typography-title :level="4" class="h4">Resumen general</a-typography-title>
|
||||
</div>
|
||||
|
||||
<a-typography-paragraph class="p">
|
||||
{{ carrera.resumen_general || "Sin resumen registrado." }}
|
||||
</a-typography-paragraph>
|
||||
</a-card>
|
||||
|
||||
<!-- Competencias -->
|
||||
<a-card class="card mt16" :bodyStyle="{ padding: '14px' }">
|
||||
<div id="seccion-competencias"></div>
|
||||
|
||||
<div class="card-head big">
|
||||
<div class="accent"></div>
|
||||
<a-typography-title :level="4" class="h4">Competencias específicas</a-typography-title>
|
||||
</div>
|
||||
|
||||
<a-alert
|
||||
v-if="!competencias.length"
|
||||
type="info"
|
||||
show-icon
|
||||
message="Sin competencias registradas."
|
||||
/>
|
||||
|
||||
<a-table
|
||||
v-else
|
||||
:columns="columns"
|
||||
:data-source="competencias"
|
||||
:pagination="{ pageSize: 8, hideOnSinglePage: true }"
|
||||
size="middle"
|
||||
rowKey="codigo"
|
||||
class="table"
|
||||
/>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal imagen completa -->
|
||||
<a-modal v-model:open="previewVisible" :footer="null" centered :width="980">
|
||||
<div class="modal-wrap">
|
||||
<img :src="previewSrc" class="modal-img" alt="Vista completa" />
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<a-back-top />
|
||||
</section>
|
||||
|
||||
<FooterModerno />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, watch } from "vue"
|
||||
import { useRoute, useRouter } from "vue-router"
|
||||
import NavbarModerno from "../../../nabvar.vue"
|
||||
import FooterModerno from "../../../Footer.vue"
|
||||
|
||||
import { carreras } from "../../../../data/carreras"
|
||||
import { areas } from "../../../../data/areas"
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const carreraId = computed(() => Number(route.params.id))
|
||||
|
||||
const carrera = computed(() => (carreras ?? []).find((c) => Number(c.id) === carreraId.value))
|
||||
|
||||
const areaNombre = computed(() => {
|
||||
const id = carrera.value?.areaId
|
||||
const a = (areas ?? []).find((x) => Number(x.id) === Number(id))
|
||||
return a?.nombre || (id != null ? `Área ${id}` : "Área")
|
||||
})
|
||||
|
||||
/** 3 slots (sin dedupe) */
|
||||
const collectImages = (c) => {
|
||||
if (!c) return []
|
||||
const invalid = new Set(["/", "#", "null", "undefined"])
|
||||
return [c?.imagen1, c?.imagen2, c?.imagen3]
|
||||
.map((x) => (x ?? "").toString().trim())
|
||||
.filter((src) => src && !invalid.has(src))
|
||||
}
|
||||
|
||||
const images = computed(() => collectImages(carrera.value))
|
||||
|
||||
const selectedImage = ref("")
|
||||
watch(images, (list) => (selectedImage.value = list?.[0] || ""), { immediate: true })
|
||||
|
||||
const secondaryImages = computed(() => images.value.slice(1, 3))
|
||||
const selectImage = (img) => (selectedImage.value = img)
|
||||
|
||||
/** Modal */
|
||||
const previewVisible = ref(false)
|
||||
const previewSrc = ref("")
|
||||
const openPreview = (img) => {
|
||||
if (!img) return
|
||||
previewSrc.value = img
|
||||
previewVisible.value = true
|
||||
}
|
||||
|
||||
/** Competencias */
|
||||
const competencias = computed(() => carrera.value?.perfil_de_egresado?.competencias_especificas ?? [])
|
||||
|
||||
const columns = [
|
||||
{ title: "Código", dataIndex: "codigo", key: "codigo", width: 120 },
|
||||
{ title: "Resumen", dataIndex: "resumen", key: "resumen" },
|
||||
]
|
||||
|
||||
const goBack = () => {
|
||||
if (window.history.length > 1) router.back()
|
||||
else router.push({ name: "home" })
|
||||
}
|
||||
|
||||
const scrollTo = (selector) => {
|
||||
const el = document.querySelector(selector)
|
||||
if (el) el.scrollIntoView({ behavior: "smooth", block: "start" })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Fondo institucional más fino */
|
||||
.detalle-section {
|
||||
padding: 28px 0 56px;
|
||||
background:
|
||||
radial-gradient(900px 260px at 10% 0%, rgba(22,119,255,0.10) 0%, transparent 60%),
|
||||
radial-gradient(900px 260px at 90% 0%, rgba(114,46,209,0.08) 0%, transparent 60%),
|
||||
#f5f7fa;
|
||||
}
|
||||
|
||||
/* Times everywhere */
|
||||
.detalle-section :deep(*) {
|
||||
font-family: "Times New Roman", Times, serif !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
/* Topbar card */
|
||||
.topbar-card {
|
||||
border-radius: 14px;
|
||||
border: 1px solid #f0f0f0;
|
||||
background: rgba(255,255,255,0.92);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.crumb {
|
||||
max-width: 780px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Hero */
|
||||
.hero-card {
|
||||
margin-top: 14px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid #f0f0f0;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
box-shadow: 0 12px 30px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.hero {
|
||||
position: relative;
|
||||
height: 280px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.hero-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(11, 24, 70, 0.78) 0%,
|
||||
rgba(11, 24, 70, 0.40) 55%,
|
||||
rgba(11, 24, 70, 0.18) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.hero-inner {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
padding: 18px 18px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
max-width: 920px;
|
||||
}
|
||||
|
||||
.hero-tags {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tag-soft {
|
||||
border-radius: 999px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
margin: 0 !important;
|
||||
color: rgba(255,255,255,0.97);
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.2px;
|
||||
line-height: 1.12;
|
||||
}
|
||||
|
||||
.hero-sub {
|
||||
margin: 0 !important;
|
||||
color: rgba(255,255,255,0.90);
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.sep {
|
||||
margin: 0 10px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.hero-hint {
|
||||
margin-top: 2px;
|
||||
color: rgba(255,255,255,0.85);
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.hero-empty {
|
||||
padding: 18px;
|
||||
}
|
||||
.hero-empty-inner {
|
||||
max-width: 900px;
|
||||
}
|
||||
.hero-title-dark {
|
||||
margin: 0 0 10px !important;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
/* Main layout */
|
||||
.main {
|
||||
margin-top: 18px;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
border-radius: 14px;
|
||||
border: 1px solid #f0f0f0;
|
||||
background: rgba(255,255,255,0.92);
|
||||
backdrop-filter: blur(8px);
|
||||
box-shadow: 0 10px 24px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.mt16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.sticky {
|
||||
position: sticky;
|
||||
top: 14px;
|
||||
}
|
||||
|
||||
/* Card headings with accent bar */
|
||||
.card-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.card-head.big {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.accent {
|
||||
width: 6px;
|
||||
height: 18px;
|
||||
border-radius: 999px;
|
||||
background: rgba(22, 119, 255, 0.85);
|
||||
}
|
||||
|
||||
.h4 {
|
||||
margin: 0 !important;
|
||||
font-weight: 900;
|
||||
color: rgba(0,0,0,0.88);
|
||||
}
|
||||
.h5 {
|
||||
margin: 0 !important;
|
||||
font-weight: 900;
|
||||
color: rgba(0,0,0,0.86);
|
||||
}
|
||||
|
||||
.p {
|
||||
margin: 0 !important;
|
||||
color: rgba(0,0,0,0.72);
|
||||
line-height: 1.78;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* thumbs */
|
||||
.thumbs {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
padding: 0;
|
||||
border: 1px solid #f0f0f0;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
.thumb:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 10px 22px rgba(0,0,0,0.08);
|
||||
border-color: rgba(22,119,255,0.45);
|
||||
}
|
||||
|
||||
.thumb-inner {
|
||||
position: relative;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.thumb-img :deep(img) {
|
||||
width: 100%;
|
||||
height: 110px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.thumb-badge {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
bottom: 8px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
background: rgba(0,0,0,0.40);
|
||||
color: rgba(255,255,255,0.95);
|
||||
font-weight: 900;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.mini-divider {
|
||||
margin: 12px 0 !important;
|
||||
}
|
||||
|
||||
.empty-small :deep(.ant-empty-description) {
|
||||
color: rgba(0,0,0,0.55);
|
||||
}
|
||||
|
||||
/* Table */
|
||||
.table :deep(.ant-table-thead > tr > th) {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
.modal-wrap { padding: 6px; }
|
||||
.modal-img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero {
|
||||
height: 230px;
|
||||
}
|
||||
.sticky {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,5 @@
|
||||
export const areas = [
|
||||
{ id: 1, nombre: "Ingenierías" },
|
||||
{ id: 2, nombre: "Biomédicas" },
|
||||
{ id: 3, nombre: "Sociales" }
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue