Compare commits

...

8 Commits

Author SHA1 Message Date
5d7f7267e7 error-fixed 2026-06-30 00:42:59 +05:30
vidhya
4b6ed3781d Merge branch 'team' of https://git.metatroncube.in/MetatroncubeSoftwareSolutions/odoo-aakriti-events-rental into team 2026-06-29 23:22:46 +05:30
vidhya
bdb23b923f blog template 2026-06-29 23:20:21 +05:30
ae8973ba11 images updated 2026-06-29 19:54:10 +05:30
vidhya
fbf20b1fa2 responsive-about page 2026-06-29 17:26:25 +05:30
vidhya
49500e41df About us page updated 2026-06-29 17:17:37 +05:30
640fae8be8 portfolio, contact page updated 2026-06-29 15:51:46 +05:30
3d8621e6ab Events home page updated 2026-06-29 13:08:19 +05:30
94 changed files with 5768 additions and 0 deletions

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import controllers

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
{
'name': 'Aakriti Events - Premium Theme V2',
'version': '17.0.1.0.0',
'category': 'Theme/Creative',
'summary': 'Premium dark-gold events theme for Aakriti Events website',
'description': """
Aakriti Events Premium Theme V2.
A fully standalone, luxurious dark-gold events theme featuring:
- Dramatic hero section with event photography
- Services grid (Wedding, Corporate, Birthday, Cultural, Stage, Brand Launch, Private Parties, Event Coordination)
- Gallery carousel, process steps, testimonials, stats and contact form
- Sticky header with gold CTA button and mobile-responsive drawer nav
- Dark footer with 4-column grid
Can be switched to from Website > Theme settings.
""",
'author': 'Antigravity',
'depends': ['website', 'event_rental'],
'data': [
'views/website_v2_templates.xml',
'views/portfolio_templates.xml',
'views/blog_templates.xml',
],
'installable': True,
'application': False,
'auto_install': False,
'license': 'LGPL-3',
}

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import blog_controller

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
from odoo import http
from odoo.http import request
from ..utils.blog_data import get_all_posts, get_post_by_slug, get_recent_posts
class V2BlogController(http.Controller):
@http.route('/blog', type='http', auth='public', website=True)
def blog_list(self, **kwargs):
"""Render the blog listing page with all posts."""
posts = get_all_posts()
return request.render('theme_aakriti_v2.v2_blog_list', {
'posts': posts,
'page_title': 'Blog — Aakriti Events',
})
@http.route('/blog/<string:slug>', type='http', auth='public', website=True)
def blog_detail(self, slug, **kwargs):
"""Render a single blog post by slug."""
post = get_post_by_slug(slug)
if not post:
return request.not_found()
recent = get_recent_posts(exclude_slug=slug, limit=3)
return request.render('theme_aakriti_v2.v2_blog_detail', {
'post': post,
'recent_posts': recent,
'page_title': post['title'] + ' — Aakriti Events',
})

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -0,0 +1,345 @@
/** @odoo-module **/
/**
* Aakriti Events Premium Theme V2 JS
* Handles: sticky header, mobile drawer, gallery carousel, scroll reveal, counter animation
*/
(function () {
'use strict';
/* ── Helpers ──────────────────────────────────────────── */
function qs(sel, ctx) { return (ctx || document).querySelector(sel); }
function qsa(sel, ctx) { return (ctx || document).querySelectorAll(sel); }
function onReady(fn) {
if (document.readyState !== 'loading') { fn(); }
else { document.addEventListener('DOMContentLoaded', fn); }
}
/* ── Sticky Header ────────────────────────────────────── */
function initHeader() {
var header = qs('#v2-header');
if (!header) return;
function updateHeader() {
if (window.scrollY > 40) {
header.classList.add('scrolled');
} else {
header.classList.remove('scrolled');
}
}
window.addEventListener('scroll', updateHeader, { passive: true });
updateHeader();
}
/* ── Mobile Drawer ────────────────────────────────────── */
function initMobileDrawer() {
var toggle = qs('#v2-mobile-toggle');
var drawer = qs('#v2-mobile-drawer');
var overlay = qs('#v2-drawer-overlay');
var closeBtn = qs('#v2-drawer-close');
if (!toggle || !drawer) return;
function openDrawer() {
drawer.classList.add('open');
if (overlay) { overlay.classList.add('active'); }
document.body.style.overflow = 'hidden';
}
function closeDrawer() {
drawer.classList.remove('open');
if (overlay) { overlay.classList.remove('active'); }
document.body.style.overflow = '';
}
toggle.addEventListener('click', openDrawer);
if (closeBtn) { closeBtn.addEventListener('click', closeDrawer); }
if (overlay) { overlay.addEventListener('click', closeDrawer); }
qsa('#v2-mobile-drawer a').forEach(function (a) {
a.addEventListener('click', closeDrawer);
});
window.addEventListener('resize', function () {
if (window.innerWidth > 991) { closeDrawer(); }
});
}
/* ── Gallery Carousel ─────────────────────────────────── */
function initGallery() {
var track = qs('#v2-gallery-track');
var prevBtn = qs('#v2-gallery-prev');
var nextBtn = qs('#v2-gallery-next');
if (!track) return;
var current = 0;
var itemWidth = 0;
var visibleItems = 4;
function updateLayout() {
var ww = window.innerWidth;
if (ww > 991) {
visibleItems = 4;
} else if (ww > 576) {
visibleItems = 3;
} else {
visibleItems = 2;
}
var containerWidth = track.parentElement.offsetWidth;
var gap = 12; // White gap between slanted items in pixels
var items = qsa('.v2-gallery-item-slant', track);
// Calculate width 'w' so that 'visibleItems' fit exactly in containerWidth
var w = (containerWidth - (visibleItems - 1) * gap) / (0.75 * visibleItems + 0.25);
var mr = (-0.25 * w) + gap;
items.forEach(function (item, idx) {
item.style.width = w + 'px';
if (idx === items.length - 1) {
item.style.marginRight = '0px';
} else {
item.style.marginRight = mr + 'px';
}
});
itemWidth = (w * 0.75) + gap; // The effective step width to slide
current = clamp(current, 0, getMax());
goTo(current, false);
updateButtons();
}
function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); }
function getMax() {
var itemsCount = track.children.length;
return Math.max(0, itemsCount - visibleItems);
}
function updateButtons() {
if (prevBtn) {
if (current <= 0) {
prevBtn.style.opacity = '0.5';
prevBtn.style.pointerEvents = 'none';
} else {
prevBtn.style.opacity = '1';
prevBtn.style.pointerEvents = 'auto';
}
}
if (nextBtn) {
if (current >= getMax()) {
nextBtn.style.opacity = '0.5';
nextBtn.style.pointerEvents = 'none';
} else {
nextBtn.style.opacity = '1';
nextBtn.style.pointerEvents = 'auto';
}
}
}
function goTo(index, animate) {
if (animate === undefined) animate = true;
current = clamp(index, 0, getMax());
track.style.transition = animate ? 'transform 0.4s ease' : 'none';
track.style.transform = 'translateX(-' + (current * itemWidth) + 'px)';
updateButtons();
}
if (prevBtn) { prevBtn.addEventListener('click', function () { goTo(current - 1); resetAutoSlide(); }); }
if (nextBtn) { nextBtn.addEventListener('click', function () { goTo(current + 1); resetAutoSlide(); }); }
// Touch/swipe support
var startX = 0;
track.addEventListener('touchstart', function (e) { startX = e.touches[0].clientX; }, { passive: true });
track.addEventListener('touchend', function (e) {
var diff = startX - e.changedTouches[0].clientX;
if (Math.abs(diff) > 50) {
goTo(current + (diff > 0 ? 1 : -1));
resetAutoSlide();
}
}, { passive: true });
// Auto slide
var autoSlideInterval;
function startAutoSlide() {
autoSlideInterval = setInterval(function () {
if (current >= getMax()) {
goTo(0);
} else {
goTo(current + 1);
}
}, 3000);
}
function resetAutoSlide() {
clearInterval(autoSlideInterval);
startAutoSlide();
}
window.addEventListener('resize', updateLayout);
updateLayout();
startAutoSlide();
}
/* ── Scroll Reveal ────────────────────────────────────── */
function initReveal() {
var els = qsa('.v2-reveal');
if (!els.length) return;
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
io.unobserve(entry.target);
}
});
}, { threshold: 0.12 });
els.forEach(function (el) { io.observe(el); });
} else {
els.forEach(function (el) { el.classList.add('visible'); });
}
}
/* ── Counter Animation ────────────────────────────────── */
function animateCounter(el, target, suffix) {
var start = 0;
var duration = 1800;
var startTime = null;
function step(ts) {
if (!startTime) startTime = ts;
var progress = Math.min((ts - startTime) / duration, 1);
var eased = 1 - Math.pow(1 - progress, 3); // ease out cubic
var val = Math.round(eased * target);
el.textContent = val + suffix;
if (progress < 1) { requestAnimationFrame(step); }
}
requestAnimationFrame(step);
}
function initCounters() {
var counters = qsa('[data-v2-counter]');
if (!counters.length) return;
var observed = false;
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting && !observed) {
observed = true;
counters.forEach(function (el) {
var target = parseInt(el.getAttribute('data-v2-counter'), 10);
var suffix = el.getAttribute('data-v2-suffix') || '';
animateCounter(el, target, suffix);
});
io.disconnect();
}
});
}, { threshold: 0.3 });
var statsSection = qs('#v2-stats');
if (statsSection) { io.observe(statsSection); }
} else {
counters.forEach(function (el) {
el.textContent = el.getAttribute('data-v2-counter') + (el.getAttribute('data-v2-suffix') || '');
});
}
}
/* ── Active nav link ──────────────────────────────────── */
function initActiveNav() {
var path = window.location.pathname;
qsa('.v2-nav a, .v2-drawer-nav a').forEach(function (a) {
var href = a.getAttribute('href') || '';
if (href === path || (href !== '/' && path.startsWith(href))) {
a.classList.add('active');
}
});
}
/* ── Bootstrap ────────────────────────────────────────── */
function initPortfolio() {
const grid = document.getElementById('v2-portfolio-grid');
const tabs = document.querySelectorAll('.v2-portfolio-tab');
if (!grid || !tabs.length) return;
// Dynamic portfolio data
const portfolioData = [
{ category: 'weddings', img: '/theme_aakriti_v2/static/src/img/wedding.png', title: 'Romantic Garden Wedding', location: 'Bangalore, India' },
{ category: 'corporate', img: '/theme_aakriti_v2/static/src/img/corporate.png', title: 'Annual Corporate Summit', location: 'Hyderabad, India' },
{ category: 'birthday', img: '/theme_aakriti_v2/static/src/img/birthday.png', title: 'Milestone Birthday Bash', location: 'Bangalore, India' },
{ category: 'cultural', img: '/theme_aakriti_v2/static/src/img/cultural.png', title: 'Royal Cultural Night', location: 'Mysore, India' },
{ category: 'private', img: '/theme_aakriti_v2/static/src/img/gallery1.webp', title: 'Private Celebration', location: 'Goa, India' },
{ category: 'weddings', img: '/theme_aakriti_v2/static/src/img/about.webp', title: 'Classic Wedding', location: 'Bangalore, India' }
];
// Function to render items
function renderItems(filter) {
grid.innerHTML = ''; // Clear current
portfolioData.forEach(item => {
if (filter === 'all' || filter === item.category) {
const itemHTML = `
<div class="v2-portfolio-item" data-category="${item.category}" style="opacity: 0; transform: scale(0.9); transition: opacity 0.4s ease, transform 0.4s ease;">
<div class="v2-portfolio-item-inner">
<img src="${item.img}" alt="${item.title}"/>
<div class="v2-portfolio-info">
<h3>${item.title}</h3>
<p><svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 12-9 12s-9-5-9-12a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg> ${item.location}</p>
</div>
</div>
</div>
`;
// Parse and append to trigger animation
const temp = document.createElement('div');
temp.innerHTML = itemHTML.trim();
const element = temp.firstChild;
grid.appendChild(element);
// Trigger animation after append
setTimeout(() => {
element.style.opacity = '1';
element.style.transform = 'scale(1)';
}, 50);
}
});
}
// Initial render
renderItems('all');
// Tab click handlers
tabs.forEach(tab => {
tab.addEventListener('click', function () {
tabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
const filter = this.getAttribute('data-filter');
// Fade out existing items
const currentItems = grid.querySelectorAll('.v2-portfolio-item');
currentItems.forEach(item => {
item.style.opacity = '0';
item.style.transform = 'scale(0.9)';
});
// Wait for fade out, then render new filtered list
setTimeout(() => {
renderItems(filter);
}, 400); // Wait for transition
});
});
}
onReady(function () {
initHeader();
initMobileDrawer();
initGallery();
initReveal();
initCounters();
initActiveNav();
initPortfolio();
});
})();

