update news & event 1

This commit is contained in:
2025-10-22 20:41:14 +07:00
parent 8f81545293
commit d4e91c7960
7 changed files with 379 additions and 80 deletions

View File

@ -73,6 +73,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\images\3000\NewFolder\" />
<Folder Include="wwwroot\images\wait-section\" />
</ItemGroup>

View File

@ -21,13 +21,41 @@
</div>
</div>
<div class="c-12 c-l-8 c-x-9 p-20">
<div class="d-f">
<h2 class="mb-4 title underline">Why GSSA Services Are Essential</h2>
<div class="d-f f-c">
<p class="sub-title">
We Are Air Cargo GSSA
</p>
<h2 class="title"> If Youve Ever Heard of A Cargo GSSA Before, This Is How It Works</h2>
<img class="w-100" src="~/images/3000/3001/gssa-overview.png" alt="Air Cargo GSSA illustration" />
</div>
<p class="mb-3">In todays competitive air cargo market, GSSA services are essential for airlines and brokers looking to optimize their operations and maximize revenue. More than 26% of global air cargo is managed through GSSAs, and this market share continues to grow as more airlines leverage these services to expand their presence in new regions.</p>
<p class="mb-3">By working with GSSA, airlines and brokers gain access to comprehensive services such as cargo sales, market analysis, capacity management and compliance with local regulations. This approach allows them to capture new business opportunities and reduce operational costs.</p>
<div class="r-n-g">
<div class="c-12 c-x-6 c-xm-4 p-20">
<div class="d-f">
<h2 class="mt-2 mb-5 title underline">Key Benefits of GSSA Services</h2>
<h3 class="mb-4 title underline">Being a Cargo GSSA/GSA means</h3>
</div>
<ul class="order-list mb-4">
<li class="d-f a-i-start">
<i class="atg atg-radiobutton a-2x"></i>
General Sales & Service Agents (GSSAs)/General Sales Agents (GSAs) represent airlines commercializing air freight capacity and supervise complex local cargo operations and administration and financial services for a assigned specific territory or region
</li>
<li class="d-f a-i-start mt-4">
<i class="atg atg-radiobutton a-2x"></i>
Represent for Airlines typically work with freight forwarders and handling agents (GHAs) to deliver transportation services to shippers.
</li>
<li class="d-f a-i-start mt-4">
<i class="atg atg-radiobutton a-2x"></i>
A extremely helpful contract with minimum cost, resources and top efficiency for Airlines in looking to expand into new markets or streamline costs in an existing location
</li>
</ul>
</div>
<div class="c-12 c-x-6 c-xm-8 p-20">
<div class="con-image">
<img class="w-100" src="~/images/3000/3001/gssa-operations-team.png" />
</div>
</div>
</div>
<div class="d-f mt-4">
<h3 class="mb-5 title underline">Why any Airlines should operate as Cargo GSSAs/GSAs model</h3>
</div>
<div class="cfull">
<div class="r-n-g">

View File

