Property Amenities Grid
blockreal-estateBuilding and luxury residential amenities matrix featuring categorized lifestyle zones, interactive category tabs, instant keyword search, and detailed feature cards with pool photography thumbnail.
Installation
$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/property-amenities-grid.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/property-amenities-grid.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/property-amenities-grid.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/property-amenities-grid.jsonNamed registry:
npx shadcn-vue@latest add @uipkge/property-amenities-gridInstalls to:app/components/blocks/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
title | string | 'World-Class Amenities & Services' | optional |
subtitle | string | 'Designed for wellness | optional |
badge | string | 'Building Features & Lifestyle' | optional |
class | HTMLAttributes['class'] | — | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
AmenityCategoryinterface AmenityCategory {
id: CategoryId
label: string
count: number
icon: any
description: string
}npm dependencies
Files installed (1)
app/components/blocks/PropertyAmenitiesGrid.vue41.4 kB
<script setup lang="ts"> import { computed, ref, type HTMLAttributes } from 'vue' import { ArrowRight, Building2, Calendar, Car, Check, CircleDot, Clock, Compass, ConciergeBell, Dumbbell, FileText, Flame, HeartPulse, Laptop, MapPin, Package, PawPrint, Search, ShieldCheck, Shirt, Video, Wifi, X, } from 'lucide-vue-next' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Separator } from '@/components/ui/separator' import { cn } from '@/lib/utils' interface Props { title?: string subtitle?: string badge?: string class?: HTMLAttributes['class'] } const props = withDefaults(defineProps<Props>(), { title: 'World-Class Amenities & Services', subtitle: 'Designed for wellness, productivity, and quiet luxury living.', badge: 'Building Features & Lifestyle', }) type CategoryId = 'all' | 'wellness' | 'work' | 'services' | 'parking' interface AmenityCategory { id: CategoryId label: string count: number icon: any description: string } const categories: AmenityCategory[] = [ { id: 'all', label: 'All Amenities', count: 24, icon: Building2, description: 'Explore the full suite of resident services, wellness facilities, and private lounges.', }, { id: 'wellness', label: 'Wellness & Recreation', count: 4, icon: HeartPulse, description: 'Resort-inspired spaces to rejuvenate body and mind, featuring sky-high fitness and hydrotherapy.', }, { id: 'work', label: 'Work & Productivity', count: 3, icon: Laptop, description: 'High-speed professional environments for focused execution, remote meetings, and collaboration.', }, { id: 'services', label: 'Convenience & Services', count: 3, icon: ConciergeBell, description: 'White-glove 24/7 concierge, contactless cold storage lockers, and on-demand valet care.', }, { id: 'parking', label: 'Parking & Pet Care', count: 2, icon: Car, description: 'Subterranean EV charging and a dedicated pet-care suite.', }, ] const activeCategory = ref<CategoryId>('all') const searchQuery = ref('') const isSearching = computed(() => searchQuery.value.trim().length > 0) function matchesQuery(text: string): boolean { if (!isSearching.value) return true return text.toLowerCase().includes(searchQuery.value.toLowerCase().trim()) } const showWellness = computed(() => { if (activeCategory.value !== 'all' && activeCategory.value !== 'wellness') return false if (!isSearching.value) return true return ( matchesQuery('Heated Rooftop Infinity Pool') || matchesQuery('Equinox-style Fitness Center & Yoga Studio') || matchesQuery('Nordic Sauna & Cold Plunge Spa') || matchesQuery('Private Tennis & Pickleball Court') || matchesQuery('wellness recreation pool gym sauna') ) }) const showWork = computed(() => { if (activeCategory.value !== 'all' && activeCategory.value !== 'work') return false if (!isSearching.value) return true return ( matchesQuery('Resident Co-Working Lounge with Private Pods') || matchesQuery('Executive Conference Room with AV') || matchesQuery('High-Speed 10 Gbps Fiber WiFi throughout') || matchesQuery('work productivity office wifi conference') ) }) const showServices = computed(() => { if (activeCategory.value !== 'all' && activeCategory.value !== 'services') return false if (!isSearching.value) return true return ( matchesQuery('24/7 Doorman & Concierge Desk') || matchesQuery('Automated Package Lockers & Cold Storage Delivery') || matchesQuery('Dry Cleaning Valet & Laundry Services') || matchesQuery('convenience services concierge doorman package laundry') ) }) const showParking = computed(() => { if (activeCategory.value !== 'all' && activeCategory.value !== 'parking') return false if (!isSearching.value) return true return ( matchesQuery('Secure Underground Parking with EV Chargers') || matchesQuery('Private Rooftop Dog Park & Pet Spa Station') || matchesQuery('parking ev charger pet care dog park') ) }) </script> <template> <section data-slot="property-amenities-grid" :class="cn('bg-background text-foreground w-full py-12 sm:py-16 lg:py-20', props.class)" > <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"> <!-- Top Section Header --> <div class="flex flex-col items-start justify-between gap-6 lg:flex-row lg:items-end"> <div class="max-w-2xl space-y-3"> <div class="flex flex-wrap items-center gap-2"> <Badge variant="outline" class="border-primary/30 bg-primary/5 text-primary gap-1.5 px-3 py-1 text-xs font-medium" > <Compass class="size-3.5" /> {{ props.badge }} </Badge> <Badge variant="secondary" class="gap-1 px-2.5 py-1 text-xs font-medium"> <Building2 class="size-3" /> 24 Total Amenities </Badge> </div> <h2 class="text-foreground text-3xl font-bold tracking-tight sm:text-4xl"> {{ props.title }} </h2> <p class="text-muted-foreground text-base sm:text-lg"> {{ props.subtitle }} </p> </div> <!-- Quick Summary Stats Bar --> <div class="grid grid-cols-2 gap-3 sm:grid-cols-4 sm:gap-4"> <div class="bg-card border-border rounded-xl border p-3 shadow-xs"> <p class="text-foreground font-mono text-xl font-bold">24</p> <p class="text-muted-foreground text-xs font-medium">Curated Amenities</p> </div> <div class="bg-card border-border rounded-xl border p-3 shadow-xs"> <p class="text-foreground font-mono text-xl font-bold">4</p> <p class="text-muted-foreground text-xs font-medium">Lifestyle Zones</p> </div> <div class="bg-card border-border rounded-xl border p-3 shadow-xs"> <p class="text-foreground font-mono text-xl font-bold">24/7</p> <p class="text-muted-foreground text-xs font-medium">Concierge Access</p> </div> <div class="bg-card border-border rounded-xl border p-3 shadow-xs"> <p class="text-foreground font-mono text-xl font-bold">100%</p> <p class="text-muted-foreground text-xs font-medium">Pet Friendly</p> </div> </div> </div> <Separator class="my-8" /> <!-- Interactive Controls: Category Filter Tabs & Quick Search --> <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"> <!-- Category Filter Pills --> <div class="flex flex-wrap items-center gap-2"> <Button v-for="cat in categories" :key="cat.id" size="sm" :variant="activeCategory === cat.id ? 'default' : 'outline'" class="h-8 gap-1.5 rounded-full px-3.5 text-xs font-medium" @click="activeCategory = cat.id" > <component :is="cat.icon" class="size-3.5" /> <span>{{ cat.label }}</span> <span :class=" cn( 'py-0.2 ml-1 rounded-full px-1.5 font-mono text-xs', activeCategory === cat.id ? 'bg-primary-foreground/20 text-primary-foreground' : 'bg-muted text-muted-foreground', ) " > {{ cat.count }} </span> </Button> </div> <!-- Keyword Search Input --> <div class="relative w-full sm:w-72"> <Search class="text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2" /> <input v-model="searchQuery" type="text" placeholder="Search amenities..." class="bg-card border-border placeholder:text-muted-foreground focus-visible:ring-primary h-9 w-full min-w-0 rounded-lg border pr-8 pl-9 text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none" /> <button v-if="searchQuery" type="button" class="text-muted-foreground hover:text-foreground absolute top-1/2 right-2.5 -translate-y-1/2" aria-label="Clear search" @click="searchQuery = ''" > <X class="size-3.5" /> </button> </div> </div> <!-- Search Results Indicator --> <div v-if="isSearching" class="bg-muted/40 border-border mt-4 flex items-center justify-between rounded-lg border px-4 py-2 text-xs" > <span class="text-muted-foreground"> Showing matching results for <strong class="text-foreground">"{{ searchQuery }}"</strong> </span> <button aria-label="Clear search" type="button" class="text-primary font-medium hover:underline" @click="searchQuery = ''" > Reset Filter </button> </div> <!-- Categories Container --> <div class="mt-10 space-y-14"> <!-- ================================================================= --> <!-- CATEGORY 1: WELLNESS & RECREATION --> <!-- ================================================================= --> <div v-if="showWellness" class="space-y-6"> <!-- Category Header --> <div class="flex flex-col items-start justify-between gap-2 sm:flex-row sm:items-center"> <div class="flex items-center gap-3"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <HeartPulse class="size-5" /> </div> <div> <h3 class="text-foreground text-xl font-bold tracking-tight">Wellness & Recreation</h3> <p class="text-muted-foreground text-xs sm:text-sm"> Resort-inspired spaces to rejuvenate body and mind with skyline views and recovery hydrotherapy. </p> </div> </div> <Badge variant="outline" class="border-border text-muted-foreground text-xs font-medium"> 4 Featured Amenities </Badge> </div> <!-- Wellness Cards Grid --> <div class="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3"> <!-- Card 1: Heated Rooftop Infinity Pool (Featured Card with Photo Thumbnail) --> <Card v-if="matchesQuery('Heated Rooftop Infinity Pool pool skyline swim cabana')" class="group border-border bg-card relative flex flex-col justify-between overflow-hidden shadow-xs transition-colors duration-300 hover:shadow-md md:col-span-2 lg:col-span-2" > <div class="relative h-64 w-full overflow-hidden sm:h-72"> <img src="https://images.unsplash.com/photo-1576013551627-0cc20b96c2a7?w=1200&auto=format&fit=crop&q=80" alt="Heated Rooftop Infinity Pool" class="size-full object-cover transition-transform duration-500 ease-out group-hover:scale-105" /> <div class="absolute inset-0 bg-gradient-to-t from-black/85 via-black/35 to-transparent" /> <div class="absolute top-4 left-4 flex flex-wrap gap-2"> <Badge class="bg-primary text-primary-foreground border-transparent text-xs font-medium shadow-xs"> Featured Highlight </Badge> <Badge variant="secondary" class="bg-background/80 text-foreground border-white/20 text-xs font-medium backdrop-blur-md" > Year-Round Heated (84°F) </Badge> </div> <div class="absolute right-4 bottom-4 left-4 text-white"> <div class="flex items-center gap-1.5 text-xs text-zinc-300"> <MapPin class="size-3.5 text-zinc-300" /> <span>Level 42 Sky Deck</span> <span class="mx-1.5">·</span> <Clock class="size-3.5 text-zinc-300" /> <span>6:00 AM – 10:00 PM Daily</span> </div> </div> </div> <CardContent class="flex flex-1 flex-col justify-between p-6"> <div class="space-y-2"> <CardTitle class="text-foreground text-xl font-bold">Heated Rooftop Infinity Pool</CardTitle> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> 50-meter temperature-controlled infinity pool on the 42nd floor featuring private cabanas, teak daybeds, outdoor sun deck, and poolside refreshment bar with 360-degree panoramic skyline views. </CardDescription> </div> <div class="border-border mt-6 flex flex-wrap items-center gap-2 border-t pt-4"> <Badge variant="outline" class="bg-muted/30 text-xs font-normal"> <Check class="text-success mr-1 size-3" /> Panoramic Views </Badge> <Badge variant="outline" class="bg-muted/30 text-xs font-normal"> <Check class="text-success mr-1 size-3" /> Private Cabanas </Badge> <Badge variant="outline" class="bg-muted/30 text-xs font-normal"> <Check class="text-success mr-1 size-3" /> Towel Valet </Badge> <Badge variant="outline" class="bg-muted/30 text-xs font-normal"> <Check class="text-success mr-1 size-3" /> Poolside Bar </Badge> </div> </CardContent> </Card> <!-- Card 2: Equinox-style Fitness Center & Yoga Studio --> <Card v-if="matchesQuery('Equinox-style Fitness Center & Yoga Studio gym pilates workout')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Dumbbell class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">24/7 Access</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Levels 3 & 4 </div> <CardTitle class="text-foreground text-lg font-bold"> Equinox-style Fitness Center & Yoga Studio </CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> Multi-level 4,500 sq ft strength and conditioning training facility outfitted with Woodway treadmills, Peloton bikes, Olympic lifting racks, and a dedicated sunlit yoga and reformer Pilates studio. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">Woodway & Peloton</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Reformer Pilates</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Personal Trainers</Badge> </div> </CardContent> </Card> <!-- Card 3: Nordic Sauna & Cold Plunge Spa --> <Card v-if="matchesQuery('Nordic Sauna & Cold Plunge Spa steam therapy recovery')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Flame class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">Hydrotherapy</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Level 3 Spa Suite </div> <CardTitle class="text-foreground text-lg font-bold">Nordic Sauna & Cold Plunge Spa</CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> Therapeutic cedarwood dry sauna, aromatic eucalyptus steam room, and dual 48°F contrast therapy cold plunge pools engineered for post-workout muscle recovery and immune resilience. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">48°F Cold Plunge</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Cedar Dry Sauna</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Eucalyptus Steam</Badge> </div> </CardContent> </Card> <!-- Card 4: Private Tennis & Pickleball Court --> <Card v-if="matchesQuery('Private Tennis & Pickleball Court sports rooftop court')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md md:col-span-2 lg:col-span-2" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <CircleDot class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">Rooftop Court</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Level 43 Sports Deck </div> <CardTitle class="text-foreground text-lg font-bold">Private Tennis & Pickleball Court</CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> Championship-grade rooftop regulation court with multi-sport cushioned acrylic surface, tournament-spec LED night illumination, automated Spinshot ball machines, and resident league reservations. </CardDescription> <div class="flex flex-wrap gap-2 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">Tournament LEDs</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Ball Machine Loan</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">App Reservation</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Skyline Backdrop</Badge> </div> </CardContent> </Card> </div> </div> <!-- ================================================================= --> <!-- CATEGORY 2: WORK & PRODUCTIVITY --> <!-- ================================================================= --> <div v-if="showWork" class="space-y-6"> <!-- Category Header --> <div class="flex flex-col items-start justify-between gap-2 sm:flex-row sm:items-center"> <div class="flex items-center gap-3"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Laptop class="size-5" /> </div> <div> <h3 class="text-foreground text-xl font-bold tracking-tight">Work & Productivity</h3> <p class="text-muted-foreground text-xs sm:text-sm"> Executive business facilities, private acoustic pods, and ultra-high-speed fiber network. </p> </div> </div> <Badge variant="outline" class="border-border text-muted-foreground text-xs font-medium"> 3 Featured Amenities </Badge> </div> <!-- Work Cards Grid --> <div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3"> <!-- Card 1: Resident Co-Working Lounge with Private Pods --> <Card v-if="matchesQuery('Resident Co-Working Lounge with Private Pods workspace zoom office')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Laptop class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">Quiet Focus</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Level 2 Mezzanine </div> <CardTitle class="text-foreground text-lg font-bold"> Resident Co-Working Lounge with Private Pods </CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> Spacious communal workspace with Herman Miller ergonomic seating, acoustic focus booths, private soundproof Zoom phone pods, and complimentary artisan espresso and nitro cold brew bar. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">Soundproof Pods</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Herman Miller Chairs</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Espresso Bar</Badge> </div> </CardContent> </Card> <!-- Card 2: Executive Conference Room with AV --> <Card v-if="matchesQuery('Executive Conference Room with AV boardroom meeting video presentation')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Video class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">App Booking</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Level 2 North Wing </div> <CardTitle class="text-foreground text-lg font-bold"> Executive Conference Room with AV </CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> 16-person board room equipped with 85-inch 4K HDR video conferencing displays, spatial beamforming microphone arrays, digital whiteboard, and integrated presentation hub. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">16-Seat Capacity</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">4K Video Suite</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Digital Whiteboard</Badge> </div> </CardContent> </Card> <!-- Card 3: High-Speed 10 Gbps Fiber WiFi throughout --> <Card v-if="matchesQuery('High-Speed 10 Gbps Fiber WiFi throughout wifi internet mesh')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md sm:col-span-2 lg:col-span-1" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Wifi class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">Ultra-Fast</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Building-wide Coverage </div> <CardTitle class="text-foreground text-lg font-bold"> High-Speed 10 Gbps Fiber WiFi throughout </CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> Enterprise-grade symmetrical 10 Gbps redundant fiber backbone with Wi-Fi 7 access points delivering uninterrupted zero-drop roaming across all indoor lounges and outdoor terraces. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">Wi-Fi 7 Mesh</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">10 Gbps Symmetrical</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Zero Roam Handoff</Badge> </div> </CardContent> </Card> </div> </div> <!-- ================================================================= --> <!-- CATEGORY 3: CONVENIENCE & SERVICES --> <!-- ================================================================= --> <div v-if="showServices" class="space-y-6"> <!-- Category Header --> <div class="flex flex-col items-start justify-between gap-2 sm:flex-row sm:items-center"> <div class="flex items-center gap-3"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <ConciergeBell class="size-5" /> </div> <div> <h3 class="text-foreground text-xl font-bold tracking-tight">Convenience & Services</h3> <p class="text-muted-foreground text-xs sm:text-sm"> White-glove hospitality, smart package handling, and premium garment care at your fingertips. </p> </div> </div> <Badge variant="outline" class="border-border text-muted-foreground text-xs font-medium"> 3 Featured Amenities </Badge> </div> <!-- Convenience Cards Grid --> <div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3"> <!-- Card 1: 24/7 Doorman & Concierge Desk --> <Card v-if="matchesQuery('24/7 Doorman & Concierge Desk doorman front desk hospitality security')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <ConciergeBell class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">24/7 Attended</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Main Lobby Entrance </div> <CardTitle class="text-foreground text-lg font-bold"> 24/7 Doorman & Concierge Desk </CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> Round-the-clock uniform concierge team providing white-glove resident hospitality, verified visitor check-in, key holding, luggage assistance, taxi dispatch, and dining reservations. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">White-Glove Staff</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Key Holding</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Guest Escort</Badge> </div> </CardContent> </Card> <!-- Card 2: Automated Package Lockers & Cold Storage Delivery --> <Card v-if="matchesQuery('Automated Package Lockers & Cold Storage Delivery parcel grocery refrigerated')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Package class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">Smart Storage</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Lobby Concourse </div> <CardTitle class="text-foreground text-lg font-bold"> Automated Package Lockers & Cold Storage Delivery </CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> Contactless Luxer One smart package room with digital PIN and barcode pickup alerts, secure oversized parcel rooms, and commercial-grade refrigerated storage for fresh grocery deliveries. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">Cold Grocery Lockers</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">24/7 App QR Pickup</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Oversized Item Vault</Badge> </div> </CardContent> </Card> <!-- Card 3: Dry Cleaning Valet & Laundry Services --> <Card v-if="matchesQuery('Dry Cleaning Valet & Laundry Services garment tailoring wash')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md sm:col-span-2 lg:col-span-1" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Shirt class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">Valet Care</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Service Concourse B1 </div> <CardTitle class="text-foreground text-lg font-bold"> Dry Cleaning Valet & Laundry Services </CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> On-demand eco-friendly dry cleaning drop-off lockers with 24-hour turnaround, garment pressing, shoe shining, tailored alterations, and in-locker contactless delivery return. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">24h Turnaround</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Eco-Friendly Cleaning</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Tailored Alterations</Badge> </div> </CardContent> </Card> </div> </div> <!-- ================================================================= --> <!-- CATEGORY 4: PARKING & PET CARE --> <!-- ================================================================= --> <div v-if="showParking" class="space-y-6"> <!-- Category Header --> <div class="flex flex-col items-start justify-between gap-2 sm:flex-row sm:items-center"> <div class="flex items-center gap-3"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Car class="size-5" /> </div> <div> <h3 class="text-foreground text-xl font-bold tracking-tight">Parking & Pet Care</h3> <p class="text-muted-foreground text-xs sm:text-sm"> Subterranean EV charging bays and private rooftop pet facilities for four-legged residents. </p> </div> </div> <Badge variant="outline" class="border-border text-muted-foreground text-xs font-medium"> 2 Featured Amenities </Badge> </div> <!-- Parking & Pet Care Cards Grid --> <div class="grid grid-cols-1 gap-6 md:grid-cols-2"> <!-- Card 1: Secure Underground Parking with EV Chargers --> <Card v-if="matchesQuery('Secure Underground Parking with EV Chargers electric vehicle garage stall')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <Car class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">EV Ready</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Levels P1 – P3 Subterranean </div> <CardTitle class="text-foreground text-lg font-bold"> Secure Underground Parking with EV Chargers </CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> Gated subterranean multi-level garage featuring automated license plate recognition entry gates, assigned private stalls, 48 universal Level 2 EV charging stations, and dedicated bike storage. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">48 EV Charging Ports</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">LPR Auto-Gate</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Covered Bike Vault</Badge> </div> </CardContent> </Card> <!-- Card 2: Private Rooftop Dog Park & Pet Spa Station --> <Card v-if="matchesQuery('Private Rooftop Dog Park & Pet Spa Station canine grooming wash animal')" class="border-border bg-card hover:border-primary/30 flex flex-col justify-between shadow-xs transition-colors duration-300 hover:shadow-md" > <CardHeader class="space-y-3 p-6 pb-2"> <div class="flex items-center justify-between"> <div class="bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl"> <PawPrint class="size-5" /> </div> <Badge variant="secondary" class="text-xs font-medium">Pet Friendly</Badge> </div> <div class="space-y-1"> <div class="text-muted-foreground flex items-center gap-1 text-xs"> <MapPin class="text-primary size-3" /> Level 44 Sky Deck </div> <CardTitle class="text-foreground text-lg font-bold"> Private Rooftop Dog Park & Pet Spa Station </CardTitle> </div> </CardHeader> <CardContent class="space-y-4 p-6 pt-2"> <CardDescription class="text-muted-foreground text-sm leading-relaxed"> Enclosed rooftop canine park with anti-microbial synthetic turf and agility obstacles, paired with an indoor heated pet wash spa featuring stainless steel wash tubs and pro blow dry station. </CardDescription> <div class="flex flex-wrap gap-1.5 pt-2"> <Badge variant="outline" class="bg-muted/20 text-xs">Anti-Microbial Turf</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Heated Wash Tubs</Badge> <Badge variant="outline" class="bg-muted/20 text-xs">Pro Blow Dryers</Badge> </div> </CardContent> </Card> </div> </div> </div> <!-- Bottom Callout Card: Resident Keycard App & Tour Booking --> <Card class="bg-muted/30 border-border mt-16 overflow-hidden border shadow-xs"> <CardContent class="flex flex-col items-start justify-between gap-6 p-6 sm:p-8 md:flex-row md:items-center"> <div class="flex items-start gap-4"> <div class="bg-primary/10 text-primary flex size-12 shrink-0 items-center justify-center rounded-xl"> <ShieldCheck class="size-6" /> </div> <div class="space-y-1"> <h4 class="text-foreground text-base font-semibold sm:text-lg"> Keyless Resident Access & Mobile App Integration </h4> <p class="text-muted-foreground max-w-2xl text-xs sm:text-sm"> All 24 building amenities are digitally accessible via NFC mobile key, offering instant cabana and conference room reservations, real-time fitness occupancy, and automated guest passes. </p> </div> </div> <div class="flex flex-wrap items-center gap-3"> <Button variant="outline" size="sm" class="gap-1.5 text-xs"> <FileText class="size-3.5" /> Amenity Guide PDF </Button> <Button size="sm" class="gap-1.5 text-xs"> <Calendar class="size-3.5" /> Schedule a Tour <ArrowRight class="size-3.5" /> </Button> </div> </CardContent> </Card> </div> </section> </template>
Raw manifest:https://uipkge.dev/r/vue/property-amenities-grid.json