View File

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,330 @@
# -*- coding: utf-8 -*-
"""
Aakriti Events V2 Blog Data Utility
All blog post data lives here. Each post is a dict with a unique `slug`
field used for URL routing.
"""
BLOG_POSTS = [
{
"slug": "how-to-plan-a-luxury-wedding",
"title": "How to Plan a Luxury Wedding Without the Stress",
"category": "Wedding Tips",
"date": "June 15, 2025",
"read_time": "6 min read",
"image": "/theme_aakriti_v2/static/src/img/wedding.png",
"excerpt": (
"Planning a luxury wedding can feel overwhelming, but with the right team "
"and timeline in place, your dream celebration becomes effortlessly achievable."
),
"content": """
<p>A luxury wedding is more than just a big budget it's about creating an experience
that feels personal, intentional, and effortlessly beautiful for every single guest.
The secret to pulling this off without stress lies in three core principles:
<strong>early planning, the right vendor network, and clear communication.</strong></p>
<h3>Start at Least 12 Months Out</h3>
<p>The most sought-after venues, photographers, and floral designers book up quickly.
Starting your planning journey 1218 months before your date ensures you have access
to the very best. Begin with a clear vision board and a realistic budget conversation
before you book anything.</p>
<h3>Build Your Dream Team First</h3>
<p>Your wedding planner and creative director are your most important hires.
A great planner will have an established vendor network, negotiate on your behalf,
and manage your timeline so you never have to worry. At Aakriti Design, we handle
every detail from mandap construction to guest flow logistics so you can
simply be present on your day.</p>
<h3>Design Around Your Story</h3>
<p>Luxury isn't about excess — it's about meaning. Every floral arrangement,
every lighting choice, every table setting should reflect who you are as a couple.
We begin every wedding design consultation by listening. Your cultural background,
your favourite memories, the mood you want guests to feel walking in all of this
shapes our creative direction.</p>
<h3>Plan for the Guest Experience</h3>
<p>The difference between a good wedding and an unforgettable one is how guests
<em>feel</em> as they move through the day. Think about arrival moments, cocktail
hour flow, the reveal of the main reception space, and the energy of the dance floor.
Every transition is an opportunity to create a memory.</p>
<p>Ready to start planning your dream wedding? <a href="/contactus">Reach out to our
team</a> for a complimentary consultation.</p>
""",
"author": "Paarul Ladd",
"author_title": "Founder & Creative Director",
"tags": ["Wedding", "Planning", "Luxury"],
},
{
"slug": "multicultural-wedding-design-tips",
"title": "Multicultural Wedding Design: Blending Traditions Beautifully",
"category": "Design Inspiration",
"date": "May 28, 2025",
"read_time": "5 min read",
"image": "/theme_aakriti_v2/static/src/img/cultural.png",
"excerpt": (
"Designing a fusion wedding that honours two cultures with equal beauty and "
"respect requires deep creativity and cultural sensitivity. Here's how we do it."
),
"content": """
<p>Multicultural weddings are among the most beautiful and complex celebrations
to design. When two families bring different traditions, colour palettes, rituals,
and aesthetic expectations to the same event, the challenge is to honour them
all without making the event feel like a compromise.</p>
<h3>Start with a Cultural Discovery Session</h3>
<p>Before we pick a single flower or choose a fabric, we sit down with both
families separately and together. We ask about rituals that matter most, colours
that hold significance, and moments they want guests to witness and remember.
This forms the emotional foundation of the entire design.</p>
<h3>Find the Visual Thread</h3>
<p>Every beautiful fusion design has a unifying visual language a colour, a
texture, or a motif that bridges both traditions. For an Indo-Western wedding,
this might be ivory and gold paired with lush greenery. For an African-South Asian
celebration, it could be rich jewel tones with intricate pattern work. The thread
makes the event feel cohesive rather than divided.</p>
<h3>Zone the Ceremony Thoughtfully</h3>
<p>When a celebration includes two ceremony types a Hindu ceremony and a Christian
blessing, for example how you transition between zones matters enormously. Lighting,
floral drops, or even a simple walk through a beautifully designed corridor can create
a meaningful moment of transition between rituals.</p>
<h3>Communicate with Vendors Early</h3>
<p>Caterers, musicians, officiants every vendor needs to understand the cultural
context of what they're contributing to. We brief each vendor thoroughly and ensure
all timelines account for the natural pace of different ritual formats.</p>
<p>We've had the privilege of designing some of the most extraordinary fusion
celebrations in the region. <a href="/contactus">Let's talk about yours.</a></p>
""",
"author": "Paarul Ladd",
"author_title": "Founder & Creative Director",
"tags": ["Multicultural", "Wedding", "Design"],
},
{
"slug": "stage-decoration-trends-2025",
"title": "Stage Decoration Trends Dominating 2025 Events",
"category": "Trends",
"date": "May 10, 2025",
"read_time": "4 min read",
"image": "/theme_aakriti_v2/static/src/img/stage_decor.png",
"excerpt": (
"From dramatic floral backdrops to architectural LED installations, "
"stage design in 2025 is bolder, more immersive, and deeply personal."
),
"content": """
<p>The stage is the centrepiece of any celebration the place where memories are
made, photographs are taken, and families gather. In 2025, stage decoration has
evolved dramatically, moving beyond standard floral arches toward fully immersive,
architectural experiences.</p>
<h3>1. Organic Floral Cascades</h3>
<p>Tightly packed, symmetrical floral walls are giving way to organic, cascading
arrangements that feel wild and lush. Think trailing jasmine, oversized garden roses,
and dramatic foliage draping from asymmetric frames. The effect is romantic without
feeling rigid.</p>
<h3>2. Mixed Material Backdrops</h3>
<p>Combining fabric draping with metallic accents, geometric frames, and live
botanicals creates stages with depth and texture. The interplay of softness and
structure creates a visually rich backdrop that photographs beautifully from
every angle.</p>
<h3>3. LED and Projection Mapping</h3>
<p>For evening events and corporate galas, projection mapping onto stage structures
allows dynamic, programmable visuals that transform throughout the event. From a
soft warm glow during dinner to an energetic light show for the reception, the
technology adds theatrical impact.</p>
<h3>4. Custom Monogram and Typography</h3>
<p>Couples and brands are increasingly requesting custom typographic elements
integrated into stage design neon or acrylic lettering, laser-cut name panels,
and illuminated monograms that serve as both decor and branding.</p>
<h3>5. Sustainability-Forward Design</h3>
<p>2025 is also the year event design gets more sustainable. Rental flower walls,
reusable structural elements, and seasonal, locally sourced florals are becoming
standard practice for conscious couples.</p>
<p>Want to see how these trends could work for your event?
<a href="/contactus">Book a design consultation with us.</a></p>
""",
"author": "Paarul Ladd",
"author_title": "Founder & Creative Director",
"tags": ["Stage Design", "Trends", "Decor"],
},
{
"slug": "corporate-event-design-essentials",
"title": "Corporate Event Design: Making Brand Moments Unforgettable",
"category": "Corporate Events",
"date": "April 22, 2025",
"read_time": "5 min read",
"image": "/theme_aakriti_v2/static/src/img/corporate.png",
"excerpt": (
"A corporate event is a brand experience. Every design decision — from "
"the entrance to the stage — should reflect your company's identity and values."
),
"content": """
<p>Corporate events are often treated as purely logistical exercises a venue,
some food, a speaker. But the most impactful brand moments happen when design
is treated with the same care as content. When a space feels right, guests
pay attention, they engage, they remember.</p>
<h3>Design That Communicates Brand Identity</h3>
<p>The moment a guest enters your event, they should feel your brand. This means
your colour palette, typography, and visual language need to carry through from
the signage at the entrance to the centrepieces on the tables. We work closely
with your marketing team to ensure design coherence at every touchpoint.</p>
<h3>Entrance Design Matters Most</h3>
<p>The entrance moment sets the emotional tone for everything that follows. A
dramatic brand-installed entrance arch, a branded photo wall, or a thoughtfully
lit arrival corridor creates a sense of occasion and signals to guests that this
event is special.</p>
<h3>Stage and AV Integration</h3>
<p>For conferences and product launches, the stage design and audio-visual
production need to work as one unified system. Screens, lighting rigs, speaker
positions, and branding elements should be designed together, not assembled
separately. We coordinate closely with AV teams to ensure a seamless result.</p>
<h3>Networking Spaces Deserve Design Attention</h3>
<p>Breakout rooms and networking areas are where conversations and deals
happen. Comfortable, well-designed networking zones with good lighting and
thoughtful furniture arrangements significantly increase guest engagement and
event ROI.</p>
<p>Planning a corporate event or brand activation?
<a href="/contactus">Let's create something exceptional together.</a></p>
""",
"author": "Paarul Ladd",
"author_title": "Founder & Creative Director",
"tags": ["Corporate", "Brand", "Events"],
},
{
"slug": "birthday-celebration-ideas-adults",
"title": "Elevated Birthday Celebrations: Beyond the Ordinary",
"category": "Birthday Events",
"date": "April 5, 2025",
"read_time": "4 min read",
"image": "/theme_aakriti_v2/static/src/img/birthday.png",
"excerpt": (
"Adult birthday celebrations deserve the same creative attention as weddings. "
"Here's how to design a milestone birthday that guests will talk about for years."
),
"content": """
<p>A milestone birthday a 30th, 50th, or 60th is a significant life moment.
It deserves to be celebrated with intention, beauty, and personal meaning.
Gone are the days of a standard restaurant booking. Today's elevated birthday
events are intimate, immersive, and deeply personal.</p>
<h3>Choose a Theme That Reflects the Guest of Honour</h3>
<p>The best birthday events tell the story of the person being celebrated.
Are they a lover of travel? A fashion devotee? A nature enthusiast? Let their
passions and personality drive every design decision from the colour palette
and centrepieces to the entertainment and menu.</p>
<h3>The Importance of the Reveal Moment</h3>
<p>One of the most powerful design tools for a birthday celebration is the
reveal moment when the guest of honour first sees the decorated space.
A well-designed reveal, with controlled lighting and a dramatic entrance, creates
an emotional peak that guests carry with them forever.</p>
<h3>Intimate vs. Grand Both Can Be Extraordinary</h3>
<p>An intimate dinner for 20 can be just as visually stunning as a party for 200.
In fact, smaller gatherings allow for more intricate, personal design details
custom place cards, hand-written menus, personalised table settings, and curated
sensory experiences that wouldn't be practical at scale.</p>
<h3>Commission a Signature Cake</h3>
<p>A custom birthday cake that reflects the guest of honour's aesthetic is one
of the most photographed moments of any celebration. Work with an artist-baker
to create something that becomes a conversation piece and the centrepiece of
the dessert table.</p>
<p>Ready to design a birthday celebration as special as the person it honours?
<a href="/contactus">Let's begin the conversation.</a></p>
""",
"author": "Paarul Ladd",
"author_title": "Founder & Creative Director",
"tags": ["Birthday", "Celebration", "Design"],
},
{
"slug": "event-decor-colour-palette-guide",
"title": "The Art of Choosing Your Event Colour Palette",
"category": "Design Guide",
"date": "March 18, 2025",
"read_time": "7 min read",
"image": "/theme_aakriti_v2/static/src/img/gallery1.webp",
"excerpt": (
"Your event colour palette sets the emotional tone before a single guest "
"arrives. Learn how to choose a palette that feels intentional and cohesive."
),
"content": """
<p>Colour is the most powerful emotional tool in event design. Before a guest
reads a single word of signage or takes their first bite of food, the colour
palette of a space has already communicated the mood, the personality, and the
intention of the event. Choosing thoughtfully matters enormously.</p>
<h3>Start with the Emotional Goal</h3>
<p>Ask yourself: how do you want guests to feel when they walk in? Romantic and
intimate? Celebratory and energetic? Sophisticated and calm? Different colour
families evoke different emotional responses. Warm terracottas and earthy tones
feel grounded and welcoming. Cool blues and silvers feel refined and elegant.
Jewel tones feel festive and rich.</p>
<h3>Choose a Dominant, Secondary, and Accent</h3>
<p>The most successful event palettes use three layers of colour. A dominant
colour (60% of the visual space), a secondary colour (30%), and an accent
(10%). This structure creates visual harmony without monotony. For a luxury
wedding, this might look like: soft ivory (dominant), dusty sage (secondary),
and burnished gold (accent).</p>
<h3>Consider How Colour Behaves in Different Lighting</h3>
<p>A colour that looks perfect in a mood board can behave completely differently
under warm uplighting, natural daylight, or cool LED wash. We always review
fabric and floral samples under the actual event lighting conditions before
finalising any palette decision.</p>
<h3>Cultural Significance of Colour</h3>
<p>In multicultural events, colour choices carry symbolic weight. Red is auspicious
in South Asian traditions; white is associated with mourning in some East Asian
cultures. Understanding the cultural context of your guest list ensures colour
choices feel respectful and celebratory.</p>
<h3>Test Before You Commit</h3>
<p>We always recommend a mock-up table setting or a small test arrangement before
finalising large orders. Seeing colours together in a real-world setting with
real fabrics, real florals, and real lighting often reveals refinements that
digital mood boards miss.</p>
<p>Working on an event and unsure about your palette?
<a href="/contactus">Book a design session with our team.</a></p>
""",
"author": "Paarul Ladd",
"author_title": "Founder & Creative Director",
"tags": ["Design", "Colour", "Tips"],
},
]
def get_all_posts():
"""Return all blog posts."""
return BLOG_POSTS
def get_post_by_slug(slug):
"""Return a single blog post dict by its slug, or None if not found."""
for post in BLOG_POSTS:
if post["slug"] == slug:
return post
return None
def get_recent_posts(exclude_slug=None, limit=3):
"""Return recent posts, optionally excluding one by slug."""
posts = [p for p in BLOG_POSTS if p["slug"] != exclude_slug]
return posts[:limit]