@ -2129,7 +2129,7 @@ video.bg-video {
padding: 25px;
background: var(--text-color-lW3);
border-radius: var(--radius);
margin-bottom: 40px
transition: top .3s ease-in-out
}
.sidebarR .nav-item {
font-size: .98rem;

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

View File

@ -1,5 +1,5 @@
import AMenu from '/js/libs/js-AMenu.js'
import AElementFixed from '/js/libs/js-AElementFixed.js'
function runServices () {
const parallaxEls = document.querySelectorAll('.con-page-section .parallax');
@ -24,6 +24,12 @@ function runServices () {
window.LoadingServices = function () {
runServices();
const aef = new AElementFixed({
ref: '#fHeader',
target: '.con-sidebarR',
breakpoint: 992,
unsnapThreshold: 40
});
}
function updateParallax(t, parallaxEls) {

View File

@ -0,0 +1,264 @@
// AElementFixed.snap-up-near-ref.js
// - Snap xuống (portal lên body) khi PROBE ra khỏi top viewport (như trước)
// - Khi lướt LÊN lại: nếu TARGET (đang ở trong CHA) tiến gần REF (±snapUpProximity quanh ref.bottom+unsnapThreshold) → SNAP ABSOLUTE
// - Unsnap khi PROBE vào lại viewport và |ref.top - target.top| > unsnapThreshold (giữ logic cũ)
// - Khi đang snap: nếu đáy TARGET chạm/vượt đáy CHA → trả về CHA & pin bottom:0
export default class AElementFixed extends window.AObject {
constructor(opts = {}) {
super();
this.o = Object.assign({
ref: null, // Element | selector
target: null, // Element | selector
breakpoint: 992, // < => mobile: unsnap
unsnapThreshold: 40, // px: ngưỡng unsnap và offset top khi snap
enableTargetBottomSnap: true, // snap nếu target ra đáy viewport (khi đang ở CHA)
gutterLeft: 20, // px: left khi pin bottom về CHA
snapUpProximity: 24 // px: khoảng "gần" để snap khi lướt lên lại
}, opts);
this.ref = typeof this.o.ref === 'string' ? document.querySelector(this.o.ref) : this.o.ref;
this.el = typeof this.o.target === 'string' ? document.querySelector(this.o.target) : this.o.target;
if (!this.ref || !this.el) throw new Error('[AElementFixed] Missing ref/target');
// state
this.snapped = false;
this._isPortaled = false;
this._hostParent = null; // CHA ban đầu
this._loopId = null; // rAF: loop khi probe trong viewport (check snap-up & unsnap)
this._snapLoopId = null; // rAF: monitor chạm đáy CHA khi đang snap
this._needIntroPose = true;// hiệu ứng intro khi snap
this._snapWidth = null; // lock width (px) khi portal
this._bind();
this._initObservers();
this._onResize();
}
update(opts = {}) {
Object.assign(this.o, opts);
this._stopInViewLoop();
this._stopSnapMonitor();
this._teardownObservers();
this._initObservers();
this._onResize();
}
destroy() {
this._stopInViewLoop();
this._stopSnapMonitor();
this._teardownObservers();
this._unsnapAbsolute();
window.removeEventListener('resize', this._onResize);
}
// ===== private =====
_bind() {
this._onResize = this._onResize.bind(this);
window.addEventListener('resize', this._onResize);
}
_onResize() {
const desktop = window.innerWidth >= this.o.breakpoint;
if (!desktop) {
this._stopInViewLoop();
this._stopSnapMonitor();
this._unsnapAbsolute();
}
}
_initObservers() {
// PROBE: đặt TRƯỚC target trong CHA để giữ vị trí flow
this.probe = document.createElement('div');
this.probe.setAttribute('data-aef-probe', '');
Object.assign(this.probe.style, { width: '1px', height: '1px', pointerEvents: 'none', opacity: 0 });
this.el.parentElement.insertBefore(this.probe, this.el);
// PROBE với root = viewport
this.ioProbeVP = new IntersectionObserver((entries) => {
for (const e of entries) {
// PROBE rời viewport phía TRÊN => SNAP ngay
if (!e.isIntersecting && e.boundingClientRect.top < 0) {
if (!this.snapped) this._snapAbsolute();
this._stopInViewLoop();
continue;
}
// PROBE vào viewport => bật loop để:
// - nếu đang snap: xét UNSNAP
// - nếu không snap: xét SNAP-UP khi target gần ref
if (e.isIntersecting) {
this._needIntroPose = true;
this._startInViewLoop();
}
}
}, { root: null, threshold: 0 });
this.ioProbeVP.observe(this.probe);
// (Tuỳ chọn) TARGET với root = viewport: out đáy viewport (khi ở CHA) → snap
if (this.o.enableTargetBottomSnap) {
this.ioTargetVP = new IntersectionObserver((entries) => {
for (const e of entries) {
if (this.snapped) continue;
// Bắt "đáy chạm đáy viewport" (vẫn intersect) để mượt hơn:
const vpBottom = e.rootBounds ? e.rootBounds.bottom : document.documentElement.clientHeight;
const EPS = 0.75;
if (e.isIntersecting) {
console.log(e.target.style.bottom);
if (e.target.style.bottom === '0px') {
this._startInViewLoop();
}
}
}
}, { root: null, threshold: [0, 1] });
this.ioTargetVP.observe(this.el);
}
}
_teardownObservers() {
if (this.ioProbeVP) { this.ioProbeVP.disconnect(); this.ioProbeVP = null; }
if (this.ioTargetVP) { this.ioTargetVP.disconnect(); this.ioTargetVP = null; }
if (this.probe && this.probe.parentNode) this.probe.parentNode.removeChild(this.probe);
this.probe = null;
}
// ===== Loop khi PROBE đang trong viewport: xét SNAP-UP & UNSNAP =====
_startInViewLoop() {
if (this._loopId) return;
const tick = () => {
const rr = this.ref.getBoundingClientRect();
const tr = this.el.getBoundingClientRect();
const anchor = rr.bottom + this.o.unsnapThreshold; // mốc bám khi snap
if (this.snapped) {
// UNSNAP: nếu lệch khỏi mốc quá ngưỡng (tránh dính khi user kéo mạnh)
const dy = Math.abs(tr.top - rr.top);
if (dy > this.o.unsnapThreshold) {
this._unsnapAbsolute(); // trả về CHA, giữ width
this._stopInViewLoop();
return;
}
} else {
// SNAP-UP khi lướt lên: target gần ref (quanh anchor)
const delta = Math.abs(tr.top - anchor);
if (delta <= this.o.snapUpProximity) {
this._snapAbsolute();
// sau snap, tiếp tục monitor chạm đáy CHA; loop này tự dừng khi ioProbe báo ra viewport
this._stopInViewLoop();
return;
}
}
this._loopId = requestAnimationFrame(tick);
};
this._loopId = requestAnimationFrame(tick);
}
_stopInViewLoop() {
if (this._loopId) { cancelAnimationFrame(this._loopId); this._loopId = null; }
}
// ===== Snap monitor: đang snap → nếu đáy target chạm/vượt đáy CHA => pin bottom:0 =====
_startSnapMonitor() {
if (this._snapLoopId) return;
const tickSnap = () => {
if (!this.snapped || !this._isPortaled || !this._hostParent) {
this._stopSnapMonitor();
return;
}
const pr = this._hostParent.getBoundingClientRect();
const er = this.el.getBoundingClientRect();
if (er.bottom >= pr.bottom) {
this._pinToHostBottom();
this._stopSnapMonitor();
return;
}
this._snapLoopId = requestAnimationFrame(tickSnap);
};
this._snapLoopId = requestAnimationFrame(tickSnap);
}
_stopSnapMonitor() {
if (this._snapLoopId) { cancelAnimationFrame(this._snapLoopId); this._snapLoopId = null; }
}
// ===== helpers =====
_portalToBody() {
if (this._isPortaled) return;
this._hostParent = this.el.parentNode;
// lock width trước khi portal để không nhảy layout
this._snapWidth = this.el.offsetWidth;
if (this._snapWidth && Number.isFinite(this._snapWidth)) {
this.el.style.width = this._snapWidth + 'px';
}
document.body.appendChild(this.el);
this._isPortaled = true;
}
// Trả về CHA: append vào host (đứng sau mọi thứ; nếu probe ở cuối thì tự sau probe)
_restoreToHostBeforeProbe() {
if (!this._isPortaled) return;
if (this._hostParent) this._hostParent.appendChild(this.el);
else document.body.appendChild(this.el); // fallback hiếm
this._isPortaled = false;
// GIỮ style.width để giữ bề ngang như yêu cầu
}
// Pin về CHA bottom:0 (absolute trong CHA), giữ width
_pinToHostBottom() {
this._restoreToHostBeforeProbe();
const host = this._hostParent || this.el.parentNode;
if (host && getComputedStyle(host).position === 'static') {
host.style.position = 'relative';
}
this.el.style.position = 'absolute';
this.el.style.left = this.o.gutterLeft + 'px';
this.el.style.top = '';
this.el.style.bottom = '0px';
this.snapped = false;
}
// ===== Snap / Unsnap =====
_snapAbsolute() {
if (this.snapped) return;
this.snapped = true;
// đo trước khi portal để giữ LEFT & intro pose
const tRectBefore = this.el.getBoundingClientRect();
const rRect = this.ref.getBoundingClientRect();
const pageLeft = Math.round(tRectBefore.left);
const pageTopNow = Math.round(tRectBefore.top + this.o.unsnapThreshold);
const pageTopFinal = Math.round(rRect.bottom + this.o.unsnapThreshold);
// portal lên body (đồng thời lock width)
this._portalToBody();
// intro pose → frame kế tiếp về top final
this.el.style.position = 'absolute';
this.el.style.left = pageLeft + 'px';
this.el.style.top = (this._needIntroPose ? pageTopNow : pageTopFinal) + 'px';
this.el.style.bottom = '';
if (this._needIntroPose) {
requestAnimationFrame(() => { this.el.style.top = pageTopFinal + 'px'; });
this._needIntroPose = false;
}
this._startSnapMonitor();
}
_unsnapAbsolute() {
if (!this.snapped) return;
this.snapped = false;
// không xoá width → giữ nguyên bề ngang
this.el.style.position = '';
this.el.style.left = '';
this.el.style.top = '';
this.el.style.bottom = '';
this._restoreToHostBeforeProbe();
}
}