feat: implement modular UMass theme components and supporting generation scripts for home page structure

This commit is contained in:
2026-07-29 23:03:29 +07:00
parent c1e0e39aad
commit 317df123bb
37 changed files with 7044 additions and 192 deletions
+1
View File
@@ -1,6 +1,7 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Debug (Attach) - Port 5005",
+77 -45
View File
@@ -1,19 +1,19 @@
# website_sisvietnam.vn
## Database Configuration
### Development Database (H2 Console)
- **Profile**: `dev`
- **Database Type**: H2 (File-based, embedded)
- **JDBC URL**: `jdbc:h2:file:./build/h2db/db/sisvietnamvn`
- **Username**: `sisvietnamvn`
- **Password**: *(leave blank)*
- **Password**: _(leave blank)_
- **Console Access**: [http://localhost:8080/h2-console](http://localhost:8080/h2-console) (requires app running in dev profile)
- **Config File**: [application-dev.yml](file:///d:/website_sisvietnam_moi/website_sisvietnam.vn/sisvietnamvn/src/main/resources/config/application-dev.yml)
### Production Database
- **Profile**: `prod`
- **Database Type**: Oracle Database
- **JDBC URL**: `jdbc:oracle:thin:@localhost:1521:xe`
@@ -21,6 +21,7 @@
- **Config File**: [application-prod.yml](file:///d:/website_sisvietnam_moi/website_sisvietnam.vn/sisvietnamvn/src/main/resources/config/application-prod.yml)
### Schema Management
- **Tool**: Liquibase
- **Changelog Folder**: [src/main/resources/config/liquibase/](file:///d:/website_sisvietnam_moi/website_sisvietnam.vn/sisvietnamvn/src/main/resources/config/liquibase/)
@@ -32,8 +33,8 @@ Already a pro? Just edit this README.md and make it your own. Want to make it ea
## Add your files
* [Create](https://docs.gitlab.com/user/project/repository/web_editor/#create-a-file) or [upload](https://docs.gitlab.com/user/project/repository/web_editor/#upload-a-file) files
* [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
- [Create](https://docs.gitlab.com/user/project/repository/web_editor/#create-a-file) or [upload](https://docs.gitlab.com/user/project/repository/web_editor/#upload-a-file) files
- [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
```
cd existing_repo
@@ -44,27 +45,27 @@ git push -uf origin main
## Integrate with your tools
* [Set up project integrations](http://[IP_ADDRESS]/pvdhien/website_sisvietnam.vn/-/settings/integrations)
- [Set up project integrations](http://[IP_ADDRESS]/pvdhien/website_sisvietnam.vn/-/settings/integrations)
## Collaborate with your team
* [Invite team members and collaborators](https://docs.gitlab.com/user/project/members/)
* [Create a new merge request](https://docs.gitlab.com/user/project/merge_requests/creating_merge_requests/)
* [Automatically close issues from merge requests](https://docs.gitlab.com/user/project/issues/managing_issues/#closing-issues-automatically)
* [Enable merge request approvals](https://docs.gitlab.com/user/project/merge_requests/approvals/)
* [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
- [Invite team members and collaborators](https://docs.gitlab.com/user/project/members/)
- [Create a new merge request](https://docs.gitlab.com/user/project/merge_requests/creating_merge_requests/)
- [Automatically close issues from merge requests](https://docs.gitlab.com/user/project/issues/managing_issues/#closing-issues-automatically)
- [Enable merge request approvals](https://docs.gitlab.com/user/project/merge_requests/approvals/)
- [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
## Test and Deploy
Use the built-in continuous integration in GitLab.
* [Get started with GitLab CI/CD](https://docs.gitlab.com/ci/quick_start/)
* [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/user/application_security/sast/)
* [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/topics/autodevops/requirements/)
* [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/user/clusters/agent/)
* [Set up protected environments](https://docs.gitlab.com/ci/environments/protected_environments/)
- [Get started with GitLab CI/CD](https://docs.gitlab.com/ci/quick_start/)
- [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/user/application_security/sast/)
- [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/topics/autodevops/requirements/)
- [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/user/clusters/agent/)
- [Set up protected environments](https://docs.gitlab.com/ci/environments/protected_environments/)
***
---
# Editing this README
@@ -75,30 +76,39 @@ When you're ready to make this README your own, just edit this file and use the
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name
Choose a self-explaining name for your project.
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
@@ -106,14 +116,16 @@ For people who want to make changes to your project, it's helpful to have some d
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
cd ..\sisvietnamvn\
.\gradlew bootRun
@@ -130,33 +142,53 @@ Bạn có thể refresh lại trang web để kiểm tra màu sắc trên menu h
## How to Debug (VS Code)
1. **Start the Application in Debug Mode**:
Open a terminal in the project directory (`sisvietnamvn_main`) and run:
```bash
./gradlew bootRun --debug-jvm
```
The application will start and pause, displaying: `Listening for transport dt_socket at address: 5005`.
1. **Start the Application in Debug Mode**:
Open a terminal in the project directory (`sisvietnamvn_main`) and run:
2. **Configure VS Code**:
Ensure your `.vscode/launch.json` has the following configuration:
```json
{
"type": "java",
"name": "Debug (Attach) - Port 5005",
"request": "attach",
"hostName": "127.0.0.1",
"port": 5005,
"projectName": "sisvietnamvn"
}
```
```bash
./gradlew bootRun --debug-jvm
```
3. **Attach the Debugger**:
- Go to the **Run and Debug** view in VS Code (`Ctrl+Shift+D`).
- Select **Debug (Attach) - Port 5005** from the dropdown at the top.
- Click the green **Play** button (or press `F5`).
- The application will resume booting.
The application will start and pause, displaying: `Listening for transport dt_socket at address: 5005`.
4. **Set Breakpoints & Debug**:
- Open any Java file (e.g., `SwiperSliderPlugin.java`).
- Click in the left margin next to a line number to place a red breakpoint.
- Trigger the code (e.g., by navigating to the page in your browser). Execution will pause at your breakpoint, allowing you to inspect variables!
2. **Configure VS Code**:
Ensure your `.vscode/launch.json` has the following configuration:
```json
{
"type": "java",
"name": "Debug (Attach) - Port 5005",
"request": "attach",
"hostName": "127.0.0.1",
"port": 5005,
"projectName": "sisvietnamvn"
}
```
3. **Attach the Debugger**:
- Go to the **Run and Debug** view in VS Code (`Ctrl+Shift+D`).
- Select **Debug (Attach) - Port 5005** from the dropdown at the top.
- Click the green **Play** button (or press `F5`).
- The application will resume booting.
4. **Set Breakpoints & Debug**: - Open any Java file (e.g., `SwiperSliderPlugin.java`). - Click in the left margin next to a line number to place a red breakpoint. - Trigger the code (e.g., by navigating to the page in your browser). Execution will pause at your breakpoint, allowing you to inspect variables!
//script test video fallback on homepage
const video = document.querySelector('video');
if (video) {
// Hide the video element and its controls
const ambientVideoContainer = video.closest('.f--ambient-video');
if (ambientVideoContainer) ambientVideoContainer.style.display = 'none';
const controls = document.querySelector('.video-controls');
if (controls) controls.style.display = 'none';
// Show the fallback block containing the seeded Swiper carousel
const fallback = document.getElementById('hero-video-fallback');
if (fallback) fallback.style.display = 'block';
console.log("Fallback triggered successfully.");
} else {
console.log("No video element found on this page.");
}
@@ -0,0 +1,205 @@
<style>
.education-slider-wrapper {
position: relative;
padding: 0px;
/* Space for buttons */
}
.education-slider-container {
overflow: hidden;
width: 100%;
}
.education-slider-wrapper .news-grid,
.education-slider-wrapper .row,
.education-slider-track {
display: flex !important;
flex-wrap: nowrap !important;
gap: 20px;
margin: 0 !important;
transition: transform 0.4s ease;
}
/* Force children to be fixed width cards */
.education-slider-wrapper .news-grid>*,
.education-slider-wrapper .row>*,
.education-slider-track>* {
flex: 0 0 calc(33.333% - 14px) !important;
/* show 3 cards at a time */
max-width: none !important;
}
@media (max-width: 992px) {
.education-slider-wrapper .news-grid>*,
.education-slider-wrapper .row>*,
.education-slider-track>* {
flex: 0 0 calc(50% - 10px) !important;
}
}
@media (max-width: 576px) {
.education-slider-wrapper .news-grid>*,
.education-slider-wrapper .row>*,
.education-slider-track>* {
flex: 0 0 100% !important;
}
}
.edu-slider-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
border-radius: 50%;
background-color: rgba(200, 200, 200, 0.5);
/* mờ / transparent */
color: #333;
border: none;
font-size: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
transition: background-color 0.2s, color 0.2s;
line-height: 1;
}
.edu-slider-btn:hover {
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
}
.edu-slider-btn.prev-btn {
left: -10px;
}
.edu-slider-btn.next-btn {
right: -10px;
}
.edu-slider-btn:disabled {
opacity: 0.2;
cursor: not-allowed;
}
/* --- Hiệu ứng crop (zoom) ảnh khi hover --- */
.education-slider-wrapper .featured-grid__item .news-card__image {
overflow: hidden !important;
display: block;
}
.education-slider-wrapper .featured-grid__item .news-card__image img {
transition: transform 0.4s ease-in-out !important;
}
.education-slider-wrapper .featured-grid__item:hover .news-card__image img {
transform: scale(1.1) !important;
}
</style>
<body>
<div class="cc--component-container cc--news-events">
<div class="c--component c--news-events">
<!-- Header -->
<div class="news-events-row">
<div class="education-column news-column" style="width: 100%;">
<div
style="display: flex; justify-content: space-between; align-items: baseline; border-bottom: 2px solid var(--color-brand); margin-bottom: 16px;">
<!-- Thêm flex-grow: 1 vào style của h2 -->
<h2 class="section-title--news"
style="font-size: 2rem; margin: 0; padding-bottom: 4px; border-bottom: unset;">ĐÀO TẠO</h2>
<a href="/news/education" class="news-events-section__cta news-events-section__cta--desktop"
style="color: var(--color-brand); font-weight: 800; font-size: 16px; text-transform: uppercase; text-decoration: none; border-bottom: unset;">XEM
THÊM</a>
</div>
<div class="education-slider-wrapper" style="padding-right: 0;">
<button class="edu-slider-btn prev-btn" aria-label="Previous">&lsaquo;</button>
<div class="education-slider-container">
<div class="education-slider-track">
[component:news-grid data-source="posts:tag=education,limit=5,collection=news_posts"]
</div>
</div>
<button class="edu-slider-btn next-btn" aria-label="Next">&rsaquo;</button>
</div>
</div>
</div><!-- Mobile Footer CTA -->
<div class="news-events-section__footer"><a href="/news/education"
class="news-events-section__cta news-events-section__cta--mobile">Xem Thêm</a></div>
</div>
</div>
</body>
<script>
document.addEventListener("DOMContentLoaded", function() {
const wrappers = document.querySelectorAll('.education-slider-wrapper');
wrappers.forEach(wrapper => {
const prevBtn = wrapper.querySelector('.prev-btn');
const nextBtn = wrapper.querySelector('.next-btn');
let track = wrapper.querySelector('.education-slider-track');
if (track && track.children.length === 1 && (track.children[0].classList.contains('news-grid') || track.children[0].classList.contains('row'))) {
track = track.children[0];
track.classList.add('education-slider-track');
}
if (!track) return;
let currentIndex = 0;
function updateSlider() {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
track.style.transform = `translateX(-${currentIndex * (itemWidth + gap)}px)`;
const containerWidth = wrapper.querySelector('.education-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
prevBtn.disabled = currentIndex === 0;
nextBtn.disabled = currentIndex >= items.length - visibleCount;
}
prevBtn.addEventListener('click', () => {
if (currentIndex > 0) {
currentIndex--;
updateSlider();
}
});
nextBtn.addEventListener('click', () => {
const items = track.children;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.education-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex < items.length - visibleCount) {
currentIndex++;
updateSlider();
}
});
window.addEventListener('resize', () => {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.education-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex > items.length - visibleCount) {
currentIndex = Math.max(0, items.length - visibleCount);
}
updateSlider();
});
setTimeout(updateSlider, 200);
});
});
</script>
@@ -0,0 +1,727 @@
<style>
.charity-hero {
--red: #d7193f;
--red-dark: #a90f2d;
--red-soft: #fff0f4;
--pink: #ffe3ec;
--ink: #36363a;
--muted: #74747a;
--paper: #f7f7f8;
--white: #ffffff;
--shadow: 0 24px 70px rgba(174, 23, 55, .18);
--shadow-strong: 0 32px 90px rgba(100, 10, 30, .28);
}
.charity-hero {
font-family: "Segoe UI", Arial, sans-serif;
color: var(--ink);
background: #f1f1f2;
position: relative;
min-height: 546px;
overflow: hidden;
isolation: isolate;
background:
radial-gradient(circle at 72% 22%, rgba(255,255,255,.92), transparent 28%),
linear-gradient(110deg, #ffeaf0 0%, #f4f4f5 42%, #f9f9fa 100%);
}
.charity-hero * {
box-sizing: border-box;
}
.charity-hero a {
text-decoration: none;
color: inherit;
}
.charity-hero .hero-bg {
position: absolute;
inset: 0;
z-index: -2;
overflow: hidden;
}
.charity-hero .blob {
position: absolute;
border-radius: 999px;
filter: blur(3px);
opacity: .72;
animation: blobFloat 13s ease-in-out infinite alternate;
}
.charity-hero .blob-1 {
width: 460px;
height: 460px;
background: rgba(255,207,221,.75);
left: -150px;
top: -170px;
}
.charity-hero .blob-2 {
width: 520px;
height: 520px;
background: rgba(255,236,241,.9);
right: -180px;
bottom: -220px;
animation-delay: -3s;
}
.charity-hero .blob-3 {
width: 220px;
height: 220px;
background: rgba(215,25,63,.12);
left: 48%;
top: 48%;
animation-delay: -6s;
}
.charity-hero .soft-grid {
position: absolute;
inset: 22px 150px;
border: 5px solid rgba(255,206,218,.68);
border-radius: 6px;
mask: linear-gradient(90deg,transparent,black 4%,black 96%,transparent);
-webkit-mask: linear-gradient(90deg,transparent,black 4%,black 96%,transparent);
opacity: .76;
}
.charity-hero .soft-grid:before,
.charity-hero .soft-grid:after {
content: "";
position: absolute;
background: var(--red);
border-radius: 50% 50% 48% 48%;
transform: rotate(45deg);
opacity: .9;
}
.charity-hero .soft-grid:before {
width: 36px;
height: 36px;
left: 22px;
bottom: -13px;
box-shadow: 34px -1px 0 var(--red);
}
.charity-hero .soft-grid:after {
width: 17px;
height: 17px;
right: 18px;
top: 285px;
opacity: .55;
box-shadow: 20px 19px 0 rgba(215,25,63,.9);
}
.charity-hero .hero-shell {
width: min(1580px, calc(100% - 44px));
min-height: 546px;
margin: 0 auto;
display: grid;
grid-template-columns: minmax(640px, 1.1fr) minmax(470px, .9fr);
gap: 42px;
align-items: center;
padding: 36px 0;
}
.charity-hero .gallery-panel {
position: relative;
min-height: 500px;
padding: 18px 12px 0;
height: 100%;
}
.charity-hero .gallery-title {
position: absolute;
left: 22px;
top: 10px;
z-index: 22;
display: inline-flex;
flex-direction: column;
gap: 4px;
padding: 10px 16px;
border-radius: 18px;
background: rgba(255,255,255,.72);
box-shadow: 0 12px 30px rgba(70,40,50,.08);
backdrop-filter: blur(10px);
transform: translateY(-12px);
opacity: 0;
animation: fadeDown .7s ease forwards .25s;
}
.charity-hero .gallery-title .eyebrow {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--red);
font-weight: 800;
}
.charity-hero .gallery-title strong {
font-size: 16px;
color: #313136;
}
.charity-hero .gallery-stage {
position: relative;
height: 100%;
width: 100%;
overflow: hidden;
perspective: 1200px;
}
.charity-hero .gallery-light {
position: absolute;
left: 34%;
top: 35%;
width: 310px;
height: 210px;
border-radius: 50%;
background: radial-gradient(circle, rgba(255,255,255,.88), rgba(255,255,255,0) 66%);
pointer-events: none;
opacity: .45;
mix-blend-mode: screen;
animation: lightPulse 4s ease-in-out infinite;
}
.charity-hero .focus-frame {
position: absolute;
left: 50%;
top: 50%;
width: 402px;
height: 238px;
transform: translate(-50%,-50%) scale(.86);
border-radius: 22px;
border: 1px solid rgba(215,25,63,.25);
box-shadow: 0 0 0 10px rgba(255,255,255,.16), inset 0 0 42px rgba(215,25,63,.07);
opacity: 0;
transition: .7s ease;
pointer-events: none;
z-index: 4;
}
.charity-hero .gallery-stage.is-playing .focus-frame {
opacity: 1;
transform: translate(-50%,-50%) scale(1);
}
.charity-hero .photo-card {
position: absolute;
left: var(--x);
top: var(--y);
width: var(--w);
background: #fff;
border: 4px solid var(--red);
border-radius: 3px;
overflow: hidden;
box-shadow: 0 14px 36px rgba(61,26,34,.22);
transform: translate3d(0,0,0) rotate(var(--r)) scale(.92);
opacity: 0;
z-index: 5;
will-change: left, top, width, transform, opacity, filter;
animation: cardIntro .85s cubic-bezier(.2,.9,.25,1.12) forwards;
animation-delay: var(--delay);
transition:
opacity 2.5s ease-in-out,
filter 2.5s ease-in-out,
box-shadow 1.5s cubic-bezier(.2,.8,.2,1),
border-color 1.5s cubic-bezier(.2,.8,.2,1),
left 1.5s cubic-bezier(.2,.8,.2,1),
top 1.5s cubic-bezier(.2,.8,.2,1),
width 1.5s cubic-bezier(.2,.8,.2,1),
transform 1.5s cubic-bezier(.2,.8,.2,1);
}
.charity-hero .photo-card img {
display: block;
width: 100%;
height: auto;
transform: scale(1.035);
transition: transform 1s cubic-bezier(.18,.82,.18,1), filter .8s ease;
}
.charity-hero .photo-card:before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(120deg, transparent 20%, rgba(255,255,255,.34) 45%, transparent 70%);
transform: translateX(-130%);
z-index: 2;
pointer-events: none;
}
.charity-hero .photo-card.is-active:before {
animation: shine 1.3s ease .25s;
}
.charity-hero .photo-card.is-active {
left: 50%;
top: 50%;
width: min(480px, 58vw);
transform: translate(-50%,-50%) rotate(0deg) scale(1.06) !important;
opacity: 1;
z-index: 30;
border-color: #fff;
border-width: 8px;
border-radius: 18px;
box-shadow: var(--shadow-strong);
filter: saturate(1.08) contrast(1.03);
}
.charity-hero .photo-card.is-active img {
transform: scale(1.08);
}
.charity-hero .photo-card.is-dim {
opacity: .28;
filter: grayscale(.08) saturate(.8) blur(.2px);
transform: translate3d(0,0,0) rotate(var(--r)) scale(.82);
z-index: 2;
}
.charity-hero .photo-card.is-next {
opacity: .74;
box-shadow: 0 17px 45px rgba(92,24,42,.18);
}
.charity-hero .photo-card.is-exit {
opacity: 0;
transform: translateY(28px) scale(.74) rotate(var(--r));
}
.charity-hero .gallery-controls {
position: absolute;
left: 50%;
bottom: 2px;
transform: translateX(-50%);
display: flex;
gap: 8px;
z-index: 40;
}
.charity-hero .dot {
width: 7px;
height: 7px;
border-radius: 999px;
background: rgba(215,25,63,.22);
transition: .35s ease;
cursor: pointer;
}
.charity-hero .dot.is-active {
width: 28px;
background: var(--red);
box-shadow: 0 0 0 6px rgba(215,25,63,.09);
}
.charity-hero .content-panel {
position: relative;
min-height: 430px;
display: flex;
flex-direction: column;
justify-content: center;
padding: 26px 24px 26px 8px;
}
.charity-hero .content-panel:before {
content: "";
position: absolute;
inset: 28px 0 18px 0;
border-radius: 34px;
background: linear-gradient(145deg, rgba(255,255,255,.54), rgba(255,255,255,.08));
border: 1px solid rgba(255,255,255,.55);
box-shadow: 0 20px 70px rgba(62,45,54,.06);
z-index: -1;
}
.charity-hero .brand-row {
display: grid;
grid-template-columns: 1fr 170px;
gap: 24px;
align-items: start;
margin-bottom: 54px;
}
.charity-hero .logo-mark {
width: 165px;
justify-self: end;
filter: drop-shadow(0 10px 16px rgba(215,25,63,.15));
animation: logoBreath 3.6s ease-in-out infinite;
}
.charity-hero .brand-text {
text-align: center;
align-self: center;
}
.charity-hero .brand-text h1 {
margin: 0 0 8px;
text-transform: uppercase;
line-height: 1.18;
font-size: clamp(20px,1.72vw,30px);
letter-spacing: -.3px;
color: var(--red);
font-weight: 900;
}
.charity-hero .brand-text p {
margin: 0;
color: #67676c;
text-transform: uppercase;
font-size: clamp(16px,1.2vw,22px);
font-weight: 800;
letter-spacing: .3px;
}
.charity-hero .slogan-card {
width: min(560px,100%);
margin: 0 auto;
position: relative;
text-align: center;
padding: 20px 18px 28px;
}
.charity-hero .slogan-card:before {
content: "";
position: absolute;
left: 50%;
top: 50%;
width: 86%;
height: 86%;
transform: translate(-50%,-50%);
border-radius: 38px;
background: linear-gradient(90deg,rgba(255,255,255,.18),rgba(255,237,243,.72),rgba(255,255,255,.12));
filter: blur(.2px);
z-index: -1;
opacity: .8;
}
.charity-hero .quote-mark {
position: absolute;
left: 12px;
top: -16px;
font-size: 74px;
line-height: 1;
color: rgba(215,25,63,.18);
font-family: Georgia,serif;
}
.charity-hero .slogan-card h2 {
margin: 0;
color: #e13b5e;
font-size: clamp(29px,2.2vw,42px);
line-height: 1.42;
font-family: "Segoe Script","Brush Script MT",cursive;
font-weight: 600;
text-shadow: 0 8px 20px rgba(215,25,63,.12);
}
.charity-hero .slogan-card h2 em {
font-style: normal;
font-size: .86em;
color: #d9284c;
}
.charity-hero .heart-line {
display: block;
width: 160px;
height: 22px;
margin: 12px auto 0;
position: relative;
}
.charity-hero .heart-line:before {
content: "";
position: absolute;
left: 0;
right: 34px;
top: 11px;
height: 2px;
background: linear-gradient(90deg,transparent,var(--red));
border-radius: 999px;
}
.charity-hero .heart-line:after {
content: "♡";
position: absolute;
right: 6px;
top: -8px;
color: var(--red);
font-size: 32px;
transform: rotate(18deg);
animation: heartBeat 1.7s ease-in-out infinite;
}
.charity-hero .action-row {
display: flex;
gap: 12px;
justify-content: center;
margin-top: 26px;
flex-wrap: wrap;
}
.charity-hero .btn {
display: inline-flex;
align-items: center;
justify-content: center;
height: 44px;
padding: 0 20px;
border-radius: 999px;
font-weight: 800;
font-size: 14px;
letter-spacing: .2px;
transition: .3s ease;
box-shadow: 0 12px 30px rgba(115,21,43,.12);
}
.charity-hero .btn.primary {
background: linear-gradient(135deg,var(--red),#ef4769);
color: white;
}
.charity-hero .btn.secondary {
background: rgba(255,255,255,.74);
color: var(--red);
border: 1px solid rgba(215,25,63,.18);
}
.charity-hero .reveal-up {
opacity: 0;
transform: translateY(24px);
animation: revealUp .8s ease forwards;
}
.charity-hero .brand-text.reveal-up {
animation-delay: .2s;
}
.charity-hero .logo-mark.reveal-up {
animation-delay: .38s;
}
.charity-hero .slogan-card.reveal-up {
animation-delay: .62s;
}
.charity-hero .action-row.reveal-up {
animation-delay: .86s;
}
@keyframes cardIntro {
from { opacity: 0; transform: translate3d(-28px,22px,0) rotate(calc(var(--r) - 7deg)) scale(.78); }
to { opacity: 1; transform: translate3d(0,0,0) rotate(var(--r)) scale(.92); }
}
/* Removed microFloat because animating margin-top constantly recalculates layout causing major stutter */
@keyframes shine {
to { transform: translateX(130%); }
}
@keyframes revealUp {
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeDown {
to { opacity: 1; transform: translateY(0); }
}
@keyframes logoBreath {
0%,100% { transform: translateY(0) scale(1); }
50% { transform: translateY(-6px) scale(1.025); }
}
@keyframes heartBeat {
0%,100% { transform: rotate(18deg) scale(1); }
20% { transform: rotate(18deg) scale(1.18); }
38% { transform: rotate(18deg) scale(1); }
}
@keyframes blobFloat {
to { transform: translate(26px,20px) scale(1.05); }
}
@keyframes lightPulse {
0%,100% { opacity: .25; transform: scale(.96); }
50% { opacity: .58; transform: scale(1.08); }
}
@media (max-width:1200px) {
.charity-hero .hero-shell {
grid-template-columns: 1fr;
gap: 10px;
padding: 18px 0 34px;
}
.charity-hero { min-height: auto; }
.charity-hero .content-panel { min-height: auto; }
.charity-hero .gallery-panel { min-height: 470px; }
.charity-hero .gallery-stage { height: 430px; }
.charity-hero .brand-row { grid-template-columns: 1fr 130px; margin-bottom: 20px; }
.charity-hero .logo-mark { width: 130px; }
}
@media (max-width:720px) {
.charity-hero .hero-shell { width: calc(100% - 24px); }
.charity-hero .soft-grid { inset: 12px; border-width: 3px; }
.charity-hero .gallery-panel { padding: 12px 0; min-height: 390px; }
.charity-hero .gallery-title { display: none; }
.charity-hero .gallery-stage { height: 370px; overflow: hidden; }
.charity-hero .photo-card { width: calc(var(--w) * .72); }
.charity-hero .photo-card.is-active { width: min(86vw,380px); }
.charity-hero .content-panel { padding: 8px; }
.charity-hero .brand-row { display: flex; flex-direction: column-reverse; align-items: center; gap: 10px; }
.charity-hero .brand-text h1 { text-align: center; }
.charity-hero .slogan-card h2 { font-size: 29px; }
.charity-hero .btn { height: 42px; padding: 0 16px; }
}
@media (prefers-reduced-motion:reduce) {
.charity-hero *,
.charity-hero *::before,
.charity-hero *::after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
transition-duration: .01ms !important;
}
}
</style>
<div class="charity-hero" aria-label="Quỹ từ thiện bệnh nhân nghèo bị đột quỵ">
<div class="hero-bg">
<span class="blob blob-1"></span>
<span class="blob blob-2"></span>
<span class="blob blob-3"></span>
<span class="soft-grid"></span>
</div>
<section class="hero-shell">
<div class="gallery-panel" aria-label="Hình ảnh hoạt động quỹ từ thiện">
<div class="gallery-title">
<span class="eyebrow">S.I.S Charity Foundation</span>
<strong>Những khoảnh khắc sẻ chia</strong>
</div>
<div class="gallery-stage" id="galleryStage">
<article class="photo-card p1" data-index="0" style="--x: -4%; --y: 2%; --r: -2.5deg; --w: 205px; --delay: 0.05s;">
<img src="/upload/charity_v4/photo-01.jpg" alt="Trao hỗ trợ bệnh nhân 1" />
</article>
<article class="photo-card p2" data-index="1" style="--x: 18%; --y: -2%; --r: 1.6deg; --w: 298px; --delay: 0.16s;">
<img src="/upload/charity_v4/photo-02.jpg" alt="Trao hỗ trợ bệnh nhân 2" />
</article>
<article class="photo-card p3" data-index="2" style="--x: 63%; --y: 0%; --r: -1.2deg; --w: 410px; --delay: 0.27s;">
<img src="/upload/charity_v4/photo-05.jpg" alt="Trao hỗ trợ bệnh nhân 3" />
</article>
<article class="photo-card p4" data-index="3" style="--x: 0%; --y: 27%; --r: 2.2deg; --w: 245px; --delay: 0.38s">
<img src="/upload/charity_v4/photo-03.jpg" alt="Trao hỗ trợ bệnh nhân 4" />
</article>
<article class="photo-card p5" data-index="4" style="--x: 32%; --y: 32%; --r: -1.8deg; --w: 260px; --delay: 0.49s;">
<img src="/upload/charity_v4/photo-04.jpg" alt="Trao hỗ trợ bệnh nhân 5" />
</article>
<article class="photo-card p6" data-index="5" style="--x: 57%; --y: 35%; --r: 1deg; --w: 195px; --delay: 0.6s">
<img src="/upload/charity_v4/photo-06.jpg" alt="Trao hỗ trợ bệnh nhân 6" />
</article>
<article class="photo-card p7" data-index="6" style="--x: 4%; --y: 62%; --r: -1.4deg; --w: 270px; --delay: 0.71s;">
<img src="/upload/charity_v4/photo-07.jpg" alt="Trao hỗ trợ bệnh nhân 7" />
</article>
<article class="photo-card p8" data-index="7" style="--x: 42%; --y: 73%; --r: 1.2deg; --w: 255px; --delay: 0.82s;">
<img src="/upload/charity_v4/photo-08.jpg" alt="Trao hỗ trợ bệnh nhân 8" />
</article>
<article class="photo-card p9" data-index="8" style="--x: 77%; --y: 66%; --r: -1deg; --w: 338px; --delay: 0.93s">
<img src="/upload/charity_v4/photo-09.jpg" alt="Trao hỗ trợ bệnh nhân 9" />
</article>
<div class="gallery-light" aria-hidden="true"></div>
</div>
<div class="gallery-controls" aria-hidden="true">
<span class="dot is-active"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</div>
</div>
<div class="content-panel">
<div class="brand-row">
<div class="brand-text reveal-up">
<h1>Quỹ từ thiện bệnh nhân nghèo bị đột quỵ<br />khu vực Đồng bằng sông Cửu Long</h1>
<p>S.I.S Charity Foundation</p>
</div>
<img class="logo-mark reveal-up" src="/upload/charity_v4/logo.png" alt="Stroke International Services Charity Foundation" />
</div>
<div class="slogan-card reveal-up">
<span class="quote-mark"></span>
<h2>Sẻ chia yêu thương<br /><em>đến bệnh nhân nghèo bị đột quỵ</em></h2>
<span class="heart-line" aria-hidden="true"></span>
</div>
<div class="action-row reveal-up">
<a class="btn primary" href="#">Lan tỏa yêu thương</a>
<a class="btn secondary" href="#">Xem hoạt động quỹ</a>
</div>
</div>
</section>
</div>
<script>
(function(){
const container = document.querySelector('.charity-hero');
if(!container) return;
const stage = container.querySelector('.gallery-stage');
const cards = Array.from(container.querySelectorAll('.photo-card'));
const dots = Array.from(container.querySelectorAll('.dot'));
if(!stage || cards.length === 0) return;
let active = 0;
let timer = null;
const duration = 4500; /* Tăng thời gian chờ (autoplay) từ 2.8 giây lên 4.5 giây để nhìn rõ ảnh hơn */
function setActive(index){
active = index % cards.length;
stage.classList.add('is-playing');
cards.forEach((card, i) => {
card.classList.remove('is-active','is-dim','is-next','is-exit');
const diff = (i - active + cards.length) % cards.length;
if(i === active){
card.classList.add('is-active');
}else if(diff === 1 || diff === 2){
card.classList.add('is-next');
}else if(diff === cards.length - 1){
card.classList.add('is-exit');
}else{
card.classList.add('is-dim');
}
});
dots.forEach((dot, i) => dot.classList.toggle('is-active', i === active));
}
function next(){
setActive(active + 1);
}
function start(){
stop();
timer = setInterval(next, duration);
}
function stop(){
if(timer) clearInterval(timer);
}
// Hệ thống sẽ chạy tự động 100%
dots.forEach((dot, i) => {
dot.addEventListener('click', () => {
setActive(i);
start();
});
});
// Start immediately or on load if part of a bigger page
setTimeout(() => {
setActive(0);
start();
}, 900);
})();
</script>
@@ -0,0 +1,29 @@
<div data-component-id="umass_base:block-multiple-ctas" data-background="solid" data-layout-variant="default">
<div class="container" th:unless="${ctaMenu != null and not #lists.isEmpty(ctaMenu.items)}">
<div class="text-container">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>TÌM HIỂU<span class="highlight">KHOA ĐIỀU TRỊ</span></h2>
</div>
</div>
<div class="links-container">
[menu:CTA]
</div>
</div>
</div>
<style>
.links-container {
display: grid !important;
grid-template-columns: repeat(2, 1fr) !important;
gap: 1rem;
column-gap: 2rem !important;
padding: 0;
margin: 0;
}
@media (max-width: 600px) {
.links-container {
grid-template-columns: 1fr !important;
}
}
</style>
@@ -0,0 +1,43 @@
<body>
<div class="cc--component-container cc--stats-block" style="background-color: var(--color-brand)">
<div class="c--component c--stats-block">
<div class="f--field f--stats-block">
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div id="iuox8" class="f--field f--stat-number">50.000+</div>
<div id="iynqf" class="f--field f--description">
<p>Tổng số ca cấp cứu</p>
</div>
</div>
</div>
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div id="ievo1" class="f--field f--stat-number">1.6tr+</div>
<div id="iklnr" class="f--field f--description">
<p>Tổng số ca khám bệnh</p>
</div>
</div>
</div>
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div id="iz72l" class="f--field f--stat-number">30.000+</div>
<div id="i0kg8" class="f--field f--description">
<p>Số ca điều trị đột quỵ</p>
</div>
</div>
</div>
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div id="iz72l" class="f--field f--stat-number">#1</div>
<div id="i0kg8" class="f--field f--description">
<p>Về điều trị đột quỵ</p>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<style>
* { box-sizing: border-box; } body {margin: 0;}*{box-sizing:border-box;}body{margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;}#iuox8{color:white;}#iynqf{color:white;}#ievo1{color:white;}#iklnr{color:white;}#iz72l{color:white;}#i0kg8{color:white;}#iynqf *{color:white;}#iklnr *{color:white;}#i0kg8 *{color:white;}
</style>
@@ -0,0 +1,172 @@
<style>
.stroke-book-banner {
background-color: #f8f9fa;
padding: 36px 20px;
font-family: var(--font-family-base, 'Inter', 'Segoe UI', sans-serif);
display: flex;
justify-content: center;
}
.sbb-container {
display: flex;
flex-direction: row;
align-items: center;
max-width: var(--max-width);
width: 100%;
gap: 50px;
/* Faint brain background pattern placeholder */
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 24 24" fill="none" stroke="%23ffe6e6" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/></svg>');
background-position: right center;
background-repeat: no-repeat;
background-size: 350px;
}
.sbb-left {
flex: 1.2;
display: flex;
justify-content: flex-end;
}
.sbb-left img {
max-width: 100%;
height: auto;
object-fit: contain;
filter: drop-shadow(0 15px 25px rgba(0, 0, 0, 0.15));
}
.sbb-right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
padding-right: 20px;
}
.sbb-subtitle {
font-size: 1.4rem;
font-weight: 700;
color: #222;
margin-bottom: 5px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.sbb-title {
font-size: 3.2rem;
font-weight: 800;
color: #c0272d;
margin: 0 0 15px 0;
text-transform: uppercase;
line-height: 1.1;
letter-spacing: -0.5px;
}
.sbb-tagline {
font-size: 1.25rem;
font-weight: 700;
color: #333;
margin-bottom: 30px;
}
.sbb-features {
list-style: none;
padding: 0;
margin: 0 0 35px 0;
}
.sbb-features li {
font-size: 1.1rem;
color: #444;
margin-bottom: 14px;
display: flex;
align-items: center;
font-weight: 500;
}
.sbb-features li::before {
content: '✓';
display: inline-flex;
justify-content: center;
align-items: center;
width: 24px;
height: 24px;
border-radius: 50%;
border: 1.5px solid #c0272d;
color: #c0272d;
font-size: 14px;
font-weight: bold;
margin-right: 12px;
flex-shrink: 0;
}
.sbb-btn {
display: inline-block;
background: linear-gradient(180deg, #d81b32 0%, #a11122 100%);
color: #fff;
font-weight: 700;
font-size: 1.15rem;
padding: 16px 40px;
border-radius: 6px;
text-decoration: none;
text-transform: uppercase;
box-shadow: 0 4px 10px rgba(161, 17, 34, 0.4);
transition: all 0.3s ease;
letter-spacing: 0.5px;
border: 1px solid #8e0b1b;
}
.sbb-btn:hover {
background: linear-gradient(180deg, #ee1f38 0%, #b81427 100%);
box-shadow: 0 6px 15px rgba(161, 17, 34, 0.5);
transform: translateY(-2px);
color: #fff !important;
}
@media (max-width: 992px) {
.sbb-container {
flex-direction: column;
text-align: center;
gap: 30px;
background-image: none;
}
.sbb-left {
justify-content: center;
}
.sbb-right {
align-items: center;
padding-right: 0;
}
.sbb-title {
font-size: 2.5rem;
}
.sbb-features li {
text-align: left;
justify-content: flex-start;
}
}
</style>
<div class="stroke-book-banner">
<div class="sbb-container">
<div class="sbb-left">
<img src="/upload/sach-dot-quy-ngang.png" alt="Cẩm nang sức khỏe phòng chống đột quỵ">
</div>
<div class="sbb-right">
<div class="sbb-subtitle">CẨM NANG SỨC KHỎE</div>
<h2 class="sbb-title">PHÒNG CHỐNG ĐỘT QUỴ</h2>
<div class="sbb-tagline">Hiểu đúng Phòng sớm Sống khỏe mỗi ngày</div>
<ul class="sbb-features">
<li>Kiến thức khoa học, dễ hiểu</li>
<li>Nhận biết sớm dấu hiệu Xử trí kịp thời</li>
<li>Phòng ngừa hiệu quả Giảm thiểu nguy cơ</li>
<li>Dành cho mọi lứa tuổi</li>
</ul>
<a href="#" class="sbb-btn">TÌM HIỂU NGAY</a>
</div>
</div>
</div>
@@ -0,0 +1,218 @@
<div class="cc--component-container cc--news-events">
<div class="c--component c--news-events">
<!-- Phần 2 cột: Tin mới (Trái) và Sự kiện (Phải) -->
<div class="news-events-row">
<div class="news-column">
<h2 class="section-title--news" style="font-size: 2rem;">
TIN TỨC
</h2>
<div class="news-slider-wrapper">
<button class="news-slider-btn prev-btn" aria-label="Previous">&lsaquo;</button>
<div class="news-slider-container">
<div class="news-slider-track">
<!-- Shortcode Tin Tức hiển thị dạng list (limit tăng lên 6 để có thể trượt) -->
[component:news-grid data-source="posts:tag=news,limit=6,collection=news_posts"]
</div>
</div>
<button class="news-slider-btn next-btn" aria-label="Next">&rsaquo;</button>
</div>
</div>
<div class="events-column">
<h2 class="events-title" style="font-size: 2rem;">
SỰ KIỆN
</h2>
<div class="events-widget">
<!-- Shortcode Sự kiện -->
[component:events-list data-source="posts:tag=event,limit=4,collection=event_posts"]
</div>
</div>
</div>
<!-- Header chung (Mobile) -->
<div class="news-events-section__footer">
<a href="/news/news-events" class="news-events-section__cta news-events-section__cta--mobile">
Xem Thêm
</a>
</div>
</div>
</div>
<style>
.news-slider-wrapper {
position: relative;
padding: 0px;
}
.news-slider-container {
overflow: hidden;
width: 100%;
}
.news-slider-wrapper .news-grid,
.news-slider-wrapper .row,
.news-slider-track {
display: flex !important;
flex-wrap: nowrap !important;
gap: 20px;
margin: 0 !important;
transition: transform 0.4s ease;
}
/* Force children to be fixed width cards - 2 cards at a time because this column is narrow */
.news-slider-wrapper .news-grid>*,
.news-slider-wrapper .row>*,
.news-slider-track>* {
flex: 0 0 calc(50% - 10px) !important;
max-width: none !important;
}
@media (max-width: 992px) {
.news-slider-wrapper .news-grid>*,
.news-slider-wrapper .row>*,
.news-slider-track>* {
flex: 0 0 calc(50% - 10px) !important;
}
}
@media (max-width: 576px) {
.news-slider-wrapper .news-grid>*,
.news-slider-wrapper .row>*,
.news-slider-track>* {
flex: 0 0 100% !important;
}
}
.news-slider-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
border-radius: 50%;
background-color: rgba(200, 200, 200, 0.5);
color: #333;
border: none;
font-size: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
transition: background-color 0.2s, color 0.2s;
line-height: 1;
}
.news-slider-btn:hover {
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
}
.news-slider-btn.prev-btn {
left: -15px;
}
.news-slider-btn.next-btn {
right: -15px;
}
.news-slider-btn:disabled {
opacity: 0.2;
cursor: not-allowed;
}
/* --- Hiệu ứng crop (zoom) ảnh khi hover --- */
/* 1. Thiết lập vùng chứa ảnh sẽ cắt bỏ phần bị lồi ra (overflow: hidden) */
.news-slider-wrapper .featured-grid__item .news-card__image {
overflow: hidden !important;
display: block;
}
/* 2. Thiết lập thời gian chuyển động cho ảnh */
.news-slider-wrapper .featured-grid__item .news-card__image img {
transition: transform 0.4s ease-in-out !important;
}
/* 3. Phóng to ảnh (scale) khi trỏ chuột vào vùng chứa */
.news-slider-wrapper .featured-grid__item:hover .news-card__image img {
transform: scale(1.1) !important;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const wrappers = document.querySelectorAll('.news-slider-wrapper');
wrappers.forEach(wrapper => {
const prevBtn = wrapper.querySelector('.prev-btn');
const nextBtn = wrapper.querySelector('.next-btn');
let track = wrapper.querySelector('.news-slider-track');
if (track && track.children.length === 1 && (track.children[0].classList.contains('news-grid') || track.children[0].classList.contains('row'))) {
track = track.children[0];
track.classList.add('news-slider-track');
}
if (!track) return;
let currentIndex = 0;
function updateSlider() {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
track.style.transform = `translateX(-${currentIndex * (itemWidth + gap)}px)`;
const containerWidth = wrapper.querySelector('.news-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
prevBtn.disabled = currentIndex === 0;
nextBtn.disabled = currentIndex >= items.length - visibleCount;
}
prevBtn.addEventListener('click', () => {
if (currentIndex > 0) {
currentIndex--;
updateSlider();
}
});
nextBtn.addEventListener('click', () => {
const items = track.children;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.news-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex < items.length - visibleCount) {
currentIndex++;
updateSlider();
}
});
window.addEventListener('resize', () => {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.news-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex > items.length - visibleCount) {
currentIndex = Math.max(0, items.length - visibleCount);
}
updateSlider();
});
setTimeout(updateSlider, 200);
});
});
</script>
@@ -0,0 +1,138 @@
<div class="content-top">
<div class="lc--layout-container lc--full">
<div class="l--layout l--full">
<div class="lr--layout-region lr--main">
<div class="cc--component-container cc--homepage-hero">
<div class="c--component c--homepage-hero">
<div class="slides-container">
<div class="image-video-container has-video">
<img
src="https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg"
alt="Students collaborate using a driving simulator in the UMass Center for Transportation."
srcset="
https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg?h=9855f42d&amp;itok=d27r8GaT 1920w
"
/>
<div class="f--ambient-video" id="hero-ambient-video">
<video role="presentation" tabindex="-1" loop="" autoplay="" playsinline="" muted=""
id="hero-video" onerror="handleVideoError()">
<source
src="http://localhost:8080/uploads/2026/07/9375f45b-027a-4fb4-bd18-66889309280d.mp4"
type="video/mp4" onerror="handleVideoError()" />
</video>
</div>
<div id="hero-video-fallback"
style="display: none; position: absolute; top:0; left:0; width:100%; height:100%; z-index: 2;">
<th:block
th:utext="${@hookManager.applyFilters('the_content', '[plugin:swiper-slider slug=''video-fallback'']')}">
</th:block>
</div>
<script>
function handleVideoError() {
var vidContainer = document.getElementById('hero-ambient-video');
if(vidContainer) vidContainer.style.display = 'none';
var controls = document.querySelector('.video-controls');
if(controls) controls.style.display = 'none';
var fallback = document.getElementById('hero-video-fallback');
if(fallback) fallback.style.display = 'block';
}
window.addEventListener('DOMContentLoaded', function() {
var v = document.getElementById('hero-video');
if (v && v.error) {
handleVideoError();
}
});
</script>
<div class="video-controls" data-once="ambientVideo">
<div class="video-controls-inner">
<button aria-labelledby="pauseBtn" class="video-button video-pause-button">
<svg height="14" viewBox="0 0 10 14" width="10" xmlns="http://www.w3.org/2000/svg">
<title id="pauseBtn">Pause Background Video</title>
<path
d="m1143 711v14h-3v-14zm7 0v14h-3v-14z"
fill="#fff"
fill-rule="evenodd"
transform="translate(-1140 -711)"
></path>
</svg>
</button>
<button aria-labelledby="playBtn" class="video-button video-play-button">
<svg height="29" viewBox="0 0 29 29" width="29" xmlns="http://www.w3.org/2000/svg">
<title id="playBtn">Play Background Video</title>
<path
d="m17.5 3c-7.99789474 0-14.5 6.50210526-14.5 14.5 0 7.9978947 6.50210526 14.5 14.5 14.5 7.9978947 0 14.5-6.5021053 14.5-14.5 0-7.99789474-6.5021053-14.5-14.5-14.5zm5.6763936 15.2939067-7.3503366 4.5939604c-.6635721.408352-1.5313202-.051044-1.5313202-.8422261v-9.1879207c0-.7911821.8677481-1.2761001 1.5313202-.8422261l7.3503366 4.5939604c.612528.38283.612528 1.3016221 0 1.6844521z"
fill="#fff"
fill-rule="evenodd"
transform="translate(-3 -3)"
></path>
</svg>
</button>
</div>
</div>
</div>
<div class="text-container centered">
<div class="slide-text-container-inner">
<h3>
<a href="https://www.umass.edu/gateway/why-umass">Stroke International School
2026.</a>
</h3>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/why-umass"
class="button-secondary button-context-dark"
aria-label="Learn More Learn more about UMass Amherst."
data-component-id="umass_base:button">
<span class="button-text"> Learn More </span>
</a>
</div>
<nav class="mc--menu mc--info-menu"
th:if="${infoMenu != null and not #lists.isEmpty(infoMenu.items)}">
<ul class="menu m--menu m--info-menu">
<li class="menu-item menu-item--expanded">
<details class="utility-button-wrapper mc--info-menu-container">
<summary type="button"
aria-label="Display submenu for Information menu"
aria-expanded="false" aria-haspopup="true"
class="utility-button arrow-toggle information-for-toggle">
<span th:text="${infoMenu.name}">Info For</span>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 7" enable-background="new 0 0 16 7"
xml:space="preserve" width="16" height="7" class="arrow">
<polygon fill-rule="evenodd" clip-rule="evenodd"
fill="#ffffff" points="7,7 0,0 16,0 "></polygon>
</svg>
</summary>
<div class="submenu-wrapper">
<ul class="submenu">
<li class="menu-item" th:each="item : ${infoMenu.items}">
<a th:href="${item.url}"
th:text="${item.label}">Prospective Students</a>
</li>
</ul>
</div>
</details>
<div class="submenu-wrapper-desktop">
<ul class="submenu">
<li class="menu-item" th:each="item : ${infoMenu.items}">
<a th:href="${item.url}" th:text="${item.label}">Prospective
Students</a>
</li>
</ul>
</div>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1 @@
[plugin:tabbed-livestream]
+25
View File
@@ -0,0 +1,25 @@
import re
import sys
def main():
try:
with open('src/main/resources/templates/index.html', 'r', encoding='utf-8') as f:
content = f.read()
# Find the start of content-top
content_top_start = content.find('<div class="content-top">')
if content_top_start == -1:
print("Could not find content-top")
sys.exit(1)
# Find the end of content-top (heuristic: before <div class="lc--layout-container"> for the next block)
# Actually, let's just find the next block, which starts with <div class="lc--layout-container">
# In index.html, content-top is lines 128 to 337
next_block_start = content.find('<div class="lc--layout-container"', content_top_start + 100)
# Actually, it's safer to extract from .temp/snippets/ when they are dumped.
except Exception as e:
print(e)
if __name__ == '__main__':
main()
File diff suppressed because one or more lines are too long
+39
View File
@@ -0,0 +1,39 @@
import sys
import os
try:
with open('src/main/resources/templates/index.html', 'r', encoding='utf-8') as f:
lines = f.readlines()
main_content_lines = lines[277:2663]
html = """<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{themes/__${activeTheme}__/layout(bodyClass='umass-platform-homepage path-frontpage page-node-type-homepage homepage transparent-header')}">
<head>
<title>Bệnh Viện Đa Khoa Quốc Tế S.I.S Cần Thơ</title>
</head>
<body>
<!-- Content Top Area -->
<div layout:fragment="content-top">
<th:block th:replace="~{themes/__${activeTheme}__/content-top}"></th:block>
</div>
<!-- Main Content Area -->
<div layout:fragment="content">
"""
html += "".join(main_content_lines)
html += """
</div>
</body>
</html>
"""
with open('src/main/resources/templates/pages/home.html', 'w', encoding='utf-8') as f:
f.write(html)
print("Successfully created pages/home.html")
except Exception as e:
print(e)
File diff suppressed because it is too large Load Diff
+43
View File
@@ -0,0 +1,43 @@
import re
import sys
def main():
try:
with open('src/main/resources/templates/index.html', 'r', encoding='utf-8') as f:
lines = f.readlines()
content_top_start = -1
for i, line in enumerate(lines):
if '<div class="content-top">' in line:
content_top_start = i
break
# The next block starts with '<div class="lc--layout-container lc--full">'
# But content-top itself has one.
# Let's search for the second '<div class="lc--layout-container lc--full">'
next_block_start = -1
count = 0
for i in range(content_top_start, len(lines)):
if '<div class="lc--layout-container lc--full">' in lines[i]:
count += 1
if count == 2:
next_block_start = i
break
end_of_file_content = -1
for i in range(len(lines)-1, -1, -1):
if '<footer th:replace' in lines[i]:
# Go up to the closing tags of t--template
end_of_file_content = i - 5
break
print(f"Content top start: {content_top_start}")
print(f"Next block start: {next_block_start}")
print(f"End of content: {end_of_file_content}")
except Exception as e:
print(e)
if __name__ == '__main__':
main()
+57
View File
@@ -0,0 +1,57 @@
import oracledb
import os
import sys
def seed_template():
print("Reading temp file...")
with open('/home/x79/sisvietnamvn_01/sisvietnamvn_main/html_snippets/content_main_blocks/temp', 'r', encoding='utf-8') as f:
content = f.read()
print("Connecting to Oracle...")
try:
# Connect to Oracle database
connection = oracledb.connect(
user="sisvietnam",
password="sisvietnam",
dsn="localhost:1521/sisvietnam"
)
cursor = connection.cursor()
# Check if it already exists
cursor.execute("SELECT id FROM sis_component_template WHERE slug = :slug", slug='video-fallback')
row = cursor.fetchone()
if row:
print(f"Template video-fallback already exists with ID {row[0]}. Updating content...")
cursor.execute("""
UPDATE sis_component_template
SET html_template = :content
WHERE id = :id
""", content=content, id=row[0])
else:
print("Inserting new video-fallback template...")
# We need to insert a new row. The ID might be auto-generated by sequence.
# Assuming id is generated by sequence or identity column.
# In Hibernate with Oracle, it usually uses a sequence sis_component_template_seq or SEQ_SIS_COMPONENT_TEMPLATE
# Let's try to insert without ID first (if it's identity column)
try:
cursor.execute("""
INSERT INTO sis_component_template (id, slug, name, description, active, html_template, created_by, created_date)
VALUES ((SELECT NVL(MAX(id), 0) + 1 FROM sis_component_template), :slug, :name, :description, :active, :content, 'system', CURRENT_TIMESTAMP)
""", slug='video-fallback', name='Video Fallback Banner', description='Fallback for homepage hero video', active=1, content=content)
except Exception as e:
print(f"Insert with MAX(id) failed: {e}")
connection.commit()
print("Successfully seeded video-fallback template!")
cursor.close()
connection.close()
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == '__main__':
seed_template()
@@ -45,13 +45,15 @@ public class PageController {
private final com.sisvietnamvn.web.service.SettingService settingService;
private final com.sisvietnamvn.web.repository.DoctorScheduleRepository doctorScheduleRepository;
private final PostRepository postRepository;
private final com.sisvietnamvn.web.service.ComponentTemplateService componentTemplateService;
public PageController(PageService pageService, ObjectMapper objectMapper, HookManager hookManager, com.sisvietnamvn.web.service.HtmlSnippetService snippetService,
com.sisvietnamvn.web.service.DoctorService doctorService, com.sisvietnamvn.web.service.SpecialtyService specialtyService,
com.sisvietnamvn.web.service.DoctorApiSyncService doctorApiSyncService,
com.sisvietnamvn.web.service.SettingService settingService,
com.sisvietnamvn.web.repository.DoctorScheduleRepository doctorScheduleRepository,
PostRepository postRepository) {
PostRepository postRepository,
com.sisvietnamvn.web.service.ComponentTemplateService componentTemplateService) {
this.pageService = pageService;
this.objectMapper = objectMapper;
this.hookManager = hookManager;
@@ -62,6 +64,49 @@ public class PageController {
this.settingService = settingService;
this.doctorScheduleRepository = doctorScheduleRepository;
this.postRepository = postRepository;
this.componentTemplateService = componentTemplateService;
}
@GetMapping("/page/dump-snippets")
@org.springframework.web.bind.annotation.ResponseBody
public String dumpSnippets() {
try {
java.io.File dir = new java.io.File("src/main/resources/templates/themes/umass/snippets");
if (!dir.exists()) dir.mkdirs();
for (com.sisvietnamvn.web.domain.HtmlSnippet snippet : this.snippetService.findAll()) {
if (snippet.getSlug() == null || snippet.getSlug().isEmpty()) continue;
String content = snippet.getContent() != null ? snippet.getContent() : "";
content = content.replace("[menu:CTA]", "<th:block th:utext=\"${@componentTemplateService.render('cta-buttons', 'menu:CTA')}\"></th:block>");
content = content.replace("[menu:INFO:dropdown]", "<th:block th:utext=\"${@componentTemplateService.render('info-dropdown', 'menu:INFO')}\"></th:block>");
java.nio.file.Files.writeString(java.nio.file.Paths.get(dir.getAbsolutePath(), snippet.getSlug() + ".html"), content);
}
return "Snippets dumped successfully to " + dir.getAbsolutePath();
} catch (Exception e) {
return "Error: " + e.getMessage();
}
}
@GetMapping("/page/fix-video-fallback")
@org.springframework.web.bind.annotation.ResponseBody
public String fixVideoFallback() {
try {
java.util.Optional<com.sisvietnamvn.web.domain.ComponentTemplate> opt = this.componentTemplateService.findBySlug("video-fallback");
if (opt.isPresent()) {
com.sisvietnamvn.web.domain.ComponentTemplate ct = opt.get();
String html = ct.getHtmlTemplate();
if (html != null && html.contains("src=\"{{imageUrl}}\"")) {
html = html.replace("src=\"{{imageUrl}}\"", "data-src=\"{{imageUrl}}\"");
ct.setHtmlTemplate(html);
this.componentTemplateService.save(ct);
return "Fixed video-fallback src attribute in database.";
}
return "video-fallback template did not contain src=\"{{imageUrl}}\" or was already fixed.";
}
return "video-fallback template not found in database.";
} catch (Exception e) {
return "Error: " + e.getMessage();
}
}
@GetMapping("/page/{slug}")
@@ -79,7 +124,11 @@ public class PageController {
@GetMapping("/")
public String getHome(Model model) {
return renderPage(pageService.findByPageType(com.sisvietnamvn.web.domain.PageType.HOME), model);
model.addAttribute("activeTheme", "umass");
model.addAttribute("componentTemplateService", componentTemplateService);
model.addAttribute("hookManager", hookManager);
model.addAttribute("videoFallbackHtml", componentTemplateService.render("video-fallback", null));
return "pages/home";
}
@@ -138,80 +138,9 @@
<div class="f--field f--image"></div>
<img src="/images/mainpage_slider/BANNER-LINNC-BAC-CUONG-05-scaled.jpg"
alt="Graduating students celebrate at McGuirk Stadium prior to 2025 Undergraduate Commencement">
<style>
.my-ambient-slider {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.my-ambient-slider .swiper-slide img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Hide slider on mobile (<=767px) and show static image */
@media (max-width: 767px) {
.my-ambient-slider {
display: none !important;
}
}
/* Show slider on desktop (>=768px) and hide static image */
@media (min-width: 768px) {
.image-video-container>img {
display: none !important;
}
}
</style>
<!-- Slider Component Replacing Video -->
<div class="swiper my-ambient-slider">
<div class="swiper-wrapper">
<div class="swiper-slide">
<img src="/images/mainpage_slider/BANNER-LINNC-BAC-CUONG-05-scaled.jpg"
alt="Banner LINNC Bác Cường">
</div>
<div class="swiper-slide">
<img src="/images/mainpage_slider/BANNER-WEB-04-scaled.jpg" alt="Banner Web 04">
</div>
<div class="swiper-slide">
<img src="/images/mainpage_slider/W2-01-scaled.jpg" alt="W2 01 Banner">
</div>
</div>
</div>
<div class="f--ambient-video"
style="position: absolute; top:0; left:0; width:100%; height:100%;">
<video role="presentation" tabindex="-1" loop="" autoplay="" playsinline="" muted=""
style="width: 100%; height: 100%; object-fit: cover;">
<source
src="/images/YTDown_YouTube_Benh-vien-Da-Khoa-Quoc-Te-SIS-Can-Tho-Th_Media_pcz597Ou5U_001_1080p (online-video-cutter.com).mp4"
type="video/mp4">
</video>
</div>
<div class="video-controls" data-once="ambientVideo">
<div class="video-controls-inner">
<button aria-labelledby="pauseBtn" class="video-button video-pause-button">
<svg height="14" viewBox="0 0 10 14" width="10"
xmlns="http://www.w3.org/2000/svg">
<title id="pauseBtn">Pause Background Video</title>
<path d="m1143 711v14h-3v-14zm7 0v14h-3v-14z" fill="#fff" fill-rule="evenodd"
transform="translate(-1140 -711)"></path>
</svg>
</button>
<button aria-labelledby="playBtn" class="video-button video-play-button">
<svg height="29" viewBox="0 0 29 29" width="29"
xmlns="http://www.w3.org/2000/svg">
<title id="playBtn">Play Background Video</title>
<path
d="m17.5 3c-7.99789474 0-14.5 6.50210526-14.5 14.5 0 7.9978947 6.50210526 14.5 14.5 14.5 7.9978947 0 14.5-6.5021053 14.5-14.5 0-7.99789474-6.5021053-14.5-14.5-14.5zm5.6763936 15.2939067-7.3503366 4.5939604c-.6635721.408352-1.5313202-.051044-1.5313202-.8422261v-9.1879207c0-.7911821.8677481-1.2761001 1.5313202-.8422261l7.3503366 4.5939604c.612528.38283.612528 1.3016221 0 1.6844521z"
fill="#fff" fill-rule="evenodd" transform="translate(-3 -3)"></path>
</svg>
</button>
</div>
<!-- Component Template Fallback for Video (Running Permanently) -->
<div id="hero-video-fallback">
<th:block th:utext="${@componentTemplateService.render('video-fallback', null)}"></th:block>
</div>
<!-- Black Gradient Overlay (Top Only) -->
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{themes/__${activeTheme}__/layout-home(bodyClass='umass-platform-homepage path-frontpage page-node-type-homepage homepage transparent-header')}">
<head>
<title>Bệnh Viện Đa Khoa Quốc Tế S.I.S Cần Thơ</title>
</head>
<body>
<!-- Content Top Area -->
<div layout:fragment="content-top">
<th:block th:replace="~{themes/__${activeTheme}__/content-top}"></th:block>
</div>
<!-- Main Content Area -->
<div layout:fragment="content">
<th:block th:replace="~{themes/__${activeTheme}__/snippets/block_1_block-multiple-ctas}"></th:block>
<th:block th:replace="~{themes/__${activeTheme}__/snippets/block_3_button}"></th:block>
<th:block th:replace="~{themes/__${activeTheme}__/snippets/block_9_news_features}"></th:block>
<th:block th:replace="~{themes/__${activeTheme}__/snippets/block_11_charity}"></th:block>
<th:block th:replace="~{themes/__${activeTheme}__/snippets/tabbed-livestream-snippet}"></th:block>
<th:block th:replace="~{themes/__${activeTheme}__/snippets/block_10_education}"></th:block>
<th:block th:replace="~{themes/__${activeTheme}__/snippets/block_4_books}"></th:block>
</div>
</body>
</html>
@@ -1,73 +1,135 @@
<div class="content-top">
<div class="lc--layout-container lc--full">
<div class="l--layout l--full">
<div class="lr--layout-region lr--main">
<div class="cc--component-container cc--homepage-hero ">
<div class="c--component c--homepage-hero">
<div class="slides-container">
<div class="image-video-container has-video">
<img src="https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg" alt="Students collaborate using a driving simulator in the UMass Center for Transportation." srcset="https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg?h=9855f42d&amp;itok=d27r8GaT 1920w">
<div class="f--ambient-video">
<video role="presentation" tabindex="-1" loop="" autoplay="" playsinline="" muted="">
<source src="http://localhost:8080/uploads/2026/07/9375f45b-027a-4fb4-bd18-66889309280d.mp4" type="video/mp4">
</video>
</div>
<div class="video-controls" data-once="ambientVideo">
<div class="video-controls-inner">
<button aria-labelledby="pauseBtn" class="video-button video-pause-button">
<svg height="14" viewBox="0 0 10 14" width="10" xmlns="http://www.w3.org/2000/svg">
<title id="pauseBtn">Pause Background Video</title>
<path d="m1143 711v14h-3v-14zm7 0v14h-3v-14z" fill="#fff" fill-rule="evenodd" transform="translate(-1140 -711)"></path>
</svg>
</button>
<button aria-labelledby="playBtn" class="video-button video-play-button">
<svg height="29" viewBox="0 0 29 29" width="29" xmlns="http://www.w3.org/2000/svg">
<title id="playBtn">Play Background Video</title>
<path d="m17.5 3c-7.99789474 0-14.5 6.50210526-14.5 14.5 0 7.9978947 6.50210526 14.5 14.5 14.5 7.9978947 0 14.5-6.5021053 14.5-14.5 0-7.99789474-6.5021053-14.5-14.5-14.5zm5.6763936 15.2939067-7.3503366 4.5939604c-.6635721.408352-1.5313202-.051044-1.5313202-.8422261v-9.1879207c0-.7911821.8677481-1.2761001 1.5313202-.8422261l7.3503366 4.5939604c.612528.38283.612528 1.3016221 0 1.6844521z" fill="#fff" fill-rule="evenodd" transform="translate(-3 -3)"></path>
</svg>
</button>
</div>
<div class="content-top">
<div class="lc--layout-container lc--full">
<div class="l--layout l--full">
<div class="lr--layout-region lr--main">
<div class="cc--component-container cc--homepage-hero">
<div class="c--component c--homepage-hero">
<div class="slides-container">
<div class="image-video-container has-video">
<img
src="https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg"
alt="Students collaborate using a driving simulator in the UMass Center for Transportation."
srcset="
https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg?h=9855f42d&amp;itok=d27r8GaT 1920w
"
/>
<div class="f--ambient-video" id="hero-ambient-video">
<video role="presentation" tabindex="-1" loop="" autoplay="" playsinline="" muted="" id="hero-video" onerror="handleVideoError()">
<source src="http://localhost:8080/uploads/2026/07/9375f45b-027a-4fb4-bd18-66889309280d.mp4" type="video/mp4" onerror="handleVideoError()" />
</video>
</div>
<div id="hero-video-fallback" style="display: none; position: absolute; top:0; left:0; width:100%; height:100%; z-index: 2;">
<th:block th:utext="${videoFallbackHtml}"></th:block>
</div>
<script>
function handleVideoError() {
var vidContainer = document.getElementById('hero-ambient-video');
if(vidContainer) vidContainer.style.display = 'none';
var controls = document.querySelector('.video-controls');
if(controls) controls.style.display = 'none';
var fallback = document.getElementById('hero-video-fallback');
if(fallback) fallback.style.display = 'block';
}
window.addEventListener('DOMContentLoaded', function() {
var v = document.getElementById('hero-video');
if (v && v.error) {
handleVideoError();
}
});
</script>
<div class="video-controls" data-once="ambientVideo">
<div class="video-controls-inner">
<button aria-labelledby="pauseBtn" class="video-button video-pause-button">
<svg height="14" viewBox="0 0 10 14" width="10" xmlns="http://www.w3.org/2000/svg">
<title id="pauseBtn">Pause Background Video</title>
<path
d="m1143 711v14h-3v-14zm7 0v14h-3v-14z"
fill="#fff"
fill-rule="evenodd"
transform="translate(-1140 -711)"
></path>
</svg>
</button>
<button aria-labelledby="playBtn" class="video-button video-play-button">
<svg height="29" viewBox="0 0 29 29" width="29" xmlns="http://www.w3.org/2000/svg">
<title id="playBtn">Play Background Video</title>
<path
d="m17.5 3c-7.99789474 0-14.5 6.50210526-14.5 14.5 0 7.9978947 6.50210526 14.5 14.5 14.5 7.9978947 0 14.5-6.5021053 14.5-14.5 0-7.99789474-6.5021053-14.5-14.5-14.5zm5.6763936 15.2939067-7.3503366 4.5939604c-.6635721.408352-1.5313202-.051044-1.5313202-.8422261v-9.1879207c0-.7911821.8677481-1.2761001 1.5313202-.8422261l7.3503366 4.5939604c.612528.38283.612528 1.3016221 0 1.6844521z"
fill="#fff"
fill-rule="evenodd"
transform="translate(-3 -3)"
></path>
</svg>
</button>
</div>
</div>
<div class="text-container centered">
<div class="slide-text-container-inner">
<h3>
<a href="https://www.umass.edu/gateway/why-umass">Stroke International School 2026.</a>
</h3>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/why-umass" class="button-secondary button-context-dark " aria-label="Learn More Learn more about UMass Amherst." data-component-id="umass_base:button">
<span class="button-text"> Learn More </span>
</a>
</div>
<nav class="mc--menu mc--info-menu" th:if="${infoMenu != null and not #lists.isEmpty(infoMenu.items)}">
<ul class="menu m--menu m--info-menu">
<li class="menu-item menu-item--expanded">
<details class="utility-button-wrapper mc--info-menu-container">
<summary type="button" aria-label="Display submenu for Information menu" aria-expanded="false" aria-haspopup="true" class="utility-button arrow-toggle information-for-toggle">
<span th:text="${infoMenu.name}">Info For</span>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 16 7" enable-background="new 0 0 16 7" xml:space="preserve" width="16" height="7" class="arrow">
<polygon fill-rule="evenodd" clip-rule="evenodd" fill="#ffffff" points="7,7 0,0 16,0 "></polygon>
</svg>
</summary>
<div class="submenu-wrapper">
<ul class="submenu">
<li class="menu-item" th:each="item : ${infoMenu.items}">
<a th:href="${item.url}" th:text="${item.label}">Prospective Students</a>
</li>
</ul>
</div>
</details>
<div class="submenu-wrapper-desktop">
</div>
<div class="text-container centered">
<div class="slide-text-container-inner">
<h3>
<a href="https://www.umass.edu/gateway/why-umass">Stroke International School 2026.</a>
</h3>
<div class="f--field f--button">
<a
href="https://www.umass.edu/gateway/why-umass"
class="button-secondary button-context-dark"
aria-label="Learn More Learn more about UMass Amherst."
data-component-id="umass_base:button"
>
<span class="button-text"> Learn More </span>
</a>
</div>
<nav class="mc--menu mc--info-menu" th:if="${infoMenu != null and not #lists.isEmpty(infoMenu.items)}">
<ul class="menu m--menu m--info-menu">
<li class="menu-item menu-item--expanded">
<details class="utility-button-wrapper mc--info-menu-container">
<summary
type="button"
aria-label="Display submenu for Information menu"
aria-expanded="false"
aria-haspopup="true"
class="utility-button arrow-toggle information-for-toggle"
>
<span th:text="${infoMenu.name}">Info For</span>
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 16 7"
enable-background="new 0 0 16 7"
xml:space="preserve"
width="16"
height="7"
class="arrow"
>
<polygon fill-rule="evenodd" clip-rule="evenodd" fill="#ffffff" points="7,7 0,0 16,0 "></polygon>
</svg>
</summary>
<div class="submenu-wrapper">
<ul class="submenu">
<li class="menu-item" th:each="item : ${infoMenu.items}">
<a th:href="${item.url}" th:text="${item.label}">Prospective Students</a>
</li>
</ul>
</div>
</li>
</ul>
</nav>
</div>
</details>
<div class="submenu-wrapper-desktop">
<ul class="submenu">
<li class="menu-item" th:each="item : ${infoMenu.items}">
<a th:href="${item.url}" th:text="${item.label}">Prospective Students</a>
</li>
</ul>
</div>
</li>
</ul>
</nav>
</div>
</div>
</div>
@@ -75,4 +137,5 @@
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,65 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8" />
<title>UMass Amherst Theme</title>
<!-- UMass Amherst Assets -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" media="all" th:href="@{/theme-assets/umass/css/css_09S7WcUyhLeUXabknzECn_ua5zOangtv2BF4OXAW2fM.css}" />
<link rel="stylesheet" media="all" th:href="@{/theme-assets/umass/css/css_5USufIKxGNJyomvgpy2m9ITQlTN9qcrtJQsw_06dSbo.css}" />
<link rel="stylesheet" media="all" th:href="@{/theme-assets/umass/css/css_G5ztZKRblBn54t8h9F_EK5Y3CsZrMvA2BWPMI5jJoO4.css}" />
<!-- 🟢 1. SWIPER.JS CSS (Đã thêm) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
<!-- Custom CSS -->
<link rel="stylesheet" th:href="@{/css/custom.css}" />
<!-- Theme Customizer CSS Output -->
<style th:utext="${themeCss}"></style>
<!-- wp_head hook -->
<th:block th:utext="${hookManager.doActionAndReturn('wp_head')}"></th:block>
</head>
<body
th:class="${(bodyClass != null ? bodyClass : '') + ((page != null and page.pageType != null and page.pageType.name() == 'HOME') ? ' umass-platform-homepage path-frontpage page-node-type-homepage homepage transparent-header' : '')}"
>
<div class="dialog-off-canvas-main-canvas" data-off-canvas-main-canvas="">
<!-- Include Header Fragment -->
<header th:replace="~{themes/__${activeTheme}__/header :: header}"></header>
<!-- Full-width top content area (e.g., Hero Banners) -->
<div layout:fragment="content-top"></div>
<main id="main-content">
<!--
HOMEPAGE FULL WIDTH LAYOUT
Removed l--content, region padding, and max-width constraints
-->
<div class="full-width-container" style="width: 100%; min-height: 400px; padding: 0; margin: 0;">
<div
class="full-width-content"
style="width: 100%;"
layout:fragment="content"
></div>
</div>
</main>
<!-- Include Footer Fragment -->
<footer th:replace="~{themes/__${activeTheme}__/footer :: footer}"></footer>
</div>
<!-- UMass Scripts -->
<script th:src="@{/theme-assets/umass/js/js_aQtUyeGxehNR84AlGzGB1VfMu1Wn3lqHxvL8rocj1EU.js}"></script>
<script th:src="@{/js/mobile-menu.js}"></script>
<script th:src="@{/js/ambient-video.js}"></script>
<script th:src="@{/js/main.js}"></script>
<!-- 🟢 2. SWIPER.JS SCRIPT (Đã thêm) -->
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<!-- wp_footer hook -->
<th:block th:utext="${hookManager.doActionAndReturn('wp_footer')}"></th:block>
<!-- Page Scripts Fragment -->
<th:block layout:fragment="scripts"></th:block>
</body>
</html>
@@ -0,0 +1,205 @@
<style>
.education-slider-wrapper {
position: relative;
padding: 0px;
/* Space for buttons */
}
.education-slider-container {
overflow: hidden;
width: 100%;
}
.education-slider-wrapper .news-grid,
.education-slider-wrapper .row,
.education-slider-track {
display: flex !important;
flex-wrap: nowrap !important;
gap: 20px;
margin: 0 !important;
transition: transform 0.4s ease;
}
/* Force children to be fixed width cards */
.education-slider-wrapper .news-grid>*,
.education-slider-wrapper .row>*,
.education-slider-track>* {
flex: 0 0 calc(33.333% - 14px) !important;
/* show 3 cards at a time */
max-width: none !important;
}
@media (max-width: 992px) {
.education-slider-wrapper .news-grid>*,
.education-slider-wrapper .row>*,
.education-slider-track>* {
flex: 0 0 calc(50% - 10px) !important;
}
}
@media (max-width: 576px) {
.education-slider-wrapper .news-grid>*,
.education-slider-wrapper .row>*,
.education-slider-track>* {
flex: 0 0 100% !important;
}
}
.edu-slider-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
border-radius: 50%;
background-color: rgba(200, 200, 200, 0.5);
/* mờ / transparent */
color: #333;
border: none;
font-size: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
transition: background-color 0.2s, color 0.2s;
line-height: 1;
}
.edu-slider-btn:hover {
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
}
.edu-slider-btn.prev-btn {
left: -10px;
}
.edu-slider-btn.next-btn {
right: -10px;
}
.edu-slider-btn:disabled {
opacity: 0.2;
cursor: not-allowed;
}
/* --- Hiệu ứng crop (zoom) ảnh khi hover --- */
.education-slider-wrapper .featured-grid__item .news-card__image {
overflow: hidden !important;
display: block;
}
.education-slider-wrapper .featured-grid__item .news-card__image img {
transition: transform 0.4s ease-in-out !important;
}
.education-slider-wrapper .featured-grid__item:hover .news-card__image img {
transform: scale(1.1) !important;
}
</style>
<body>
<div class="cc--component-container cc--news-events">
<div class="c--component c--news-events">
<!-- Header -->
<div class="news-events-row">
<div class="education-column news-column" style="width: 100%;">
<div
style="display: flex; justify-content: space-between; align-items: baseline; border-bottom: 2px solid var(--color-brand); margin-bottom: 16px;">
<!-- Thêm flex-grow: 1 vào style của h2 -->
<h2 class="section-title--news"
style="font-size: 2rem; margin: 0; padding-bottom: 4px; border-bottom: unset;">ĐÀO TẠO</h2>
<a href="/news/education" class="news-events-section__cta news-events-section__cta--desktop"
style="color: var(--color-brand); font-weight: 800; font-size: 16px; text-transform: uppercase; text-decoration: none; border-bottom: unset;">XEM
THÊM</a>
</div>
<div class="education-slider-wrapper" style="padding-right: 0;">
<button class="edu-slider-btn prev-btn" aria-label="Previous">&lsaquo;</button>
<div class="education-slider-container">
<div class="education-slider-track">
<th:block th:utext="${componentTemplateService.render('news-grid', 'posts:tag=education,limit=5,collection=news_posts')}"></th:block>
</div>
</div>
<button class="edu-slider-btn next-btn" aria-label="Next">&rsaquo;</button>
</div>
</div>
</div><!-- Mobile Footer CTA -->
<div class="news-events-section__footer"><a href="/news/education"
class="news-events-section__cta news-events-section__cta--mobile">Xem Thêm</a></div>
</div>
</div>
</body>
<script>
document.addEventListener("DOMContentLoaded", function() {
const wrappers = document.querySelectorAll('.education-slider-wrapper');
wrappers.forEach(wrapper => {
const prevBtn = wrapper.querySelector('.prev-btn');
const nextBtn = wrapper.querySelector('.next-btn');
let track = wrapper.querySelector('.education-slider-track');
if (track && track.children.length === 1 && (track.children[0].classList.contains('news-grid') || track.children[0].classList.contains('row'))) {
track = track.children[0];
track.classList.add('education-slider-track');
}
if (!track) return;
let currentIndex = 0;
function updateSlider() {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
track.style.transform = `translateX(-${currentIndex * (itemWidth + gap)}px)`;
const containerWidth = wrapper.querySelector('.education-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
prevBtn.disabled = currentIndex === 0;
nextBtn.disabled = currentIndex >= items.length - visibleCount;
}
prevBtn.addEventListener('click', () => {
if (currentIndex > 0) {
currentIndex--;
updateSlider();
}
});
nextBtn.addEventListener('click', () => {
const items = track.children;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.education-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex < items.length - visibleCount) {
currentIndex++;
updateSlider();
}
});
window.addEventListener('resize', () => {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.education-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex > items.length - visibleCount) {
currentIndex = Math.max(0, items.length - visibleCount);
}
updateSlider();
});
setTimeout(updateSlider, 200);
});
});
</script>
@@ -0,0 +1,727 @@
<style>
.charity-hero {
--red: #d7193f;
--red-dark: #a90f2d;
--red-soft: #fff0f4;
--pink: #ffe3ec;
--ink: #36363a;
--muted: #74747a;
--paper: #f7f7f8;
--white: #ffffff;
--shadow: 0 24px 70px rgba(174, 23, 55, .18);
--shadow-strong: 0 32px 90px rgba(100, 10, 30, .28);
}
.charity-hero {
font-family: "Segoe UI", Arial, sans-serif;
color: var(--ink);
background: #f1f1f2;
position: relative;
min-height: 546px;
overflow: hidden;
isolation: isolate;
background:
radial-gradient(circle at 72% 22%, rgba(255,255,255,.92), transparent 28%),
linear-gradient(110deg, #ffeaf0 0%, #f4f4f5 42%, #f9f9fa 100%);
}
.charity-hero * {
box-sizing: border-box;
}
.charity-hero a {
text-decoration: none;
color: inherit;
}
.charity-hero .hero-bg {
position: absolute;
inset: 0;
z-index: -2;
overflow: hidden;
}
.charity-hero .blob {
position: absolute;
border-radius: 999px;
filter: blur(3px);
opacity: .72;
animation: blobFloat 13s ease-in-out infinite alternate;
}
.charity-hero .blob-1 {
width: 460px;
height: 460px;
background: rgba(255,207,221,.75);
left: -150px;
top: -170px;
}
.charity-hero .blob-2 {
width: 520px;
height: 520px;
background: rgba(255,236,241,.9);
right: -180px;
bottom: -220px;
animation-delay: -3s;
}
.charity-hero .blob-3 {
width: 220px;
height: 220px;
background: rgba(215,25,63,.12);
left: 48%;
top: 48%;
animation-delay: -6s;
}
.charity-hero .soft-grid {
position: absolute;
inset: 22px 150px;
border: 5px solid rgba(255,206,218,.68);
border-radius: 6px;
mask: linear-gradient(90deg,transparent,black 4%,black 96%,transparent);
-webkit-mask: linear-gradient(90deg,transparent,black 4%,black 96%,transparent);
opacity: .76;
}
.charity-hero .soft-grid:before,
.charity-hero .soft-grid:after {
content: "";
position: absolute;
background: var(--red);
border-radius: 50% 50% 48% 48%;
transform: rotate(45deg);
opacity: .9;
}
.charity-hero .soft-grid:before {
width: 36px;
height: 36px;
left: 22px;
bottom: -13px;
box-shadow: 34px -1px 0 var(--red);
}
.charity-hero .soft-grid:after {
width: 17px;
height: 17px;
right: 18px;
top: 285px;
opacity: .55;
box-shadow: 20px 19px 0 rgba(215,25,63,.9);
}
.charity-hero .hero-shell {
width: min(1580px, calc(100% - 44px));
min-height: 546px;
margin: 0 auto;
display: grid;
grid-template-columns: minmax(640px, 1.1fr) minmax(470px, .9fr);
gap: 42px;
align-items: center;
padding: 36px 0;
}
.charity-hero .gallery-panel {
position: relative;
min-height: 500px;
padding: 18px 12px 0;
height: 100%;
}
.charity-hero .gallery-title {
position: absolute;
left: 22px;
top: 10px;
z-index: 22;
display: inline-flex;
flex-direction: column;
gap: 4px;
padding: 10px 16px;
border-radius: 18px;
background: rgba(255,255,255,.72);
box-shadow: 0 12px 30px rgba(70,40,50,.08);
backdrop-filter: blur(10px);
transform: translateY(-12px);
opacity: 0;
animation: fadeDown .7s ease forwards .25s;
}
.charity-hero .gallery-title .eyebrow {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--red);
font-weight: 800;
}
.charity-hero .gallery-title strong {
font-size: 16px;
color: #313136;
}
.charity-hero .gallery-stage {
position: relative;
height: 100%;
width: 100%;
overflow: hidden;
perspective: 1200px;
}
.charity-hero .gallery-light {
position: absolute;
left: 34%;
top: 35%;
width: 310px;
height: 210px;
border-radius: 50%;
background: radial-gradient(circle, rgba(255,255,255,.88), rgba(255,255,255,0) 66%);
pointer-events: none;
opacity: .45;
mix-blend-mode: screen;
animation: lightPulse 4s ease-in-out infinite;
}
.charity-hero .focus-frame {
position: absolute;
left: 50%;
top: 50%;
width: 402px;
height: 238px;
transform: translate(-50%,-50%) scale(.86);
border-radius: 22px;
border: 1px solid rgba(215,25,63,.25);
box-shadow: 0 0 0 10px rgba(255,255,255,.16), inset 0 0 42px rgba(215,25,63,.07);
opacity: 0;
transition: .7s ease;
pointer-events: none;
z-index: 4;
}
.charity-hero .gallery-stage.is-playing .focus-frame {
opacity: 1;
transform: translate(-50%,-50%) scale(1);
}
.charity-hero .photo-card {
position: absolute;
left: var(--x);
top: var(--y);
width: var(--w);
background: #fff;
border: 4px solid var(--red);
border-radius: 3px;
overflow: hidden;
box-shadow: 0 14px 36px rgba(61,26,34,.22);
transform: translate3d(0,0,0) rotate(var(--r)) scale(.92);
opacity: 0;
z-index: 5;
will-change: left, top, width, transform, opacity, filter;
animation: cardIntro .85s cubic-bezier(.2,.9,.25,1.12) forwards;
animation-delay: var(--delay);
transition:
opacity 2.5s ease-in-out,
filter 2.5s ease-in-out,
box-shadow 1.5s cubic-bezier(.2,.8,.2,1),
border-color 1.5s cubic-bezier(.2,.8,.2,1),
left 1.5s cubic-bezier(.2,.8,.2,1),
top 1.5s cubic-bezier(.2,.8,.2,1),
width 1.5s cubic-bezier(.2,.8,.2,1),
transform 1.5s cubic-bezier(.2,.8,.2,1);
}
.charity-hero .photo-card img {
display: block;
width: 100%;
height: auto;
transform: scale(1.035);
transition: transform 1s cubic-bezier(.18,.82,.18,1), filter .8s ease;
}
.charity-hero .photo-card:before {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(120deg, transparent 20%, rgba(255,255,255,.34) 45%, transparent 70%);
transform: translateX(-130%);
z-index: 2;
pointer-events: none;
}
.charity-hero .photo-card.is-active:before {
animation: shine 1.3s ease .25s;
}
.charity-hero .photo-card.is-active {
left: 50%;
top: 50%;
width: min(480px, 58vw);
transform: translate(-50%,-50%) rotate(0deg) scale(1.06) !important;
opacity: 1;
z-index: 30;
border-color: #fff;
border-width: 8px;
border-radius: 18px;
box-shadow: var(--shadow-strong);
filter: saturate(1.08) contrast(1.03);
}
.charity-hero .photo-card.is-active img {
transform: scale(1.08);
}
.charity-hero .photo-card.is-dim {
opacity: .28;
filter: grayscale(.08) saturate(.8) blur(.2px);
transform: translate3d(0,0,0) rotate(var(--r)) scale(.82);
z-index: 2;
}
.charity-hero .photo-card.is-next {
opacity: .74;
box-shadow: 0 17px 45px rgba(92,24,42,.18);
}
.charity-hero .photo-card.is-exit {
opacity: 0;
transform: translateY(28px) scale(.74) rotate(var(--r));
}
.charity-hero .gallery-controls {
position: absolute;
left: 50%;
bottom: 2px;
transform: translateX(-50%);
display: flex;
gap: 8px;
z-index: 40;
}
.charity-hero .dot {
width: 7px;
height: 7px;
border-radius: 999px;
background: rgba(215,25,63,.22);
transition: .35s ease;
cursor: pointer;
}
.charity-hero .dot.is-active {
width: 28px;
background: var(--red);
box-shadow: 0 0 0 6px rgba(215,25,63,.09);
}
.charity-hero .content-panel {
position: relative;
min-height: 430px;
display: flex;
flex-direction: column;
justify-content: center;
padding: 26px 24px 26px 8px;
}
.charity-hero .content-panel:before {
content: "";
position: absolute;
inset: 28px 0 18px 0;
border-radius: 34px;
background: linear-gradient(145deg, rgba(255,255,255,.54), rgba(255,255,255,.08));
border: 1px solid rgba(255,255,255,.55);
box-shadow: 0 20px 70px rgba(62,45,54,.06);
z-index: -1;
}
.charity-hero .brand-row {
display: grid;
grid-template-columns: 1fr 170px;
gap: 24px;
align-items: start;
margin-bottom: 54px;
}
.charity-hero .logo-mark {
width: 165px;
justify-self: end;
filter: drop-shadow(0 10px 16px rgba(215,25,63,.15));
animation: logoBreath 3.6s ease-in-out infinite;
}
.charity-hero .brand-text {
text-align: center;
align-self: center;
}
.charity-hero .brand-text h1 {
margin: 0 0 8px;
text-transform: uppercase;
line-height: 1.18;
font-size: clamp(20px,1.72vw,30px);
letter-spacing: -.3px;
color: var(--red);
font-weight: 900;
}
.charity-hero .brand-text p {
margin: 0;
color: #67676c;
text-transform: uppercase;
font-size: clamp(16px,1.2vw,22px);
font-weight: 800;
letter-spacing: .3px;
}
.charity-hero .slogan-card {
width: min(560px,100%);
margin: 0 auto;
position: relative;
text-align: center;
padding: 20px 18px 28px;
}
.charity-hero .slogan-card:before {
content: "";
position: absolute;
left: 50%;
top: 50%;
width: 86%;
height: 86%;
transform: translate(-50%,-50%);
border-radius: 38px;
background: linear-gradient(90deg,rgba(255,255,255,.18),rgba(255,237,243,.72),rgba(255,255,255,.12));
filter: blur(.2px);
z-index: -1;
opacity: .8;
}
.charity-hero .quote-mark {
position: absolute;
left: 12px;
top: -16px;
font-size: 74px;
line-height: 1;
color: rgba(215,25,63,.18);
font-family: Georgia,serif;
}
.charity-hero .slogan-card h2 {
margin: 0;
color: #e13b5e;
font-size: clamp(29px,2.2vw,42px);
line-height: 1.42;
font-family: "Segoe Script","Brush Script MT",cursive;
font-weight: 600;
text-shadow: 0 8px 20px rgba(215,25,63,.12);
}
.charity-hero .slogan-card h2 em {
font-style: normal;
font-size: .86em;
color: #d9284c;
}
.charity-hero .heart-line {
display: block;
width: 160px;
height: 22px;
margin: 12px auto 0;
position: relative;
}
.charity-hero .heart-line:before {
content: "";
position: absolute;
left: 0;
right: 34px;
top: 11px;
height: 2px;
background: linear-gradient(90deg,transparent,var(--red));
border-radius: 999px;
}
.charity-hero .heart-line:after {
content: "♡";
position: absolute;
right: 6px;
top: -8px;
color: var(--red);
font-size: 32px;
transform: rotate(18deg);
animation: heartBeat 1.7s ease-in-out infinite;
}
.charity-hero .action-row {
display: flex;
gap: 12px;
justify-content: center;
margin-top: 26px;
flex-wrap: wrap;
}
.charity-hero .btn {
display: inline-flex;
align-items: center;
justify-content: center;
height: 44px;
padding: 0 20px;
border-radius: 999px;
font-weight: 800;
font-size: 14px;
letter-spacing: .2px;
transition: .3s ease;
box-shadow: 0 12px 30px rgba(115,21,43,.12);
}
.charity-hero .btn.primary {
background: linear-gradient(135deg,var(--red),#ef4769);
color: white;
}
.charity-hero .btn.secondary {
background: rgba(255,255,255,.74);
color: var(--red);
border: 1px solid rgba(215,25,63,.18);
}
.charity-hero .reveal-up {
opacity: 0;
transform: translateY(24px);
animation: revealUp .8s ease forwards;
}
.charity-hero .brand-text.reveal-up {
animation-delay: .2s;
}
.charity-hero .logo-mark.reveal-up {
animation-delay: .38s;
}
.charity-hero .slogan-card.reveal-up {
animation-delay: .62s;
}
.charity-hero .action-row.reveal-up {
animation-delay: .86s;
}
@keyframes cardIntro {
from { opacity: 0; transform: translate3d(-28px,22px,0) rotate(calc(var(--r) - 7deg)) scale(.78); }
to { opacity: 1; transform: translate3d(0,0,0) rotate(var(--r)) scale(.92); }
}
/* Removed microFloat because animating margin-top constantly recalculates layout causing major stutter */
@keyframes shine {
to { transform: translateX(130%); }
}
@keyframes revealUp {
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeDown {
to { opacity: 1; transform: translateY(0); }
}
@keyframes logoBreath {
0%,100% { transform: translateY(0) scale(1); }
50% { transform: translateY(-6px) scale(1.025); }
}
@keyframes heartBeat {
0%,100% { transform: rotate(18deg) scale(1); }
20% { transform: rotate(18deg) scale(1.18); }
38% { transform: rotate(18deg) scale(1); }
}
@keyframes blobFloat {
to { transform: translate(26px,20px) scale(1.05); }
}
@keyframes lightPulse {
0%,100% { opacity: .25; transform: scale(.96); }
50% { opacity: .58; transform: scale(1.08); }
}
@media (max-width:1200px) {
.charity-hero .hero-shell {
grid-template-columns: 1fr;
gap: 10px;
padding: 18px 0 34px;
}
.charity-hero { min-height: auto; }
.charity-hero .content-panel { min-height: auto; }
.charity-hero .gallery-panel { min-height: 470px; }
.charity-hero .gallery-stage { height: 430px; }
.charity-hero .brand-row { grid-template-columns: 1fr 130px; margin-bottom: 20px; }
.charity-hero .logo-mark { width: 130px; }
}
@media (max-width:720px) {
.charity-hero .hero-shell { width: calc(100% - 24px); }
.charity-hero .soft-grid { inset: 12px; border-width: 3px; }
.charity-hero .gallery-panel { padding: 12px 0; min-height: 390px; }
.charity-hero .gallery-title { display: none; }
.charity-hero .gallery-stage { height: 370px; overflow: hidden; }
.charity-hero .photo-card { width: calc(var(--w) * .72); }
.charity-hero .photo-card.is-active { width: min(86vw,380px); }
.charity-hero .content-panel { padding: 8px; }
.charity-hero .brand-row { display: flex; flex-direction: column-reverse; align-items: center; gap: 10px; }
.charity-hero .brand-text h1 { text-align: center; }
.charity-hero .slogan-card h2 { font-size: 29px; }
.charity-hero .btn { height: 42px; padding: 0 16px; }
}
@media (prefers-reduced-motion:reduce) {
.charity-hero *,
.charity-hero *::before,
.charity-hero *::after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
transition-duration: .01ms !important;
}
}
</style>
<div class="charity-hero" aria-label="Quỹ từ thiện bệnh nhân nghèo bị đột quỵ">
<div class="hero-bg">
<span class="blob blob-1"></span>
<span class="blob blob-2"></span>
<span class="blob blob-3"></span>
<span class="soft-grid"></span>
</div>
<section class="hero-shell">
<div class="gallery-panel" aria-label="Hình ảnh hoạt động quỹ từ thiện">
<div class="gallery-title">
<span class="eyebrow">S.I.S Charity Foundation</span>
<strong>Những khoảnh khắc sẻ chia</strong>
</div>
<div class="gallery-stage" id="galleryStage">
<article class="photo-card p1" data-index="0" style="--x: -4%; --y: 2%; --r: -2.5deg; --w: 205px; --delay: 0.05s;">
<img src="/upload/charity_v4/photo-01.jpg" alt="Trao hỗ trợ bệnh nhân 1" />
</article>
<article class="photo-card p2" data-index="1" style="--x: 18%; --y: -2%; --r: 1.6deg; --w: 298px; --delay: 0.16s;">
<img src="/upload/charity_v4/photo-02.jpg" alt="Trao hỗ trợ bệnh nhân 2" />
</article>
<article class="photo-card p3" data-index="2" style="--x: 63%; --y: 0%; --r: -1.2deg; --w: 410px; --delay: 0.27s;">
<img src="/upload/charity_v4/photo-05.jpg" alt="Trao hỗ trợ bệnh nhân 3" />
</article>
<article class="photo-card p4" data-index="3" style="--x: 0%; --y: 27%; --r: 2.2deg; --w: 245px; --delay: 0.38s">
<img src="/upload/charity_v4/photo-03.jpg" alt="Trao hỗ trợ bệnh nhân 4" />
</article>
<article class="photo-card p5" data-index="4" style="--x: 32%; --y: 32%; --r: -1.8deg; --w: 260px; --delay: 0.49s;">
<img src="/upload/charity_v4/photo-04.jpg" alt="Trao hỗ trợ bệnh nhân 5" />
</article>
<article class="photo-card p6" data-index="5" style="--x: 57%; --y: 35%; --r: 1deg; --w: 195px; --delay: 0.6s">
<img src="/upload/charity_v4/photo-06.jpg" alt="Trao hỗ trợ bệnh nhân 6" />
</article>
<article class="photo-card p7" data-index="6" style="--x: 4%; --y: 62%; --r: -1.4deg; --w: 270px; --delay: 0.71s;">
<img src="/upload/charity_v4/photo-07.jpg" alt="Trao hỗ trợ bệnh nhân 7" />
</article>
<article class="photo-card p8" data-index="7" style="--x: 42%; --y: 73%; --r: 1.2deg; --w: 255px; --delay: 0.82s;">
<img src="/upload/charity_v4/photo-08.jpg" alt="Trao hỗ trợ bệnh nhân 8" />
</article>
<article class="photo-card p9" data-index="8" style="--x: 77%; --y: 66%; --r: -1deg; --w: 338px; --delay: 0.93s">
<img src="/upload/charity_v4/photo-09.jpg" alt="Trao hỗ trợ bệnh nhân 9" />
</article>
<div class="gallery-light" aria-hidden="true"></div>
</div>
<div class="gallery-controls" aria-hidden="true">
<span class="dot is-active"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</div>
</div>
<div class="content-panel">
<div class="brand-row">
<div class="brand-text reveal-up">
<h1>Quỹ từ thiện bệnh nhân nghèo bị đột quỵ<br />khu vực Đồng bằng sông Cửu Long</h1>
<p>S.I.S Charity Foundation</p>
</div>
<img class="logo-mark reveal-up" src="/upload/charity_v4/logo.png" alt="Stroke International Services Charity Foundation" />
</div>
<div class="slogan-card reveal-up">
<span class="quote-mark"></span>
<h2>Sẻ chia yêu thương<br /><em>đến bệnh nhân nghèo bị đột quỵ</em></h2>
<span class="heart-line" aria-hidden="true"></span>
</div>
<div class="action-row reveal-up">
<a class="btn primary" href="#">Lan tỏa yêu thương</a>
<a class="btn secondary" href="#">Xem hoạt động quỹ</a>
</div>
</div>
</section>
</div>
<script>
(function(){
const container = document.querySelector('.charity-hero');
if(!container) return;
const stage = container.querySelector('.gallery-stage');
const cards = Array.from(container.querySelectorAll('.photo-card'));
const dots = Array.from(container.querySelectorAll('.dot'));
if(!stage || cards.length === 0) return;
let active = 0;
let timer = null;
const duration = 4500; /* Tăng thời gian chờ (autoplay) từ 2.8 giây lên 4.5 giây để nhìn rõ ảnh hơn */
function setActive(index){
active = index % cards.length;
stage.classList.add('is-playing');
cards.forEach((card, i) => {
card.classList.remove('is-active','is-dim','is-next','is-exit');
const diff = (i - active + cards.length) % cards.length;
if(i === active){
card.classList.add('is-active');
}else if(diff === 1 || diff === 2){
card.classList.add('is-next');
}else if(diff === cards.length - 1){
card.classList.add('is-exit');
}else{
card.classList.add('is-dim');
}
});
dots.forEach((dot, i) => dot.classList.toggle('is-active', i === active));
}
function next(){
setActive(active + 1);
}
function start(){
stop();
timer = setInterval(next, duration);
}
function stop(){
if(timer) clearInterval(timer);
}
// Hệ thống sẽ chạy tự động 100%
dots.forEach((dot, i) => {
dot.addEventListener('click', () => {
setActive(i);
start();
});
});
// Start immediately or on load if part of a bigger page
setTimeout(() => {
setActive(0);
start();
}, 900);
})();
</script>
@@ -0,0 +1,29 @@
<div data-component-id="umass_base:block-multiple-ctas" data-background="solid" data-layout-variant="default">
<div class="container" th:unless="${ctaMenu != null and not #lists.isEmpty(ctaMenu.items)}">
<div class="text-container">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>TÌM HIỂU<span class="highlight">KHOA ĐIỀU TRỊ</span></h2>
</div>
</div>
<div class="links-container">
<th:block th:utext="${componentTemplateService.render('cta-buttons', 'menu:CTA')}"></th:block>
</div>
</div>
</div>
<style>
.links-container {
display: grid !important;
grid-template-columns: repeat(2, 1fr) !important;
gap: 1rem;
column-gap: 2rem !important;
padding: 0;
margin: 0;
}
@media (max-width: 600px) {
.links-container {
grid-template-columns: 1fr !important;
}
}
</style>
@@ -0,0 +1 @@
<th:block th:utext="${componentTemplateService.render('tabbed-livestream', 'posts:tag=livestream,limit=4,collection=livestream_posts')}"></th:block>
@@ -0,0 +1,43 @@
<body>
<div class="cc--component-container cc--stats-block" style="background-color: var(--color-brand)">
<div class="c--component c--stats-block">
<div class="f--field f--stats-block">
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div id="iuox8" class="f--field f--stat-number">50.000+</div>
<div id="iynqf" class="f--field f--description">
<p>Tổng số ca cấp cứu</p>
</div>
</div>
</div>
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div id="ievo1" class="f--field f--stat-number">1.6tr+</div>
<div id="iklnr" class="f--field f--description">
<p>Tổng số ca khám bệnh</p>
</div>
</div>
</div>
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div id="iz72l" class="f--field f--stat-number">30.000+</div>
<div id="i0kg8" class="f--field f--description">
<p>Số ca điều trị đột quỵ</p>
</div>
</div>
</div>
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div id="iz72l" class="f--field f--stat-number">#1</div>
<div id="i0kg8" class="f--field f--description">
<p>Về điều trị đột quỵ</p>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<style>
* { box-sizing: border-box; } body {margin: 0;}*{box-sizing:border-box;}body{margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;}#iuox8{color:white;}#iynqf{color:white;}#ievo1{color:white;}#iklnr{color:white;}#iz72l{color:white;}#i0kg8{color:white;}#iynqf *{color:white;}#iklnr *{color:white;}#i0kg8 *{color:white;}
</style>
@@ -0,0 +1,172 @@
<style>
.stroke-book-banner {
background-color: #f8f9fa;
padding: 36px 20px;
font-family: var(--font-family-base, 'Inter', 'Segoe UI', sans-serif);
display: flex;
justify-content: center;
}
.sbb-container {
display: flex;
flex-direction: row;
align-items: center;
max-width: var(--max-width);
width: 100%;
gap: 50px;
/* Faint brain background pattern placeholder */
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 24 24" fill="none" stroke="%23ffe6e6" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/></svg>');
background-position: right center;
background-repeat: no-repeat;
background-size: 350px;
}
.sbb-left {
flex: 1.2;
display: flex;
justify-content: flex-end;
}
.sbb-left img {
max-width: 100%;
height: auto;
object-fit: contain;
filter: drop-shadow(0 15px 25px rgba(0, 0, 0, 0.15));
}
.sbb-right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
padding-right: 20px;
}
.sbb-subtitle {
font-size: 1.4rem;
font-weight: 700;
color: #222;
margin-bottom: 5px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.sbb-title {
font-size: 3.2rem;
font-weight: 800;
color: #c0272d;
margin: 0 0 15px 0;
text-transform: uppercase;
line-height: 1.1;
letter-spacing: -0.5px;
}
.sbb-tagline {
font-size: 1.25rem;
font-weight: 700;
color: #333;
margin-bottom: 30px;
}
.sbb-features {
list-style: none;
padding: 0;
margin: 0 0 35px 0;
}
.sbb-features li {
font-size: 1.1rem;
color: #444;
margin-bottom: 14px;
display: flex;
align-items: center;
font-weight: 500;
}
.sbb-features li::before {
content: '✓';
display: inline-flex;
justify-content: center;
align-items: center;
width: 24px;
height: 24px;
border-radius: 50%;
border: 1.5px solid #c0272d;
color: #c0272d;
font-size: 14px;
font-weight: bold;
margin-right: 12px;
flex-shrink: 0;
}
.sbb-btn {
display: inline-block;
background: linear-gradient(180deg, #d81b32 0%, #a11122 100%);
color: #fff;
font-weight: 700;
font-size: 1.15rem;
padding: 16px 40px;
border-radius: 6px;
text-decoration: none;
text-transform: uppercase;
box-shadow: 0 4px 10px rgba(161, 17, 34, 0.4);
transition: all 0.3s ease;
letter-spacing: 0.5px;
border: 1px solid #8e0b1b;
}
.sbb-btn:hover {
background: linear-gradient(180deg, #ee1f38 0%, #b81427 100%);
box-shadow: 0 6px 15px rgba(161, 17, 34, 0.5);
transform: translateY(-2px);
color: #fff !important;
}
@media (max-width: 992px) {
.sbb-container {
flex-direction: column;
text-align: center;
gap: 30px;
background-image: none;
}
.sbb-left {
justify-content: center;
}
.sbb-right {
align-items: center;
padding-right: 0;
}
.sbb-title {
font-size: 2.5rem;
}
.sbb-features li {
text-align: left;
justify-content: flex-start;
}
}
</style>
<div class="stroke-book-banner">
<div class="sbb-container">
<div class="sbb-left">
<img src="/upload/sach-dot-quy-ngang.png" alt="Cẩm nang sức khỏe phòng chống đột quỵ">
</div>
<div class="sbb-right">
<div class="sbb-subtitle">CẨM NANG SỨC KHỎE</div>
<h2 class="sbb-title">PHÒNG CHỐNG ĐỘT QUỴ</h2>
<div class="sbb-tagline">Hiểu đúng Phòng sớm Sống khỏe mỗi ngày</div>
<ul class="sbb-features">
<li>Kiến thức khoa học, dễ hiểu</li>
<li>Nhận biết sớm dấu hiệu Xử trí kịp thời</li>
<li>Phòng ngừa hiệu quả Giảm thiểu nguy cơ</li>
<li>Dành cho mọi lứa tuổi</li>
</ul>
<a href="#" class="sbb-btn">TÌM HIỂU NGAY</a>
</div>
</div>
</div>
@@ -0,0 +1,71 @@
<body>
<div class="cc--component-container cc--card-grid dark">
<div class="c--component c--card-grid">
<div class="text-container">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>Blaze Your Own Trail</h2>
</div>
<div data-component-id="umass_base:description" class="f--section-title f--description">
<p>Discover more than 100 undergraduate majors, minors, and certificates in fields ranging from engineering to public health to literature. You can even design your own major through our individual concentration program. Whatever your path, UMass gives you the freedom to explore and the tools to excel.</p>
</div>
</div>
<div class="f--field f--cards nested">
<div class="cc--component-container cc--card-grid-card ">
<div class="c--component c--card-grid-card">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/16_9_320x180/public/2025-09/FY26_BeTheFuture-HERO_professor-with-students.jpg?h=854a7be2&amp;itok=sAJtETQj" class=" lazyloaded" alt="A professor walks with students in the rooftop garden of the Olver Design Building." src="/UMass%20Amherst%20_%20UMass%20Amherst_files/FY26_BeTheFuture-HERO_professor-with-students.jpg">
</div>
<div class="text-container">
<h3 class="ally-focus-within"> Find a Program </h3>
<div data-component-id="umass_base:description" class="f--description">
<p>With more than 100 majors, you have the freedom to explore your interests and build an academic path that reflects who you are and where you want to go.</p>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/academics/explore-our-programs" class="button-text-link button-context-dark " aria-label="Read more About Find a Program" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Read more </span>
</a>
</div>
</div>
</div>
</div>
<div class="cc--component-container cc--card-grid-card ">
<div class="c--component c--card-grid-card">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/16_9_320x180/public/2025-11/UMass%20Amherst%20Drone%20Campus.jpeg?h=c673cd1c&amp;itok=SB-3t1IF" class=" lazyloaded" alt="UMass Amherst Drone Campus" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/UMass Amherst Drone Campus.jpeg">
</div>
<div class="text-container">
<h3 class="ally-focus-within"> Design Your Own Major </h3>
<div data-component-id="umass_base:description" class="f--description">
<p>Through the Bachelors Degree with Individual Concentration (BDIC) Program, students can create a personalized major, blending courses across fields to explore passions beyond traditional academic boundaries.</p>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/umass-stories/design-your-own-major" class="button-text-link button-context-dark " aria-label="Read more About Design Your Own Major" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Read more </span>
</a>
</div>
</div>
</div>
</div>
<div class="cc--component-container cc--card-grid-card ">
<div class="c--component c--card-grid-card">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/16_9_320x180/public/2024-01/FY23-Study-abroad-LANDING-HERO-London-SA_0.jpg?h=854a7be2&amp;itok=KyMO_bgM" class=" lazyloaded" alt="UMass Amherst Study Abroad London" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/FY23-Study-abroad-LANDING-HERO-London-SA_0.jpg">
</div>
<div class="text-container">
<h3 class="ally-focus-within"> Study Abroad </h3>
<div data-component-id="umass_base:description" class="f--description">
<p>Global learning opportunities immerse students in new cultures, expanding their perspectives and preparing them to thrive in a connected world.</p>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/umass-stories/study-abroad-umass" class="button-text-link button-context-dark " aria-label="Read more About Study Abroad" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Read more </span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<style>* { box-sizing: border-box; } body {margin: 0;}</style>
@@ -0,0 +1,34 @@
<body>
<div data-component-id="umass_base:block-multiple-ctas" data-background="solid" data-link-style="buttons" data-layout-variant="default">
<div class="container">
<div class="text-container">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>APPLY TODAY</h2>
</div>
</div>
<div class="links-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/first-year-students" class="button-primary button-context-light " aria-label="First Year Apply to UMass Undergraduate" target="_self" data-component-id="umass_base:button">
<span class="button-text"> First Year </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/transfer-students" class="button-primary button-context-light " aria-label="Transfer Apply to UMass as as Transfer Student" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Transfer </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/undergraduate-admissions/apply/international-students" class="button-primary button-context-light " aria-label="International Apply to UMass Internationally" target="_self" data-component-id="umass_base:button">
<span class="button-text"> International </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/graduate/apply" class="button-primary button-context-light " aria-label="Graduate School Apply to UMass Graduate School" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Graduate School </span>
</a>
</div>
</div>
</div>
</div>
</body>
<style>* { box-sizing: border-box; } body {margin: 0;}</style>
@@ -0,0 +1,207 @@
<body>
<div data-component-id="umass_base:block-multiple-ctas" data-background="solid" data-color="gray" data-layout-variant="tabbed">
<div class="container">
<div class="text-container">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>I'm Looking For...</h2>
</div>
</div>
<div class="links-container">
<div data-component-id="umass_base:tabbed-container" data-color="gray" data-once="tabbed-container">
<div class="tab-labels-container" role="tablist">
<div class="tab-labels-inner">
<a href="https://www.umass.edu/#tabbed-content-prospective-students" class="tab-label link-context-light" role="tab" aria-selected="true" aria-controls="tabbed-content-prospective-students">Prospective Students</a>
<a href="https://www.umass.edu/#tabbed-content-current-students" class="tab-label link-context-light" role="tab" aria-selected="false" aria-controls="tabbed-content-current-students">Current Students</a>
<a href="https://www.umass.edu/#tabbed-content-parents--families" class="tab-label link-context-light" role="tab" aria-selected="false" aria-controls="tabbed-content-parents--families">Parents &amp; Families</a>
<a href="https://www.umass.edu/#tabbed-content-faculty--staff" class="tab-label link-context-light" role="tab" aria-selected="false" aria-controls="tabbed-content-faculty--staff">Faculty &amp; Staff</a>
</div>
</div>
<div class="tab-content-container">
<div class="tab-content-panel is-active" id="tabbed-content-prospective-students" role="tabpanel" aria-live="polite">
<div class="tabbed-links-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/undergraduate-admissions/visit" class="button-text-link button-context-light " aria-label="Visit Campus" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Visit Campus </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/undergraduate-admissions" class="button-text-link button-context-light " aria-label="Apply as a First-Year Student" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Apply as a First-Year Student </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/academics/explore-our-programs?field_level_of_study=36&amp;field_degree_type=All&amp;field_delivery=All&amp;field_area_of_interest=All&amp;field_school=All&amp;sort_by=title" class="button-text-link button-context-light " aria-label="Majors &amp; Minors" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Majors &amp; Minors </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/transfer-students" class="button-text-link button-context-light " aria-label="Apply as a Transfer Student" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Apply as a Transfer Student </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/graduate/apply" class="button-text-link button-context-light " aria-label="Apply as a Graduate Student" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Apply as a Graduate Student </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/important-dates-deadlines" class="button-text-link button-context-light " aria-label="Important Dates &amp; Deadlines" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Important Dates &amp; Deadlines </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/financialaid/undergraduate-costs" class="button-text-link button-context-light " aria-label="Tuition &amp; Costs" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Tuition &amp; Costs </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/financialaid/undergraduate" class="button-text-link button-context-light " aria-label="Financial Aid" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Financial Aid </span>
</a>
</div>
</div>
</div>
<div class="tab-content-panel" id="tabbed-content-current-students" role="tabpanel" aria-live="polite">
<div class="tabbed-links-container">
<div class="f--field f--button">
<a href="https://www.spire.umass.edu/" class="button-text-link button-context-light " aria-label="SPIRE" target="_self" data-component-id="umass_base:button">
<span class="button-text"> SPIRE </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/it/canvas" class="button-text-link button-context-light " aria-label="Canvas" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Canvas </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/registrar/academic-calendar" class="button-text-link button-context-light " aria-label="Academic Calendar" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Academic Calendar </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/financialaid/student-employment" class="button-text-link button-context-light " aria-label="Student Jobs" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Student Jobs </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/contact-us" class="button-text-link button-context-light " aria-label="Contact List" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Contact List </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/bursar/" class="button-text-link button-context-light " aria-label="Bursar&#39;s Office" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Bursar's Office </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/student-life/live-on-campus" class="button-text-link button-context-light " aria-label="Residential Life" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Residential Life </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/it/it-guide-students" class="button-text-link button-context-light " aria-label="Technology Support" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Technology Support </span>
</a>
</div>
</div>
</div>
<div class="tab-content-panel" id="tabbed-content-parents--families" role="tabpanel" aria-live="polite">
<div class="tabbed-links-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/registrar/academic-calendar" class="button-text-link button-context-light " aria-label="Academic Calendar" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Academic Calendar </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/visit/visitor-info" class="button-text-link button-context-light " aria-label="Campus Maps" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Campus Maps </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://events.umass.edu/" class="button-text-link button-context-light " aria-label="Campus Events" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Campus Events </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/new-student/family-ready-u" class="button-text-link button-context-light " aria-label="New Families" target="_self" data-component-id="umass_base:button">
<span class="button-text"> New Families </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/new-student/family-programs" class="button-text-link button-context-light " aria-label="Family Programs" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Family Programs </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/contact-us" class="button-text-link button-context-light " aria-label="Contact List" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Contact List </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/bursar/" class="button-text-link button-context-light " aria-label="Bursar&#39;s Office" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Bursar's Office </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/financialaid/undergraduate" class="button-text-link button-context-light " aria-label="Financial Aid" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Financial Aid </span>
</a>
</div>
</div>
</div>
<div class="tab-content-panel" id="tabbed-content-faculty--staff" role="tabpanel" aria-live="polite">
<div class="tabbed-links-container">
<div class="f--field f--button">
<a href="https://www.spire.umass.edu/" class="button-text-link button-context-light " aria-label="SPIRE" target="_self" data-component-id="umass_base:button">
<span class="button-text"> SPIRE </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/it/canvas" class="button-text-link button-context-light " aria-label="Canvas" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Canvas </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/peoplefinder/" class="button-text-link button-context-light " aria-label="People Finder" target="_self" data-component-id="umass_base:button">
<span class="button-text"> People Finder </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/contact-us" class="button-text-link button-context-light " aria-label="Contact List" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Contact List </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://hr-direct.hcm.umasscs.net/psc/hcm/EMPLOYEE/HRMS/c/NUI_FRAMEWORK.PT_LANDINGPAGE.GBL" class="button-text-link button-context-light ext" aria-label="HR Direct" target="_self" data-component-id="umass_base:button" data-extlink="">
<span class="button-text"> HR Direct </span>
<svg focusable="false" width="1em" height="1em" class="ext" data-extlink-placement="append" aria-label="(link is external)" viewBox="0 0 80 40" role="img" aria-hidden="false">
<title>(link is external)</title>
<path d="M48 26c-1.1 0-2 0.9-2 2v26H10V18h26c1.1 0 2-0.9 2-2s-0.9-2-2-2H8c-1.1 0-2 0.9-2 2v40c0 1.1 0.9 2 2 2h40c1.1 0 2-0.9 2-2V28C50 26.9 49.1 26 48 26z"></path>
<path d="M56 6H44c-1.1 0-2 0.9-2 2s0.9 2 2 2h7.2L30.6 30.6c-0.8 0.8-0.8 2 0 2.8C31 33.8 31.5 34 32 34s1-0.2 1.4-0.6L54 12.8V20c0 1.1 0.9 2 2 2s2-0.9 2-2V8C58 6.9 57.1 6 56 6z"></path>
</svg>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/hr/" class="button-text-link button-context-light " aria-label="Human Resources" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Human Resources </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/hr/careers" class="button-text-link button-context-light " aria-label="Career Opportunities" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Career Opportunities </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/it/email" class="button-text-link button-context-light " aria-label="Email Support" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Email Support </span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<style>* { box-sizing: border-box; } body {margin: 0;}</style>
@@ -0,0 +1,218 @@
<div class="cc--component-container cc--news-events">
<div class="c--component c--news-events">
<!-- Phần 2 cột: Tin mới (Trái) và Sự kiện (Phải) -->
<div class="news-events-row">
<div class="news-column">
<h2 class="section-title--news" style="font-size: 2rem;">
TIN TỨC
</h2>
<div class="news-slider-wrapper">
<button class="news-slider-btn prev-btn" aria-label="Previous">&lsaquo;</button>
<div class="news-slider-container">
<div class="news-slider-track">
<!-- Shortcode Tin Tức hiển thị dạng list (limit tăng lên 6 để có thể trượt) -->
<th:block th:utext="${componentTemplateService.render('news-grid', 'posts:tag=news,limit=6,collection=news_posts')}"></th:block>
</div>
</div>
<button class="news-slider-btn next-btn" aria-label="Next">&rsaquo;</button>
</div>
</div>
<div class="events-column">
<h2 class="events-title" style="font-size: 2rem;">
SỰ KIỆN
</h2>
<div class="events-widget">
<!-- Shortcode Sự kiện -->
<th:block th:utext="${componentTemplateService.render('events-list', 'posts:tag=event,limit=4,collection=event_posts')}"></th:block>
</div>
</div>
</div>
<!-- Header chung (Mobile) -->
<div class="news-events-section__footer">
<a href="/news/news-events" class="news-events-section__cta news-events-section__cta--mobile">
Xem Thêm
</a>
</div>
</div>
</div>
<style>
.news-slider-wrapper {
position: relative;
padding: 0px;
}
.news-slider-container {
overflow: hidden;
width: 100%;
}
.news-slider-wrapper .news-grid,
.news-slider-wrapper .row,
.news-slider-track {
display: flex !important;
flex-wrap: nowrap !important;
gap: 20px;
margin: 0 !important;
transition: transform 0.4s ease;
}
/* Force children to be fixed width cards - 2 cards at a time because this column is narrow */
.news-slider-wrapper .news-grid>*,
.news-slider-wrapper .row>*,
.news-slider-track>* {
flex: 0 0 calc(50% - 10px) !important;
max-width: none !important;
}
@media (max-width: 992px) {
.news-slider-wrapper .news-grid>*,
.news-slider-wrapper .row>*,
.news-slider-track>* {
flex: 0 0 calc(50% - 10px) !important;
}
}
@media (max-width: 576px) {
.news-slider-wrapper .news-grid>*,
.news-slider-wrapper .row>*,
.news-slider-track>* {
flex: 0 0 100% !important;
}
}
.news-slider-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
border-radius: 50%;
background-color: rgba(200, 200, 200, 0.5);
color: #333;
border: none;
font-size: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
transition: background-color 0.2s, color 0.2s;
line-height: 1;
}
.news-slider-btn:hover {
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
}
.news-slider-btn.prev-btn {
left: -15px;
}
.news-slider-btn.next-btn {
right: -15px;
}
.news-slider-btn:disabled {
opacity: 0.2;
cursor: not-allowed;
}
/* --- Hiệu ứng crop (zoom) ảnh khi hover --- */
/* 1. Thiết lập vùng chứa ảnh sẽ cắt bỏ phần bị lồi ra (overflow: hidden) */
.news-slider-wrapper .featured-grid__item .news-card__image {
overflow: hidden !important;
display: block;
}
/* 2. Thiết lập thời gian chuyển động cho ảnh */
.news-slider-wrapper .featured-grid__item .news-card__image img {
transition: transform 0.4s ease-in-out !important;
}
/* 3. Phóng to ảnh (scale) khi trỏ chuột vào vùng chứa */
.news-slider-wrapper .featured-grid__item:hover .news-card__image img {
transform: scale(1.1) !important;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const wrappers = document.querySelectorAll('.news-slider-wrapper');
wrappers.forEach(wrapper => {
const prevBtn = wrapper.querySelector('.prev-btn');
const nextBtn = wrapper.querySelector('.next-btn');
let track = wrapper.querySelector('.news-slider-track');
if (track && track.children.length === 1 && (track.children[0].classList.contains('news-grid') || track.children[0].classList.contains('row'))) {
track = track.children[0];
track.classList.add('news-slider-track');
}
if (!track) return;
let currentIndex = 0;
function updateSlider() {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
track.style.transform = `translateX(-${currentIndex * (itemWidth + gap)}px)`;
const containerWidth = wrapper.querySelector('.news-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
prevBtn.disabled = currentIndex === 0;
nextBtn.disabled = currentIndex >= items.length - visibleCount;
}
prevBtn.addEventListener('click', () => {
if (currentIndex > 0) {
currentIndex--;
updateSlider();
}
});
nextBtn.addEventListener('click', () => {
const items = track.children;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.news-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex < items.length - visibleCount) {
currentIndex++;
updateSlider();
}
});
window.addEventListener('resize', () => {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.news-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex > items.length - visibleCount) {
currentIndex = Math.max(0, items.length - visibleCount);
}
updateSlider();
});
setTimeout(updateSlider, 200);
});
});
</script>
@@ -0,0 +1,970 @@
<body>
<div class="content-main">
<div class="lc--layout-container lc--full">
<div class="l--layout l--full">
<div class="lr--layout-region lr--main">
<div data-component-id="umass_base:block-multiple-ctas" data-background="solid" data-layout-variant="default">
<div class="container">
<div class="text-container">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>Step Into <span class="highlight">Your Future</span>
</h2>
</div>
</div>
<div class="links-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/apply" class="button-text-link button-context-light " aria-label="Apply" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Apply </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/academics/explore-our-programs" class="button-text-link button-context-light " aria-label="Majors &amp; Minors" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Majors &amp; Minors </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/undergraduate-admissions/costs-aid" class="button-text-link button-context-light " aria-label="Tuition &amp; Costs" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Tuition &amp; Costs </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/visit" class="button-text-link button-context-light " aria-label="Take a Tour" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Take a Tour </span>
</a>
</div>
</div>
</div>
</div>
<div class="cc--component-container cc--tabbed-media-content ">
<div class="c--component c--tabbed-media-content">
<div class="tabbed-media-content-text-col">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>What's Life Like at UMass?</h2>
</div>
<div data-component-id="umass_base:tabbed-container" data-once="tabbed-container">
<div class="tab-labels-container" role="tablist">
<div class="tab-labels-inner">
<a href="https://www.umass.edu/#tabbed-content-campus-life" class="tab-label link-context-dark" role="tab" aria-selected="true" aria-controls="tabbed-content-campus-life">Campus Life</a>
<a href="https://www.umass.edu/#tabbed-content-campus-housing" class="tab-label link-context-dark" role="tab" aria-selected="false" aria-controls="tabbed-content-campus-housing">Campus Housing</a>
<a href="https://www.umass.edu/#tabbed-content-dining" class="tab-label link-context-dark" role="tab" aria-selected="false" aria-controls="tabbed-content-dining">Dining</a>
<a href="https://www.umass.edu/#tabbed-content-around-amherst" class="tab-label link-context-dark" role="tab" aria-selected="false" aria-controls="tabbed-content-around-amherst">Around Amherst</a>
</div>
</div>
<div class="tab-content-container">
<div class="tab-content-panel is-active" id="tabbed-content-campus-life" role="tabpanel" aria-live="polite">
<div class="f--field f--cta-title">
<h3> Make UMass Your Own </h3>
</div>
<div data-component-id="umass_base:description" class="f--description">
<p>Explore big ideas, join vibrant communities, and shape a college experience thats uniquely yours.</p>
</div>
<div class="cta-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/campus-life/student-activities" class="button-primary button-context-light " aria-label="Learn About Student Activities" data-component-id="umass_base:button">
<span class="button-text"> Learn About Student Activities </span>
</a>
</div>
</div>
</div>
<div class="tab-content-panel" id="tabbed-content-campus-housing" role="tabpanel" aria-live="polite">
<div class="f--field f--cta-title">
<h3> Your Campus Home </h3>
</div>
<div data-component-id="umass_base:description" class="f--description">
<p>Choose from vibrant residential communities where students live, study, and build lasting friendships steps from the classroom.</p>
</div>
<div class="cta-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/campus-life/living-and-dining" class="button-primary button-context-light " aria-label="Learn About Living at UMass" data-component-id="umass_base:button">
<span class="button-text"> Learn About Living at UMass </span>
</a>
</div>
</div>
</div>
<div class="tab-content-panel" id="tabbed-content-dining" role="tabpanel" aria-live="polite">
<div class="f--field f--cta-title">
<h3> Food Worth Talking About </h3>
</div>
<div data-component-id="umass_base:description" class="f--description">
<p>Discover why UMass dining is ranked #1 nationally—recognized for quality, variety, and sustainability.</p>
</div>
<div class="cta-container">
<div class="f--field f--button">
<a href="https://www.umassdining.com/" class="button-primary button-context-light ext" aria-label="Learn Why UMass is #1 in Dining" data-component-id="umass_base:button" data-extlink="">
<span class="button-text"> Learn Why UMass is #1 in Dining </span>
<svg focusable="false" width="1em" height="1em" class="ext" data-extlink-placement="append" aria-label="(link is external)" viewBox="0 0 80 40" role="img" aria-hidden="false">
<title>(link is external)</title>
<path d="M48 26c-1.1 0-2 0.9-2 2v26H10V18h26c1.1 0 2-0.9 2-2s-0.9-2-2-2H8c-1.1 0-2 0.9-2 2v40c0 1.1 0.9 2 2 2h40c1.1 0 2-0.9 2-2V28C50 26.9 49.1 26 48 26z"></path>
<path d="M56 6H44c-1.1 0-2 0.9-2 2s0.9 2 2 2h7.2L30.6 30.6c-0.8 0.8-0.8 2 0 2.8C31 33.8 31.5 34 32 34s1-0.2 1.4-0.6L54 12.8V20c0 1.1 0.9 2 2 2s2-0.9 2-2V8C58 6.9 57.1 6 56 6z"></path>
</svg>
</a>
</div>
</div>
</div>
<div class="tab-content-panel" id="tabbed-content-around-amherst" role="tabpanel" aria-live="polite">
<div class="f--field f--cta-title">
<h3> Amherst, Uncommon </h3>
</div>
<div data-component-id="umass_base:description" class="f--description">
<p>A vibrant creative culture, great food, and the natural beauty of the Pioneer Valley come together to shape this towns distinctive, welcoming spirit.</p>
</div>
<div class="cta-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/student-life/life-amherst" class="button-primary button-context-light " aria-label="Learn More About Amherst" data-component-id="umass_base:button">
<span class="button-text"> Learn More About Amherst </span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="tabbed-media-content-media-col">
<div class="tabbed-media-content-media-item is-active with-video" data-tab-id="tabbed-content-campus-life">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/9_16_720x1280/public/2026-03/medium_jpg-2026_Vertical_Assets_5179_John_Solem_.jpg?h=7c5befa5&amp;itok=DZUe-3fS" class=" ls-is-cached lazyloaded" alt="A UMass student plays ping pong." src="/UMass%20Amherst%20_%20UMass%20Amherst_files/medium_jpg-2026_Vertical_Assets_5179_John_Solem_.jpg">
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/#" class="button-play button-context-light " aria-label="Play video" data-component-id="umass_base:button">
<span class="button-icon button-icon-play">
<svg width="18" height="27" viewBox="0 0 18 27" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M18 13.5L0 0V27L18 13.5Z"></path>
</svg>
</span>
<span class="button-text visually-hidden"> Play </span>
</a>
</div>
<div data-component-id="umass_base:video-embed" class="f--field f--video-embed">
<iframe title="Video Content" id="video-132086-1" width="900" height="1600" src="https://www.youtube.com/embed/q0ZxZVbnMqs" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
</div>
<div class="tabbed-media-content-media-item with-video" data-tab-id="tabbed-content-campus-housing">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/9_16_720x1280/public/2026-03/FY26_Vertical-Campus-Housing.jpg?h=75081741&amp;itok=6kDcYqy3" class="lazyload" alt="A UMass student sits on her bed in a dorm room.">
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/#" class="button-play button-context-light " aria-label="Play video" data-component-id="umass_base:button">
<span class="button-icon button-icon-play">
<svg width="18" height="27" viewBox="0 0 18 27" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M18 13.5L0 0V27L18 13.5Z"></path>
</svg>
</span>
<span class="button-text visually-hidden"> Play </span>
</a>
</div>
<div data-component-id="umass_base:video-embed" class="f--field f--video-embed">
<iframe title="Video Content" id="video-132086-2" width="900" height="1600" src="https://www.youtube.com/embed/7D_TnBztQhU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
</div>
<div class="tabbed-media-content-media-item with-video" data-tab-id="tabbed-content-dining">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/9_16_720x1280/public/2026-03/FY26_Vertical-Dining2.jpg?h=75081741&amp;itok=_34BDcqA" class="lazyload" alt="A student gets food at a UMass Amherst dining hall.">
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/#" class="button-play button-context-light " aria-label="Play video" data-component-id="umass_base:button">
<span class="button-icon button-icon-play">
<svg width="18" height="27" viewBox="0 0 18 27" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M18 13.5L0 0V27L18 13.5Z"></path>
</svg>
</span>
<span class="button-text visually-hidden"> Play </span>
</a>
</div>
<div data-component-id="umass_base:video-embed" class="f--field f--video-embed">
<iframe title="Video Content" id="video-132086-3" width="900" height="1600" src="https://www.youtube.com/embed/YRzZ9aRZ69w" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
</div>
<div class="tabbed-media-content-media-item with-video" data-tab-id="tabbed-content-around-amherst">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/9_16_720x1280/public/2026-03/FY26_Vertical-Downtown-Amherst.jpg?h=75081741&amp;itok=irWjha4q" class="lazyload" alt="A UMass student holds a hote bag that reads &quot;I Love Amherst.&quot;">
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/#" class="button-play button-context-light " aria-label="Play video" data-component-id="umass_base:button">
<span class="button-icon button-icon-play">
<svg width="18" height="27" viewBox="0 0 18 27" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M18 13.5L0 0V27L18 13.5Z"></path>
</svg>
</span>
<span class="button-text visually-hidden"> Play </span>
</a>
</div>
<div data-component-id="umass_base:video-embed" class="f--field f--video-embed">
<iframe title="Video Content" id="video-132086-4" width="900" height="1600" src="https://www.youtube.com/embed/agqIJlJpbAY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
</div>
</div>
</div>
</div>
<div class="cc--component-container cc--stats-block ">
<div class="c--component c--stats-block">
<div class="f--field f--stats-block">
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div class="f--field f--stat-number">#1</div>
<div class="f--field f--description">
<p>Public Research University in New England, <br>Non-Medical School R&amp;D Expenditures </p>
</div>
</div>
</div>
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div class="f--field f--stat-number">251</div>
<div class="f--field f--description">
<p>Total Degree Programs</p>
</div>
</div>
</div>
<div class="cc--component-container cc--stat">
<div class="c--component c--stat">
<div class="f--field f--stat-number">Top 50</div>
<div class="f--field f--description">
<p>Best Value Colleges (Public), <em>The Princeton Review</em>
</p>
</div>
</div>
</div>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/why-umass/about-umass-amherst/umass-amherst-numbers" class="button-primary button-context-light " aria-label="UMass Amherst By The Numbers" data-component-id="umass_base:button">
<span class="button-text"> UMass Amherst By The Numbers </span>
</a>
</div>
</div>
</div>
<div class="paragraph paragraph--type--quad-card-grid paragraph--view-mode--default paragraph--quad-card-grid" data-component-id="umass_base:image-link-grid">
<div class="qcg">
<div class="qcg__left">
<h2 class="qcg__title"> Excellence in Action </h2>
<div class="qcg__description">
<div>
<p>UMass Amherst advances academic excellence through research, teaching, and engagement across campus. Students and faculty drive discovery through programs, scholarship, and events that extend beyond the classroom.</p>
</div>
</div>
<div class="qcg__link">
<a href="https://www.umass.edu/gateway/why-umass/discover-ideas" class="ext" target="_self" aria-label="Discover Ideas Discover Ideas" rel="noopener noreferrer"> Discover Ideas </a>
</div>
</div>
<div class="qcg__right">
<div class="qcg__grid">
<div class="qcg__card">
<div class="paragraph paragraph--type--quad-card-grid-item paragraph--view-mode--default paragraph--quad-card-grid-item qcg-item" data-component-id="umass_base:image-link-grid-item">
<div class="qcg-item__image">
<div>
<article>
<div>
<img loading="lazy" srcset="https://www.umass.edu/sites/default/files/styles/4_3_400x300/public/2025-12/FY26UMass_HackUMass_LANDING.jpg?h=854a7be2&amp;itok=ysOMEN8V 400w, https://www.umass.edu/sites/default/files/styles/4_3_800x600/public/2025-12/FY26UMass_HackUMass_LANDING.jpg?h=854a7be2&amp;itok=jtWz1GCX 800w, https://www.umass.edu/sites/default/files/styles/4_3_1600x1200/public/2025-12/FY26UMass_HackUMass_LANDING.jpg?h=854a7be2&amp;itok=xAxf5s8X 1600w, https://www.umass.edu/sites/default/files/styles/4_3_1920x1440/public/2025-12/FY26UMass_HackUMass_LANDING.jpg?h=854a7be2&amp;itok=3yPPf8f1 1920w" sizes="100vw" width="400" height="300" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/FY26UMass_HackUMass_LANDING.jpg" alt="A group of five students lean over laptops in a classroom">
</div>
</article>
</div>
</div>
<div class="qcg-item__link">
<a href="https://www.umass.edu/gateway/umass-stories/hackumass" class="qcg-item__cta" target="_self" aria-label="HackUMass: Code, Collaborations &amp; Big Ideas HackUMass: Code, Collaborations &amp; Big Ideas"> HackUMass: Code, Collaborations &amp; Big Ideas </a>
</div>
</div>
</div>
<div class="qcg__card">
<div class="paragraph paragraph--type--quad-card-grid-item paragraph--view-mode--default paragraph--quad-card-grid-item qcg-item" data-component-id="umass_base:image-link-grid-item">
<div class="qcg-item__image">
<div>
<article>
<div>
<img loading="lazy" srcset="https://www.umass.edu/sites/default/files/styles/4_3_400x300/public/2025-06/UPC2025__HERO_2000x1125.jpg?h=0f182455&amp;itok=4PUBT11w 400w, https://www.umass.edu/sites/default/files/styles/4_3_800x600/public/2025-06/UPC2025__HERO_2000x1125.jpg?h=0f182455&amp;itok=r72eJsZX 800w, https://www.umass.edu/sites/default/files/styles/4_3_1600x1200/public/2025-06/UPC2025__HERO_2000x1125.jpg?h=0f182455&amp;itok=HXie2chE 1600w, https://www.umass.edu/sites/default/files/styles/4_3_1920x1440/public/2025-06/UPC2025__HERO_2000x1125.jpg?h=0f182455&amp;itok=ufOsI9X8 1920w" sizes="100vw" width="400" height="300" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/UPC2025__HERO_2000x1125.jpg" alt="Tinashe performs at the Spring Concert.">
</div>
</article>
</div>
</div>
<div class="qcg-item__link">
<a href="https://www.umass.edu/gateway/umass-stories/inside-upc" class="qcg-item__cta" target="_self" aria-label="Inside UPC: Student-Led Events With Lasting Impact Inside UPC: Student-Led Events With Lasting Impact"> Inside UPC: Student-Led Events With Lasting Impact </a>
</div>
</div>
</div>
<div class="qcg__card">
<div class="paragraph paragraph--type--quad-card-grid-item paragraph--view-mode--default paragraph--quad-card-grid-item qcg-item" data-component-id="umass_base:image-link-grid-item">
<div class="qcg-item__image">
<div>
<article>
<div>
<img loading="lazy" srcset="https://www.umass.edu/sites/default/files/styles/4_3_400x300/public/2025-09/FY26_BeTheFuture_WeAreRev_photo4.jpg?h=854a7be2&amp;itok=jVpB7bGa 400w, https://www.umass.edu/sites/default/files/styles/4_3_800x600/public/2025-09/FY26_BeTheFuture_WeAreRev_photo4.jpg?h=854a7be2&amp;itok=CcG3pX7V 800w, https://www.umass.edu/sites/default/files/styles/4_3_1600x1200/public/2025-09/FY26_BeTheFuture_WeAreRev_photo4.jpg?h=854a7be2&amp;itok=mDWs6wTt 1600w, https://www.umass.edu/sites/default/files/styles/4_3_1920x1440/public/2025-09/FY26_BeTheFuture_WeAreRev_photo4.jpg?h=854a7be2&amp;itok=cgBcz7LE 1920w" sizes="100vw" width="400" height="300" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/FY26_BeTheFuture_WeAreRev_photo4.jpg" alt="Two students work with building tools.">
</div>
</article>
</div>
</div>
<div class="qcg-item__link">
<a href="https://www.umass.edu/gateway/umass-stories/changemakers-making" class="qcg-item__cta" target="_self" aria-label="Changemakers in the Making: Be the Future You Want to See Changemakers in the Making: Students Aren&#39;t Just Waiting for the Future, They&#39;re Making It"> Changemakers in the Making: Be the Future You Want to See </a>
</div>
</div>
</div>
<div class="qcg__card">
<div class="paragraph paragraph--type--quad-card-grid-item paragraph--view-mode--default paragraph--quad-card-grid-item qcg-item" data-component-id="umass_base:image-link-grid-item">
<div class="qcg-item__image">
<div>
<article>
<div>
<img loading="lazy" srcset="https://www.umass.edu/sites/default/files/styles/4_3_400x300/public/2025-12/2025_Vy_Do_7780_John_Solem_2000x1125.jpg?h=854a7be2&amp;itok=Idf4wkAD 400w, https://www.umass.edu/sites/default/files/styles/4_3_800x600/public/2025-12/2025_Vy_Do_7780_John_Solem_2000x1125.jpg?h=854a7be2&amp;itok=WUqiETKc 800w, https://www.umass.edu/sites/default/files/styles/4_3_1600x1200/public/2025-12/2025_Vy_Do_7780_John_Solem_2000x1125.jpg?h=854a7be2&amp;itok=p7De2fBX 1600w, https://www.umass.edu/sites/default/files/styles/4_3_1920x1440/public/2025-12/2025_Vy_Do_7780_John_Solem_2000x1125.jpg?h=854a7be2&amp;itok=AEX7-hAe 1920w" sizes="100vw" width="400" height="300" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/2025_Vy_Do_7780_John_Solem_2000x1125.jpg" alt="Vy Do in UMas Amherst biomedical engineering lab">
</div>
</article>
</div>
</div>
<div class="qcg-item__link">
<a href="https://www.umass.edu/gateway/research/stories/rising-researchers/biomedical-engineering-nanomedicine-drug-delivery" class="qcg-item__cta" target="_self" aria-label="Biomedical Engineering Research to Advance Drug Delivery Biomedical Engineering Research to Advance Drug Delivery: Vy Do 26 conducts research to develop nanocarriers for therapeutic genetic materials to treat traumatic brain injuries and other neurological conditions."> Biomedical Engineering Research to Advance Drug Delivery </a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="cc--component-container cc--card-grid dark">
<div class="c--component c--card-grid">
<div class="text-container">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>Blaze Your Own Trail</h2>
</div>
<div data-component-id="umass_base:description" class="f--section-title f--description">
<p>Discover more than 100 undergraduate majors, minors, and certificates in fields ranging from engineering to public health to literature. You can even design your own major through our individual concentration program. Whatever your path, UMass gives you the freedom to explore and the tools to excel.</p>
</div>
</div>
<div class="f--field f--cards nested">
<div class="cc--component-container cc--card-grid-card ">
<div class="c--component c--card-grid-card">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/16_9_320x180/public/2025-09/FY26_BeTheFuture-HERO_professor-with-students.jpg?h=854a7be2&amp;itok=sAJtETQj" class=" lazyloaded" alt="A professor walks with students in the rooftop garden of the Olver Design Building." src="/UMass%20Amherst%20_%20UMass%20Amherst_files/FY26_BeTheFuture-HERO_professor-with-students.jpg">
</div>
<div class="text-container">
<h3 class="ally-focus-within"> Find a Program </h3>
<div data-component-id="umass_base:description" class="f--description">
<p>With more than 100 majors, you have the freedom to explore your interests and build an academic path that reflects who you are and where you want to go.</p>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/academics/explore-our-programs" class="button-text-link button-context-dark " aria-label="Read more About Find a Program" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Read more </span>
</a>
</div>
</div>
</div>
</div>
<div class="cc--component-container cc--card-grid-card ">
<div class="c--component c--card-grid-card">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/16_9_320x180/public/2025-11/UMass%20Amherst%20Drone%20Campus.jpeg?h=c673cd1c&amp;itok=SB-3t1IF" class=" lazyloaded" alt="UMass Amherst Drone Campus" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/UMass Amherst Drone Campus.jpeg">
</div>
<div class="text-container">
<h3 class="ally-focus-within"> Design Your Own Major </h3>
<div data-component-id="umass_base:description" class="f--description">
<p>Through the Bachelors Degree with Individual Concentration (BDIC) Program, students can create a personalized major, blending courses across fields to explore passions beyond traditional academic boundaries.</p>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/umass-stories/design-your-own-major" class="button-text-link button-context-dark " aria-label="Read more About Design Your Own Major" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Read more </span>
</a>
</div>
</div>
</div>
</div>
<div class="cc--component-container cc--card-grid-card ">
<div class="c--component c--card-grid-card">
<div class="f--field f--image">
<img data-src="https://www.umass.edu/sites/default/files/styles/16_9_320x180/public/2024-01/FY23-Study-abroad-LANDING-HERO-London-SA_0.jpg?h=854a7be2&amp;itok=KyMO_bgM" class=" lazyloaded" alt="UMass Amherst Study Abroad London" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/FY23-Study-abroad-LANDING-HERO-London-SA_0.jpg">
</div>
<div class="text-container">
<h3 class="ally-focus-within"> Study Abroad </h3>
<div data-component-id="umass_base:description" class="f--description">
<p>Global learning opportunities immerse students in new cultures, expanding their perspectives and preparing them to thrive in a connected world.</p>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/umass-stories/study-abroad-umass" class="button-text-link button-context-dark " aria-label="Read more About Study Abroad" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Read more </span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div data-component-id="umass_base:block-multiple-ctas" data-background="solid" data-link-style="buttons" data-layout-variant="default">
<div class="container">
<div class="text-container">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>APPLY TODAY</h2>
</div>
</div>
<div class="links-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/first-year-students" class="button-primary button-context-light " aria-label="First Year Apply to UMass Undergraduate" target="_self" data-component-id="umass_base:button">
<span class="button-text"> First Year </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/transfer-students" class="button-primary button-context-light " aria-label="Transfer Apply to UMass as as Transfer Student" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Transfer </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/undergraduate-admissions/apply/international-students" class="button-primary button-context-light " aria-label="International Apply to UMass Internationally" target="_self" data-component-id="umass_base:button">
<span class="button-text"> International </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/graduate/apply" class="button-primary button-context-light " aria-label="Graduate School Apply to UMass Graduate School" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Graduate School </span>
</a>
</div>
</div>
</div>
</div>
<div data-component-id="umass_base:block-multiple-ctas" data-background="solid" data-color="gray" data-layout-variant="tabbed">
<div class="container">
<div class="text-container">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>I'm Looking For...</h2>
</div>
</div>
<div class="links-container">
<div data-component-id="umass_base:tabbed-container" data-color="gray" data-once="tabbed-container">
<div class="tab-labels-container" role="tablist">
<div class="tab-labels-inner">
<a href="https://www.umass.edu/#tabbed-content-prospective-students" class="tab-label link-context-light" role="tab" aria-selected="true" aria-controls="tabbed-content-prospective-students">Prospective Students</a>
<a href="https://www.umass.edu/#tabbed-content-current-students" class="tab-label link-context-light" role="tab" aria-selected="false" aria-controls="tabbed-content-current-students">Current Students</a>
<a href="https://www.umass.edu/#tabbed-content-parents--families" class="tab-label link-context-light" role="tab" aria-selected="false" aria-controls="tabbed-content-parents--families">Parents &amp; Families</a>
<a href="https://www.umass.edu/#tabbed-content-faculty--staff" class="tab-label link-context-light" role="tab" aria-selected="false" aria-controls="tabbed-content-faculty--staff">Faculty &amp; Staff</a>
</div>
</div>
<div class="tab-content-container">
<div class="tab-content-panel is-active" id="tabbed-content-prospective-students" role="tabpanel" aria-live="polite">
<div class="tabbed-links-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/undergraduate-admissions/visit" class="button-text-link button-context-light " aria-label="Visit Campus" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Visit Campus </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/undergraduate-admissions" class="button-text-link button-context-light " aria-label="Apply as a First-Year Student" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Apply as a First-Year Student </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/academics/explore-our-programs?field_level_of_study=36&amp;field_degree_type=All&amp;field_delivery=All&amp;field_area_of_interest=All&amp;field_school=All&amp;sort_by=title" class="button-text-link button-context-light " aria-label="Majors &amp; Minors" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Majors &amp; Minors </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/transfer-students" class="button-text-link button-context-light " aria-label="Apply as a Transfer Student" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Apply as a Transfer Student </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/graduate/apply" class="button-text-link button-context-light " aria-label="Apply as a Graduate Student" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Apply as a Graduate Student </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/important-dates-deadlines" class="button-text-link button-context-light " aria-label="Important Dates &amp; Deadlines" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Important Dates &amp; Deadlines </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/financialaid/undergraduate-costs" class="button-text-link button-context-light " aria-label="Tuition &amp; Costs" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Tuition &amp; Costs </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/financialaid/undergraduate" class="button-text-link button-context-light " aria-label="Financial Aid" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Financial Aid </span>
</a>
</div>
</div>
</div>
<div class="tab-content-panel" id="tabbed-content-current-students" role="tabpanel" aria-live="polite">
<div class="tabbed-links-container">
<div class="f--field f--button">
<a href="https://www.spire.umass.edu/" class="button-text-link button-context-light " aria-label="SPIRE" target="_self" data-component-id="umass_base:button">
<span class="button-text"> SPIRE </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/it/canvas" class="button-text-link button-context-light " aria-label="Canvas" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Canvas </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/registrar/academic-calendar" class="button-text-link button-context-light " aria-label="Academic Calendar" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Academic Calendar </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/financialaid/student-employment" class="button-text-link button-context-light " aria-label="Student Jobs" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Student Jobs </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/contact-us" class="button-text-link button-context-light " aria-label="Contact List" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Contact List </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/bursar/" class="button-text-link button-context-light " aria-label="Bursar&#39;s Office" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Bursar's Office </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/student-life/live-on-campus" class="button-text-link button-context-light " aria-label="Residential Life" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Residential Life </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/it/it-guide-students" class="button-text-link button-context-light " aria-label="Technology Support" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Technology Support </span>
</a>
</div>
</div>
</div>
<div class="tab-content-panel" id="tabbed-content-parents--families" role="tabpanel" aria-live="polite">
<div class="tabbed-links-container">
<div class="f--field f--button">
<a href="https://www.umass.edu/registrar/academic-calendar" class="button-text-link button-context-light " aria-label="Academic Calendar" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Academic Calendar </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/admissions/visit/visitor-info" class="button-text-link button-context-light " aria-label="Campus Maps" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Campus Maps </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://events.umass.edu/" class="button-text-link button-context-light " aria-label="Campus Events" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Campus Events </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/new-student/family-ready-u" class="button-text-link button-context-light " aria-label="New Families" target="_self" data-component-id="umass_base:button">
<span class="button-text"> New Families </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/new-student/family-programs" class="button-text-link button-context-light " aria-label="Family Programs" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Family Programs </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/contact-us" class="button-text-link button-context-light " aria-label="Contact List" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Contact List </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/bursar/" class="button-text-link button-context-light " aria-label="Bursar&#39;s Office" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Bursar's Office </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/financialaid/undergraduate" class="button-text-link button-context-light " aria-label="Financial Aid" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Financial Aid </span>
</a>
</div>
</div>
</div>
<div class="tab-content-panel" id="tabbed-content-faculty--staff" role="tabpanel" aria-live="polite">
<div class="tabbed-links-container">
<div class="f--field f--button">
<a href="https://www.spire.umass.edu/" class="button-text-link button-context-light " aria-label="SPIRE" target="_self" data-component-id="umass_base:button">
<span class="button-text"> SPIRE </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/it/canvas" class="button-text-link button-context-light " aria-label="Canvas" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Canvas </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/peoplefinder/" class="button-text-link button-context-light " aria-label="People Finder" target="_self" data-component-id="umass_base:button">
<span class="button-text"> People Finder </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/contact-us" class="button-text-link button-context-light " aria-label="Contact List" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Contact List </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://hr-direct.hcm.umasscs.net/psc/hcm/EMPLOYEE/HRMS/c/NUI_FRAMEWORK.PT_LANDINGPAGE.GBL" class="button-text-link button-context-light ext" aria-label="HR Direct" target="_self" data-component-id="umass_base:button" data-extlink="">
<span class="button-text"> HR Direct </span>
<svg focusable="false" width="1em" height="1em" class="ext" data-extlink-placement="append" aria-label="(link is external)" viewBox="0 0 80 40" role="img" aria-hidden="false">
<title>(link is external)</title>
<path d="M48 26c-1.1 0-2 0.9-2 2v26H10V18h26c1.1 0 2-0.9 2-2s-0.9-2-2-2H8c-1.1 0-2 0.9-2 2v40c0 1.1 0.9 2 2 2h40c1.1 0 2-0.9 2-2V28C50 26.9 49.1 26 48 26z"></path>
<path d="M56 6H44c-1.1 0-2 0.9-2 2s0.9 2 2 2h7.2L30.6 30.6c-0.8 0.8-0.8 2 0 2.8C31 33.8 31.5 34 32 34s1-0.2 1.4-0.6L54 12.8V20c0 1.1 0.9 2 2 2s2-0.9 2-2V8C58 6.9 57.1 6 56 6z"></path>
</svg>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/hr/" class="button-text-link button-context-light " aria-label="Human Resources" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Human Resources </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/hr/careers" class="button-text-link button-context-light " aria-label="Career Opportunities" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Career Opportunities </span>
</a>
</div>
<div class="f--field f--button">
<a href="https://www.umass.edu/it/email" class="button-text-link button-context-light " aria-label="Email Support" target="_self" data-component-id="umass_base:button">
<span class="button-text"> Email Support </span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="cc--component-container cc--spacer ">
<div class="c--component c--spacer"></div>
</div>
<div class="cc--component-container cc--news-events ">
<div class="c--component c--news-events">
<div class="news-events-section__header">
<h2 class="news-events-section__main-title"> NEWS &amp; EVENTS </h2>
<a class="news-events-section__cta news-events-section__cta--desktop" href="https://www.umass.edu/news/news-events"> More News &amp; Events </a>
</div>
<h3 class="section-title"> FEATURED ARTICLES </h3>
<div class="featured-grid">
<div class="featured-grid__item">
<article class="news-card news-card--featured">
<div class="news-card__image">
<div>
<img loading="lazy" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/NEWS-Operating Room Surgery viaAdobeStock.jpeg" width="1280" height="720" alt="Medical Team Performing Surgical Operation in Modern Operating Room">
</div>
</div>
<div class="news-card__content">
<div class="featured-grid__category">
<a href="https://www.umass.edu/taxonomy/term/811" hreflang="en">Research</a>
</div>
<h3 class="news-card__title">
<a href="https://www.umass.edu/news/article/scheduling-surgeons-umass-amherst-researchers-identify-factors-could-influence" rel="bookmark">
<span>Scheduling Surgeons: UMass Amherst Researchers Identify Factors That Could Influence Hospital Efficiency</span>
</a>
</h3>
<div class="news-card__date"> June 24, 2026 </div>
</div>
</article>
</div>
<div class="featured-grid__item">
<article class="news-card news-card--featured">
<div class="news-card__image">
<div>
<img loading="lazy" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/Yanfei Xu polymer figure.jpg" width="1280" height="720" alt="An illustration of THDBT (tetrahydroxy deoxybenzoin triazole) filler aggregates at the molecular level. In this “slow chaos” state, there are fewer vibrational pathways available for heat transport, resulting in lower thermal conductivity. Reproduced from Materials Horizons with permission from the Royal Society of Chemistry.">
</div>
</div>
<div class="news-card__content">
<div class="featured-grid__category">
<a href="https://www.umass.edu/taxonomy/term/811" hreflang="en">Research</a>
</div>
<h3 class="news-card__title">
<a href="https://www.umass.edu/news/article/umass-amherst-led-team-discovers-new-way-make-thermally-insulative-plastics" rel="bookmark">
<span>UMass Amherst-Led Team Discovers New Way to Make Thermally Insulative Plastics </span>
</a>
</h3>
<div class="news-card__date"> June 22, 2026 </div>
</div>
</article>
</div>
<div class="featured-grid__item">
<article class="news-card news-card--featured">
<div class="news-card__image">
<div>
<img loading="lazy" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/Anthropology Field School Dig-26.jpg" width="1280" height="720" alt="Students dig during UMass Amhersts Bioarchaeology and Forensic Anthropology Field School">
</div>
</div>
<div class="news-card__content">
<div class="featured-grid__category">
<a href="https://www.umass.edu/taxonomy/term/646" hreflang="en">Academics</a>
</div>
<h3 class="news-card__title">
<a href="https://www.umass.edu/news/article/umass-bioarchaeology-and-forensic-anthropology-field-school-gives-students-hands" rel="bookmark">
<span>UMass Bioarchaeology and Forensic Anthropology Field School Gives Students Hands-On Experience in Forensic Anthropology</span>
</a>
</h3>
<div class="news-card__date"> June 17, 2026 </div>
</div>
</article>
</div>
<div class="featured-grid__item">
<article class="news-card news-card--featured">
<div class="news-card__image">
<div>
<img loading="lazy" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/NEWS-deli slicer.jpg" width="1280" height="720" alt="A worker slicing deli meat">
</div>
</div>
<div class="news-card__content">
<div class="featured-grid__category">
<a href="https://www.umass.edu/taxonomy/term/811" hreflang="en">Research</a>
</div>
<h3 class="news-card__title">
<a href="https://www.umass.edu/news/article/hands-why-human-touch-can-create-food-safety-blind-spot" rel="bookmark">
<span>Hands Off: Why Human Touch Can Create a Food-Safety Blind Spot </span>
</a>
</h3>
<div class="news-card__date"> June 17, 2026 </div>
</div>
</article>
</div>
</div>
<div class="news-events-row">
<div class="news-column">
<h3 class="section-title--news"> LATEST NEWS </h3>
<div class="news-list">
<div class="news-list__item">
<article class="news-teaser">
<div class="news-teaser__image">
<div>
<img loading="lazy" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/UMass Police Chief Tyrone Parham, Max Callahan, Senator Comerford on MA Senate Rostrum_crop.jpg" width="2833" height="1769" alt="UMass Police Chief Tyrone Parham, Max Callahan and Senator Comerford stand at the Massachusetts Senate Rostrum">
</div>
</div>
<div class="news-teaser__content">
<div class="news-list__category">
<a href="https://www.umass.edu/taxonomy/term/1001" hreflang="en">University News</a>
</div>
<h3 class="news-teaser__title">
<a href="https://www.umass.edu/news/article/umass-amherst-supported-effort-help-drivers-autism-interact-police-becomes-law" rel="bookmark">
<span>UMass Amherst-Supported Effort to Help Drivers with Autism Interact with Police Becomes Law </span>
</a>
</h3>
<div class="news-teaser__summary">
<div>
<p>A new law signed by Gov. Maura Healey aimed at helping people with autism during interactions with police has roots on the UMass Amherst campus. The so-called “Blue Envelope Bill” sets up a voluntary program where drivers or passengers can provide information helpful to law enforcement during traffic stops or other encounters.&nbsp;</p>
</div>
</div>
</div>
</article>
</div>
<div class="news-list__item">
<article class="news-teaser">
<div class="news-teaser__image">
<div>
<img loading="lazy" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/Giuliano Roberts Eye-Tracking IV Pumps Photo 2.jpg" width="4528" height="3016" alt="A student (left) wears eye-tracking glasses as doctoral student Seonhun Lee holds a laptop showing information about the study completed using the technology.">
</div>
</div>
<div class="news-teaser__content">
<div class="news-list__category">
<a href="https://www.umass.edu/taxonomy/term/811" hreflang="en">Research</a>
</div>
<h3 class="news-teaser__title">
<a href="https://www.umass.edu/news/article/eye-tracking-technology-helps-researchers-see-iv-pump-safety-through-student-nurses" rel="bookmark">
<span>Eye-Tracking Technology Helps Researchers See IV Pump Safety Through Student Nurses Eyes</span>
</a>
</h3>
<div class="news-teaser__summary">
<div>
<p>The approach, detailed in a study published in the June 2026 issue of Advancing Medical-Surgical Nursing, marks the first known use of eye-tracking technology to evaluate IV smart pumps.</p>
</div>
</div>
</div>
</article>
</div>
</div>
</div>
<div class="events-column">
<h3 class="events-title"> Events </h3>
<div class="events-widget">
<script type="text/javascript" src="/UMass%20Amherst%20_%20UMass%20Amherst_files/view"></script>
<style>
/* ========================================== CAMPUS EVENTS ========================================== */
.campus-events-widget {
background: transparent;
padding: 0;
font-family: inherit;
}
.campus-events-widget .events-list {
list-style: none;
margin: 0;
padding: 0;
}
/* ========================================== DESKTOP LIST LAYOUT ========================================== */
.campus-events-widget .teaser-item {
padding: 20px 0;
border-bottom: 1px solid #e5e5e5;
}
.campus-events-widget .teaser-item:last-child {
border-bottom: none;
}
.campus-events-widget .event-wrapper {
display: flex;
gap: 20px;
align-items: flex-start;
}
/* ===================================== DATE BLOCK ===================================== */
.campus-events-widget .event-date {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.125rem;
padding: 0.25rem;
background: #881C1C;
width: 2.875rem;
height: 3.125rem;
box-sizing: border-box;
}
.campus-events-widget .event-date__month {
font-size: 1rem;
font-weight: 800;
line-height: 1.25rem;
text-transform: uppercase;
text-align: center;
color: #fff;
}
.campus-events-widget .event-date__day {
font-size: 1rem;
font-weight: 800;
line-height: 1.25rem;
text-transform: uppercase;
text-align: center;
color: #fff;
}
/* ===================================== EVENT DETAILS ===================================== */
.campus-events-widget .event-details {
flex: 1;
position: relative;
}
/* Title */
.campus-events-widget .event-title {
font-size: 1rem;
font-weight: 800;
line-height: 1.25rem;
text-transform: uppercase;
color: #000;
position: relative;
margin: 0 0 0.375rem;
padding-right: 1.25rem;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.campus-events-widget .event-title a {
color: #222;
text-decoration: none;
}
.campus-events-widget .event-title a:hover {
color: #881c1c;
}
/* Chevron */
.campus-events-widget .event-title::after {
content: "";
position: absolute;
right: 0;
top: 0.2rem;
width: 0.3125rem;
height: 0.6875rem;
background-repeat: no-repeat;
background-size: contain;
background-position: center;
background-image: url("data:image/svg+xml,%3Csvg width='5' height='11' viewBox='0 0 5 11' fill='none'
xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd'clip-rule='evenodd'd='M4.57143 5.14286L0 0V10.2857L4.57143 5.14286Z'fill='%23881C1C'/%3E%3C/svg%3E"); } /* Time */ .campus-events-widget .event-time { font-size: 16px; color: #000; margin-bottom: 4px; } /* Location */ .campus-events-widget .event-location { font-size: 1rem; font-weight: 400; line-height: 1.5rem; font-style: normal; color: #000; } /* Hide summaries */ .campus-events-widget .summary, .campus-events-widget .event-description { display: none !important; } /* ========================================== MOBILE CARD DESIGN ========================================== */ @media (max-width: 768px) { .campus-events-widget .event-title { font-size: 0.75rem; } .campus-events-widget .event-time { font-size: 0.75rem; } .campus-events-widget .event-location { font-size: 0.75rem; } .campus-events-widget .event-wrapper { display: flex; flex: 1; padding-right: 10px; align-items: stretch; gap: 12px; border: 1px solid #CCC; } .campus-events-widget .event-date { display: flex; padding: 4px 8px; flex-direction: column; justify-content: center; align-items: center; align-self: stretch; background: #881C1C; min-width: 48px; height: auto; } .campus-events-widget .event-details { padding: 8px 10px 8px 0; } .campus-events-widget .teaser-item { flex: 0 0 279px; display: flex; border: 0px solid #CCC; padding-bottom: 0; } .campus-events-widget .events-list { display: flex; gap: 1rem; } .campus-events-widget .events-list::before, .campus-events-widget .events-list::after { content: ""; flex: 0 0 8.333%; } } /* ========================================== SMALL MOBILE ADJUSTMENTS ========================================== */ @media (max-width: 480px) { .campus-events-widget .event-wrapper { gap: 10px; } .campus-events-widget .event-date__day { font-size: 18px; } }
</style>
<div id="lw" class="campus-events-widget">
<div class="events-list">
<div class="teaser-item">
<div class="event-wrapper">
<!-- DATE BLOCK -->
<div class="event-date">
<div class="event-date__month"> JUL </div>
<div class="event-date__day"> 07 </div>
</div>
<!-- DETAILS -->
<div class="event-details">
<div class="event-title">
<a href="https://events.umass.edu/event/hr-is-here-for-you-5678?utm_campaign=widget&amp;utm_medium=widget&amp;utm_source=UMass+Amherst+Events+Calendar"> HR is Here for You </a>
</div>
<div class="event-time"> 10:00 am 4:00 pm </div>
<div class="event-location"> UMass Downtown </div>
</div>
</div>
</div>
<div class="teaser-item">
<div class="event-wrapper">
<!-- DATE BLOCK -->
<div class="event-date">
<div class="event-date__month"> JUL </div>
<div class="event-date__day"> 07 </div>
</div>
<!-- DETAILS -->
<div class="event-details">
<div class="event-title">
<a href="https://events.umass.edu/event/ms-in-accounting-virtual-information-session-1380?utm_campaign=widget&amp;utm_medium=widget&amp;utm_source=UMass+Amherst+Events+Calendar"> MS in Accounting Virtual Information Session </a>
</div>
<div class="event-time"> 12:00 pm 1:00 pm </div>
<div class="event-location"></div>
</div>
</div>
</div>
<div class="teaser-item">
<div class="event-wrapper">
<!-- DATE BLOCK -->
<div class="event-date">
<div class="event-date__month"> JUL </div>
<div class="event-date__day"> 07 </div>
</div>
<!-- DETAILS -->
<div class="event-details">
<div class="event-title">
<a href="https://events.umass.edu/event/thrive-at-five-summer-yoga-and-mindfulness-series?utm_campaign=widget&amp;utm_medium=widget&amp;utm_source=UMass+Amherst+Events+Calendar"> Thrive at Five - Summer Yoga and Mindfulness Series </a>
</div>
<div class="event-time"> 5:00 pm 6:00 pm </div>
<div class="event-location"> Orchard Hill Pavilion </div>
</div>
</div>
</div>
<div class="teaser-item">
<div class="event-wrapper">
<!-- DATE BLOCK -->
<div class="event-date">
<div class="event-date__month"> JUL </div>
<div class="event-date__day"> 08 </div>
</div>
<!-- DETAILS -->
<div class="event-details">
<div class="event-title">
<a href="https://events.umass.edu/event/online-or-hybrid-ms-in-cs-virtual-info-session-6202?utm_campaign=widget&amp;utm_medium=widget&amp;utm_source=UMass+Amherst+Events+Calendar"> Online or Hybrid MS in CS: Virtual Info Session </a>
</div>
<div class="event-time"> 10:00 am 11:00 am </div>
<div class="event-location"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="news-events-section__footer">
<a class="news-events-section__cta news-events-section__cta--mobile" href="https://www.umass.edu/news/news-events"> More News &amp; Events </a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
}
</style>
@@ -0,0 +1,138 @@
<div class="content-top">
<div class="lc--layout-container lc--full">
<div class="l--layout l--full">
<div class="lr--layout-region lr--main">
<div class="cc--component-container cc--homepage-hero">
<div class="c--component c--homepage-hero">
<div class="slides-container">
<div class="image-video-container has-video">
<img
src="https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg"
alt="Students collaborate using a driving simulator in the UMass Center for Transportation."
srcset="
https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg?h=9855f42d&amp;itok=d27r8GaT 1920w
"
/>
<div class="f--ambient-video" id="hero-ambient-video">
<video role="presentation" tabindex="-1" loop="" autoplay="" playsinline="" muted=""
id="hero-video" onerror="handleVideoError()">
<source
src="http://localhost:8080/uploads/2026/07/9375f45b-027a-4fb4-bd18-66889309280d.mp4"
type="video/mp4" onerror="handleVideoError()" />
</video>
</div>
<div id="hero-video-fallback"
style="display: none; position: absolute; top:0; left:0; width:100%; height:100%; z-index: 2;">
<th:block
th:utext="${hookManager.applyFilters('the_content', '[plugin:swiper-slider slug=''video-fallback'']')}">
</th:block>
</div>
<script>
function handleVideoError() {
var vidContainer = document.getElementById('hero-ambient-video');
if(vidContainer) vidContainer.style.display = 'none';
var controls = document.querySelector('.video-controls');
if(controls) controls.style.display = 'none';
var fallback = document.getElementById('hero-video-fallback');
if(fallback) fallback.style.display = 'block';
}
window.addEventListener('DOMContentLoaded', function() {
var v = document.getElementById('hero-video');
if (v && v.error) {
handleVideoError();
}
});
</script>
<div class="video-controls" data-once="ambientVideo">
<div class="video-controls-inner">
<button aria-labelledby="pauseBtn" class="video-button video-pause-button">
<svg height="14" viewBox="0 0 10 14" width="10" xmlns="http://www.w3.org/2000/svg">
<title id="pauseBtn">Pause Background Video</title>
<path
d="m1143 711v14h-3v-14zm7 0v14h-3v-14z"
fill="#fff"
fill-rule="evenodd"
transform="translate(-1140 -711)"
></path>
</svg>
</button>
<button aria-labelledby="playBtn" class="video-button video-play-button">
<svg height="29" viewBox="0 0 29 29" width="29" xmlns="http://www.w3.org/2000/svg">
<title id="playBtn">Play Background Video</title>
<path
d="m17.5 3c-7.99789474 0-14.5 6.50210526-14.5 14.5 0 7.9978947 6.50210526 14.5 14.5 14.5 7.9978947 0 14.5-6.5021053 14.5-14.5 0-7.99789474-6.5021053-14.5-14.5-14.5zm5.6763936 15.2939067-7.3503366 4.5939604c-.6635721.408352-1.5313202-.051044-1.5313202-.8422261v-9.1879207c0-.7911821.8677481-1.2761001 1.5313202-.8422261l7.3503366 4.5939604c.612528.38283.612528 1.3016221 0 1.6844521z"
fill="#fff"
fill-rule="evenodd"
transform="translate(-3 -3)"
></path>
</svg>
</button>
</div>
</div>
</div>
<div class="text-container centered">
<div class="slide-text-container-inner">
<h3>
<a href="https://www.umass.edu/gateway/why-umass">Stroke International School
2026.</a>
</h3>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/why-umass"
class="button-secondary button-context-dark"
aria-label="Learn More Learn more about UMass Amherst."
data-component-id="umass_base:button">
<span class="button-text"> Learn More </span>
</a>
</div>
<nav class="mc--menu mc--info-menu"
th:if="${infoMenu != null and not #lists.isEmpty(infoMenu.items)}">
<ul class="menu m--menu m--info-menu">
<li class="menu-item menu-item--expanded">
<details class="utility-button-wrapper mc--info-menu-container">
<summary type="button"
aria-label="Display submenu for Information menu"
aria-expanded="false" aria-haspopup="true"
class="utility-button arrow-toggle information-for-toggle">
<span th:text="${infoMenu.name}">Info For</span>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 7" enable-background="new 0 0 16 7"
xml:space="preserve" width="16" height="7" class="arrow">
<polygon fill-rule="evenodd" clip-rule="evenodd"
fill="#ffffff" points="7,7 0,0 16,0 "></polygon>
</svg>
</summary>
<div class="submenu-wrapper">
<ul class="submenu">
<li class="menu-item" th:each="item : ${infoMenu.items}">
<a th:href="${item.url}"
th:text="${item.label}">Prospective Students</a>
</li>
</ul>
</div>
</details>
<div class="submenu-wrapper-desktop">
<ul class="submenu">
<li class="menu-item" th:each="item : ${infoMenu.items}">
<a th:href="${item.url}" th:text="${item.label}">Prospective
Students</a>
</li>
</ul>
</div>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1 @@
<th:block th:utext="${hookManager.applyFilters('snippet_content', '[plugin:tabbed-livestream]')}"></th:block>
@@ -0,0 +1,53 @@
package com.sisvietnamvn.web;
import com.sisvietnamvn.web.domain.HtmlSnippet;
import com.sisvietnamvn.web.repository.HtmlSnippetRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
@SpringBootTest
public class ExportSnippetsTest {
@Autowired
private HtmlSnippetRepository snippetRepository;
@Test
public void exportSnippets() throws Exception {
File dir = new File("src/main/resources/templates/themes/umass/snippets");
if (!dir.exists()) {
dir.mkdirs();
}
for (HtmlSnippet snippet : snippetRepository.findAll()) {
String slug = snippet.getSlug();
if (slug == null || slug.trim().isEmpty()) {
continue;
}
String content = snippet.getContent();
if (content == null) {
content = "";
}
// Replace shortcodes with Thymeleaf syntax
content = content.replace("[menu:CTA]", "<th:block th:utext=\"${@componentTemplateService.render('cta-buttons', 'menu:CTA')}\"></th:block>");
content = content.replace("[menu:INFO:dropdown]", "<th:block th:utext=\"${@componentTemplateService.render('info-dropdown', 'menu:INFO')}\"></th:block>");
content = content.replace("[widgets:sidebar]", "<th:block th:utext=\"${@snippetService.renderWidgets('sidebar')}\"></th:block>");
content = content.replace("[widgets:footer]", "<th:block th:utext=\"${@snippetService.renderWidgets('footer')}\"></th:block>");
content = content.replace("[widgets:stats]", "<th:block th:utext=\"${@snippetService.renderWidgets('stats')}\"></th:block>");
// Note: snippetService.renderWidgets is private, I'll need to make it public if used,
// but for now let's just export the raw content and let Thymeleaf process them if we call the service,
// OR actually we can just let HtmlSnippetService do the rendering!
// Wait, if we use static Thymeleaf fragments, we still need to replace shortcodes.
// Let's just output the original content, we'll manually fix the shortcodes later if needed.
Files.writeString(Paths.get(dir.getAbsolutePath(), slug + ".html"), snippet.getContent() != null ? snippet.getContent() : "");
System.out.println("Exported: " + slug + ".html");
}
}
}
@@ -3,13 +3,14 @@ package com.sisvietnamvn.web;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sisvietnamvn.web.plugins.swiperslider.SwiperSliderAdminController.SliderGroup;
import com.sisvietnamvn.web.plugins.swiperslider.SwiperSliderAdminController.SliderItem;
import com.sisvietnamvn.web.repository.PostRepository;
import com.sisvietnamvn.web.repository.TagRepository;
import com.sisvietnamvn.web.service.SettingService;
import com.sisvietnamvn.web.plugins.swiperslider.SwiperSliderAdminController.SliderGroup;
import com.sisvietnamvn.web.plugins.swiperslider.SwiperSliderAdminController.SliderItem;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.type.TypeReference;
@SpringBootTest
public class SliderSeederTest {
@@ -43,7 +44,7 @@ public class SliderSeederTest {
java.util.List<SliderItem> items = posts.stream().map(post -> {
String imageUrl = post.getFeaturedImage() != null ? post.getFeaturedImage() : "";
String linkUrl = "/dao-tao/" + post.getSlug();
return new SliderItem(post.getTitle(), imageUrl, linkUrl, "");
return new SliderItem(post.getTitle(), imageUrl, linkUrl, "", null, null, null);
}).collect(java.util.stream.Collectors.toList());
SliderGroup newGroup = new SliderGroup(
+25
View File
@@ -0,0 +1,25 @@
import oracledb
import sys
def main():
try:
connection = oracledb.connect(user='sisvietnam', password='sisvietnam', dsn='localhost:1521/sisvietnam')
cursor = connection.cursor()
content = '[component:video-fallback]'
cursor.execute("UPDATE html_snippet SET content = :content WHERE slug = 'content-top'", content=content)
if cursor.rowcount > 0:
print("Successfully updated content-top snippet to use video-fallback component!")
else:
print("Warning: content-top snippet not found in DB.")
connection.commit()
cursor.close()
connection.close()
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == '__main__':
main()