View File

@ -0,0 +1,615 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ══════════════════════════════════════════════════════
BLOG LIST PAGE
Route: /v2/blog
══════════════════════════════════════════════════════ -->
<template id="v2_blog_list" name="V2 Blog List">
<t t-call="website.layout">
<t t-set="head">
<title t-out="page_title"/>
<meta name="description" content="Read the latest insights on luxury weddings, event design, multicultural celebrations, and creative inspiration from the Aakriti Events team."/>
</t>
<div id="wrap" class="oe_structure" style="background-color: var(--v2-cream); font-family: var(--v2-font-body);">
<style>
/* ── Blog List Page ── */
.v2-blog-banner {
position: relative;
height: 340px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
overflow: hidden;
}
.v2-blog-banner::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(to bottom, rgba(30, 18, 8, 0.55) 0%, rgba(30, 18, 8, 0.65) 100%);
z-index: 1;
}
.v2-blog-banner-bg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.v2-blog-banner-content {
position: relative;
z-index: 2;
}
.v2-blog-banner-title {
font-family: var(--v2-font-display);
font-size: clamp(2.8rem, 5vw, 4.5rem);
font-weight: 600;
color: var(--v2-white);
line-height: 1.1;
margin-bottom: 12px;
}
.v2-blog-breadcrumb {
font-size: 0.8rem;
color: rgba(255,255,255,0.75);
letter-spacing: 0.05em;
text-transform: uppercase;
}
.v2-blog-breadcrumb a {
color: rgba(255,255,255,0.75);
text-decoration: none;
}
.v2-blog-breadcrumb .v2-active {
color: var(--v2-gold);
}
/* Blog Grid */
.v2-blog-section {
max-width: 1320px;
margin: 0 auto;
padding: 80px 24px;
}
.v2-blog-grid {
display: grid;
gap: 32px;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.v2-blog-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.v2-blog-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* Blog Card */
.v2-blog-card {
background: var(--v2-white);
border-radius: 18px;
overflow: hidden;
box-shadow: 0 10px 40px rgba(30,18,8,0.06);
border: 1px solid var(--v2-border);
transition: transform 0.3s ease, box-shadow 0.3s ease;
display: flex;
flex-direction: column;
}
.v2-blog-card:hover {
transform: translateY(-6px);
box-shadow: 0 20px 50px rgba(30,18,8,0.12);
}
.v2-blog-card-img-wrap {
position: relative;
overflow: hidden;
height: 240px;
}
.v2-blog-card-img-wrap img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.6s ease;
}
.v2-blog-card:hover .v2-blog-card-img-wrap img {
transform: scale(1.06);
}
.v2-blog-card-category {
position: absolute;
top: 16px;
left: 16px;
background: var(--v2-gold);
color: var(--v2-white);
font-size: 0.68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
padding: 4px 12px;
border-radius: 4px;
}
.v2-blog-card-body {
padding: 28px 24px 24px;
flex: 1;
display: flex;
flex-direction: column;
}
.v2-blog-card-meta {
font-size: 0.78rem;
color: var(--v2-muted);
margin-bottom: 12px;
display: flex;
gap: 14px;
align-items: center;
}
.v2-blog-card-meta span {
display: flex;
align-items: center;
gap: 4px;
}
.v2-blog-card-title {
font-family: var(--v2-font-display);
font-size: 1.4rem;
font-weight: 600;
color: var(--v2-text);
line-height: 1.25;
margin-bottom: 12px;
text-decoration: none;
display: block;
}
.v2-blog-card-title:hover {
color: var(--v2-gold);
}
.v2-blog-card-excerpt {
font-size: 0.88rem;
color: var(--v2-muted);
line-height: 1.7;
flex: 1;
margin-bottom: 24px;
}
.v2-blog-card-link {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.8rem;
font-weight: 700;
color: var(--v2-gold);
text-decoration: none;
text-transform: uppercase;
letter-spacing: 0.08em;
transition: gap 0.2s ease;
}
.v2-blog-card-link:hover {
gap: 10px;
color: var(--v2-gold-dark);
}
@media (max-width: 767px) {
.v2-blog-section {
padding: 48px 20px;
}
}
</style>
<!-- Inner Banner -->
<div class="v2-blog-banner">
<img class="v2-blog-banner-bg"
src="/theme_aakriti_v2/static/src/img/hero_bg.png"
alt="Blog banner"/>
<div class="v2-blog-banner-content">
<h1 class="v2-blog-banner-title">Our Blog</h1>
<div class="v2-blog-breadcrumb">
<a href="/">Home</a> / <span class="v2-active">Blog</span>
</div>
</div>
</div>
<!-- Blog Grid -->
<div class="v2-blog-section">
<div style="text-align:center; margin-bottom:56px;">
<div style="font-size:0.75rem; font-weight:700; color:#C9973A; text-transform:uppercase; letter-spacing:0.15em; margin-bottom:12px; display:flex; align-items:center; justify-content:center; gap:12px;">
<span style="height:1px; width:40px; background:#C9973A; display:inline-block;"></span>
INSIGHTS &amp; INSPIRATION
<span style="height:1px; width:40px; background:#C9973A; display:inline-block;"></span>
</div>
<h2 style="font-family:'Cormorant Garamond',Georgia,serif; font-size:clamp(2rem,3.5vw,3rem); color:#1a1206; font-weight:600; margin:0;">
Stories from the Studio
</h2>
</div>
<div class="v2-blog-grid">
<t t-foreach="posts" t-as="post">
<article class="v2-blog-card">
<div class="v2-blog-card-img-wrap">
<img t-att-src="post['image']" t-att-alt="post['title']"/>
<span class="v2-blog-card-category" t-out="post['category']"/>
</div>
<div class="v2-blog-card-body">
<div class="v2-blog-card-meta">
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/></svg>
<t t-out="post['date']"/>
</span>
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
<t t-out="post['read_time']"/>
</span>
</div>
<a t-attf-href="/blog/#{post['slug']}" class="v2-blog-card-title" t-out="post['title']"/>
<p class="v2-blog-card-excerpt" t-out="post['excerpt']"/>
<a t-attf-href="/blog/#{post['slug']}" class="v2-blog-card-link">
Read Article
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
</a>
</div>
</article>
</t>
</div>
</div>
</div>
</t>
</template>
<!-- ══════════════════════════════════════════════════════
BLOG DETAIL PAGE
Route: /v2/blog/<slug>
══════════════════════════════════════════════════════ -->
<template id="v2_blog_detail" name="V2 Blog Detail">
<t t-call="website.layout">
<t t-set="head">
<title t-out="page_title"/>
<meta t-att-name="'description'" t-att-content="post['excerpt']"/>
</t>
<div id="wrap" class="oe_structure" style="background-color: var(--v2-cream); font-family: var(--v2-font-body);">
<style>
/* ── Blog Detail Page ── */
.v2-detail-banner {
position: relative;
height: 460px;
overflow: hidden;
display: flex;
align-items: flex-end;
}
.v2-detail-banner::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(30,18,8,0.85) 0%, rgba(30,18,8,0.25) 60%, transparent 100%);
z-index: 1;
}
.v2-detail-banner img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.v2-detail-banner-content {
position: relative;
z-index: 2;
max-width: 1320px;
margin: 0 auto;
padding: 0 24px 48px;
width: 100%;
}
.v2-detail-category-badge {
display: inline-block;
background: var(--v2-gold);
color: var(--v2-white);
font-size: 0.68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
padding: 5px 14px;
border-radius: 4px;
margin-bottom: 16px;
}
.v2-detail-banner-title {
font-family: var(--v2-font-display);
font-size: clamp(2rem, 4vw, 3.5rem);
font-weight: 600;
color: var(--v2-white);
line-height: 1.15;
max-width: 800px;
margin-bottom: 20px;
}
.v2-detail-meta {
display: flex;
flex-wrap: wrap;
gap: 20px;
font-size: 0.82rem;
color: rgba(255,255,255,0.8);
}
.v2-detail-meta span {
display: flex;
align-items: center;
gap: 6px;
}
/* Article Layout */
.v2-detail-layout {
max-width: 1320px;
margin: 0 auto;
padding: 64px 24px;
display: grid;
gap: 60px;
grid-template-columns: 1fr;
}
@media (min-width: 1024px) {
.v2-detail-layout {
grid-template-columns: 1fr 340px;
}
}
/* Article Body */
.v2-article-body {
font-size: 1rem;
color: var(--v2-text);
line-height: 1.85;
}
.v2-article-body h3 {
font-family: var(--v2-font-display);
font-size: 1.6rem;
font-weight: 600;
color: var(--v2-text);
margin: 40px 0 16px;
line-height: 1.2;
}
.v2-article-body p {
margin-bottom: 20px;
}
.v2-article-body strong {
color: var(--v2-text);
font-weight: 600;
}
.v2-article-body a {
color: var(--v2-gold);
text-decoration: underline;
}
.v2-article-body a:hover {
color: var(--v2-gold-dark);
}
.v2-article-body em {
font-style: italic;
}
/* Author Card */
.v2-author-card {
display: flex;
align-items: center;
gap: 16px;
background: var(--v2-white);
border: 1px solid var(--v2-border);
border-radius: 12px;
padding: 24px;
margin-top: 48px;
}
.v2-author-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
background: var(--v2-gold);
display: flex;
align-items: center;
justify-content: center;
font-family: var(--v2-font-display);
font-size: 1.6rem;
color: var(--v2-white);
font-weight: 600;
flex-shrink: 0;
}
.v2-author-info h4 {
font-family: var(--v2-font-display);
font-size: 1.2rem;
color: var(--v2-text);
font-weight: 600;
margin: 0 0 4px;
}
.v2-author-info p {
font-size: 0.8rem;
color: var(--v2-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
margin: 0;
}
/* Tags */
.v2-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 32px;
}
.v2-tag {
font-size: 0.75rem;
color: var(--v2-gold);
border: 1px solid var(--v2-gold);
padding: 4px 14px;
border-radius: 100px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
}
/* Sidebar */
.v2-sidebar {}
.v2-sidebar-card {
background: var(--v2-white);
border: 1px solid var(--v2-border);
border-radius: 16px;
padding: 32px 28px;
margin-bottom: 32px;
}
.v2-sidebar-title {
font-family: var(--v2-font-display);
font-size: 1.25rem;
font-weight: 600;
color: var(--v2-text);
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid var(--v2-border);
}
.v2-recent-post {
display: flex;
gap: 14px;
align-items: flex-start;
padding: 14px 0;
border-bottom: 1px solid var(--v2-border);
text-decoration: none;
}
.v2-recent-post:last-child {
border-bottom: none;
padding-bottom: 0;
}
.v2-recent-post-img {
width: 72px;
height: 60px;
border-radius: 8px;
object-fit: cover;
flex-shrink: 0;
}
.v2-recent-post-info h5 {
font-size: 0.88rem;
font-weight: 600;
color: var(--v2-text);
line-height: 1.35;
margin: 0 0 4px;
transition: color 0.2s;
}
.v2-recent-post:hover .v2-recent-post-info h5 {
color: var(--v2-gold);
}
.v2-recent-post-info span {
font-size: 0.75rem;
color: var(--v2-muted);
}
.v2-cta-sidebar {
background: linear-gradient(135deg, var(--v2-dark) 0%, var(--v2-dark2) 100%);
border-radius: 16px;
padding: 36px 28px;
text-align: center;
}
.v2-cta-sidebar h3 {
font-family: var(--v2-font-display);
font-size: 1.5rem;
color: var(--v2-white);
font-weight: 600;
margin-bottom: 12px;
line-height: 1.25;
}
.v2-cta-sidebar p {
font-size: 0.88rem;
color: rgba(255,255,255,0.7);
margin-bottom: 24px;
line-height: 1.6;
}
@media (max-width: 767px) {
.v2-detail-layout {
padding: 40px 20px;
}
.v2-detail-banner {
height: 320px;
}
}
</style>
<!-- Hero Banner -->
<div class="v2-detail-banner">
<img t-att-src="post['image']" t-att-alt="post['title']"/>
<div class="v2-detail-banner-content">
<span class="v2-detail-category-badge" t-out="post['category']"/>
<h1 class="v2-detail-banner-title" t-out="post['title']"/>
<div class="v2-detail-meta">
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/></svg>
<t t-out="post['date']"/>
</span>
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
<t t-out="post['read_time']"/>
</span>
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
<t t-out="post['author']"/>
</span>
</div>
</div>
</div>
<!-- Article Layout -->
<div class="v2-detail-layout">
<!-- Main Content -->
<div>
<!-- Breadcrumb -->
<div style="font-size:0.78rem; color: var(--v2-muted); margin-bottom:32px; text-transform:uppercase; letter-spacing:0.06em;">
<a href="/" style="color: var(--v2-muted); text-decoration:none;">Home</a>
<span style="margin:0 8px;">·</span>
<a href="/blog" style="color: var(--v2-muted); text-decoration:none;">Blog</a>
<span style="margin:0 8px;">·</span>
<span style="color: var(--v2-gold);" t-out="post['category']"/>
</div>
<!-- Article Body -->
<div class="v2-article-body">
<t t-out="post['content']"/>
</div>
<!-- Tags -->
<div class="v2-tags">
<t t-foreach="post.get('tags', [])" t-as="tag">
<span class="v2-tag" t-out="tag"/>
</t>
</div>
<!-- Author Card -->
<div class="v2-author-card">
<div class="v2-author-avatar">
<t t-out="post['author'][0]"/>
</div>
<div class="v2-author-info">
<h4 t-out="post['author']"/>
<p t-out="post['author_title']"/>
</div>
</div>
<!-- Back Link -->
<div style="margin-top:48px;">
<a href="/blog" style="display:inline-flex; align-items:center; gap:8px; font-size:0.82rem; font-weight:700; color: var(--v2-gold); text-transform:uppercase; letter-spacing:0.08em; text-decoration:none;">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="m12 19-7-7 7-7"/></svg>
Back to All Articles
</a>
</div>
</div>
<!-- Sidebar -->
<aside class="v2-sidebar">
<!-- Recent Posts -->
<div class="v2-sidebar-card">
<div class="v2-sidebar-title">More Articles</div>
<t t-foreach="recent_posts" t-as="rp">
<a t-attf-href="/blog/#{rp['slug']}" class="v2-recent-post">
<img t-att-src="rp['image']" t-att-alt="rp['title']" class="v2-recent-post-img"/>
<div class="v2-recent-post-info">
<h5 t-out="rp['title']"/>
<span t-out="rp['date']"/>
</div>
</a>
</t>
</div>
<!-- CTA Card -->
<div class="v2-cta-sidebar">
<h3>Ready to Create Your Dream Event?</h3>
<p>Let's talk about your vision. We'd love to bring it to life.</p>
<a href="/contactus" style="display:inline-flex; align-items:center; gap:8px; background: var(--v2-gold); color: var(--v2-white); padding:12px 24px; border-radius:4px; font-size:0.8rem; font-weight:700; text-transform:uppercase; letter-spacing:0.06em; text-decoration:none; transition:background 0.3s;">
Book a Consultation
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
</a>
</div>
</aside>
</div>
</div>
</t>
</template>
</odoo>

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Page Record -->
<record id="portfolio_page" model="website.page">
<field name="name">Portfolio</field>
<field name="type">qweb</field>
<field name="url">/portfolio</field>
<field name="website_published">True</field>
<field name="key">theme_aakriti_v2.portfolio_page_template</field>
<field name="arch" type="xml">
<t name="Portfolio" t-name="theme_aakriti_v2.portfolio_page_template">
<t t-call="website.layout">
<div id="wrap" class="oe_structure oe_empty" style="background-color: var(--v2-cream); font-family: 'Jost', sans-serif;">
<!-- Inner Banner -->
<section class="v2-inner-banner" style="background-image: url('/theme_aakriti_v2/static/src/img/inner-banner/1.webp');">
<div class="v2-inner-banner-content">
<h1 class="v2-inner-banner-title" style="font-family: var(--v2-font-display);">Portfolio</h1>
<div class="v2-inner-banner-breadcrumb">
<a href="/">Home</a> / <span class="v2-active">Portfolio</span>
</div>
</div>
</section>
<!-- Portfolio Section -->
<section class="v2-portfolio-section">
<div class="v2-portfolio-tabs">
<button class="v2-portfolio-tab active" data-filter="all">
<img src="/theme_aakriti_v2/static/src/img/portfolio/all.png"
alt="All Events"
width="40"
height="40"/>
ALL
</button>
<button class="v2-portfolio-tab" data-filter="weddings">
<img src="/theme_aakriti_v2/static/src/img/portfolio/wedding.png"
alt="Weddings"
width="40"
height="40"/>
WEDDINGS
</button>
<button class="v2-portfolio-tab" data-filter="pre-wedding">
<img src="/theme_aakriti_v2/static/src/img/portfolio/pre-wedding.png"
alt="Pre Wedding"
width="40"
height="40"/>
PRE-WEDDING
</button>
<button class="v2-portfolio-tab" data-filter="engagements">
<img src="/theme_aakriti_v2/static/src/img/portfolio/engagements.png"
alt="Engagements"
width="40"
height="40"/>
ENGAGEMENTS
</button>
<button class="v2-portfolio-tab" data-filter="celebrations">
<img src="/theme_aakriti_v2/static/src/img/portfolio/celebrations.png"
alt="Celebrations"
width="40"
height="40"/>
CELEBRATIONS
</button>
<button class="v2-portfolio-tab" data-filter="corporate">
<img src="/theme_aakriti_v2/static/src/img/portfolio/corporate.png"
alt="Corporate"
width="40"
height="40"/>
CORPORATE
</button>
<button class="v2-portfolio-tab" data-filter="cultural">
<img src="/theme_aakriti_v2/static/src/img/portfolio/cultural.png"
alt="Cultural"
width="40"
height="40"/>
CULTURAL
</button>
<button class="v2-portfolio-tab" data-filter="festivals">
<img src="/theme_aakriti_v2/static/src/img/portfolio/festivals.png"
alt="Festivals"
width="40"
height="40"/>
FESTIVALS
</button>
</div>
<div class="v2-masonry-grid" id="v2-portfolio-grid">
<!-- Portfolio items injected by theme_v2.js -->
</div>
</section>
</div>
</t>
</t>
</field>
</record>
</odoo>

File diff suppressed because it is too large Load Diff