From 6e2101d019c3ab1e91bf41ff5fb5c2a9705f088e Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Fri, 12 Dec 2025 13:15:15 +0000 Subject: [PATCH] feat: add Usage Limits Panel component with usage tracking and visual indicators for limits style: implement custom color schemes and gradients for account section, enhancing visual hierarchy --- .cursorrules | 380 ++++++++++ CHANGELOG.md | 224 +++++- PLAN-LIMITS-IMPLEMENTATION.md | 465 ++++++++++++ backend/igny8_core/auth/admin.py | 17 +- ...lusters_plan_max_content_ideas_and_more.py | 49 ++ .../0014_add_usage_tracking_to_account.py | 49 ++ backend/igny8_core/auth/models.py | 48 ++ backend/igny8_core/auth/serializers.py | 3 + backend/igny8_core/auth/views.py | 8 + backend/igny8_core/business/billing/models.py | 77 ++ .../billing/services/limit_service.py | 357 +++++++++ backend/igny8_core/business/billing/urls.py | 3 + backend/igny8_core/business/billing/views.py | 41 ++ backend/igny8_core/business/content/models.py | 49 ++ .../services/content_generation_service.py | 8 + backend/igny8_core/celery.py | 9 + ...ent_account_status_created_idx_and_more.py | 53 -- .../billing/migrations/0015_planlimitusage.py | 38 + backend/igny8_core/tasks/plan_limits.py | 168 +++++ backend/igny8_core/utils/word_counter.py | 137 ++++ docker-compose.app.yml | 22 - docs/PLAN-LIMITS.md | 677 ++++++++++++++++++ .../components/billing/UsageLimitsPanel.tsx | 245 +++++++ .../src/components/ui/pricing-table/index.tsx | 67 ++ frontend/src/index.css | 1 + .../src/pages/account/PlansAndBillingPage.tsx | 10 + .../src/pages/account/UsageAnalyticsPage.tsx | 17 +- frontend/src/services/billing.api.ts | 50 ++ frontend/src/styles/account-colors.css | 435 +++++++++++ 29 files changed, 3622 insertions(+), 85 deletions(-) create mode 100644 .cursorrules create mode 100644 PLAN-LIMITS-IMPLEMENTATION.md create mode 100644 backend/igny8_core/auth/migrations/0013_plan_max_clusters_plan_max_content_ideas_and_more.py create mode 100644 backend/igny8_core/auth/migrations/0014_add_usage_tracking_to_account.py create mode 100644 backend/igny8_core/business/billing/services/limit_service.py delete mode 100644 backend/igny8_core/modules/billing/migrations/0014_remove_payment_payment_account_status_created_idx_and_more.py create mode 100644 backend/igny8_core/modules/billing/migrations/0015_planlimitusage.py create mode 100644 backend/igny8_core/tasks/plan_limits.py create mode 100644 backend/igny8_core/utils/word_counter.py create mode 100644 docs/PLAN-LIMITS.md create mode 100644 frontend/src/components/billing/UsageLimitsPanel.tsx create mode 100644 frontend/src/styles/account-colors.css diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 00000000..50ffac33 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,380 @@ +# IGNY8 Development Rules & Standards + +**Project:** IGNY8 - AI-Powered Content Platform +**Version:** v1.0.0 +**Last Updated:** December 12, 2025 + +--- + +## 📋 General Development Principles + +### 1. **Always Read Documentation First** +Before making changes, consult these critical docs: +- `ARCHITECTURE-KNOWLEDGE-BASE.md` - System architecture and design patterns +- `CHANGELOG.md` - Recent changes and version history +- `IGNY8-COMPLETE-FEATURES-GUIDE.md` - Complete feature set and capabilities +- `docs/00-SYSTEM/` - Core system architecture +- `docs/10-BACKEND/` - Backend models, services, APIs +- `docs/20-API/` - API endpoint documentation +- `docs/30-FRONTEND/` - Frontend components and architecture +- `docs/40-WORKFLOWS/` - Business workflows and processes + +### 2. **Maintain Consistency** +- **API Design:** Follow existing RESTful patterns in `backend/igny8_core/*/views.py` +- **Models:** Use existing base classes (`SoftDeletableModel`, `AccountBaseModel`, `SiteSectorBaseModel`) +- **Services:** Follow service pattern in `backend/igny8_core/business/*/services/` +- **AI Functions:** Use AI framework in `backend/igny8_core/ai/` (not legacy `utils/ai_processor.py`) +- **Frontend Components:** Use existing component library in `frontend/src/components/` +- **Styling:** Use TailwindCSS classes, follow existing design system in `frontend/DESIGN_SYSTEM.md` +- **State Management:** Use Zustand stores in `frontend/src/store/` + +### 3. **Multi-Tenancy Rules** +- **ALWAYS scope by account:** Every query must filter by account +- **Site/Sector scoping:** Use `SiteSectorBaseModel` for site-specific data +- **Permissions:** Check permissions via `IsAuthenticatedAndActive`, `HasTenantAccess`, role-based permissions +- **No cross-tenant access:** Validate account ownership before operations + +### 4. **API Endpoint Rules** +- **Use existing API structure:** All user-facing endpoints under `/api/v1//`, admin endpoints under `/api/v1//admin/` +- **No parallel API systems:** Register all endpoints in module's `urls.py`, test via Swagger at `/api/docs/` before documenting +- **Document in Swagger:** Ensure drf-spectacular auto-generates docs; verify endpoint appears at `/api/docs/` and `/api/schema/` + +--- + +## 📝 Change Management & Versioning + +alwys udpated changelog with incremental updates, as fixed aded or modified for each version update, dotn remove or modify teh exsitng version changes +### Versioning Scheme: `v..` + +**Example:** v1.2.5 +- `MAJOR when asked` (1.x.x): Breaking changes, major features, architecture changes +- `MAJOR` (x.2.x): New features, modules, significant enhancements +- `MINOR/PATCH` (x.x.5): Bug fixes, small improvements, refactors + +### Changelog Update Rules + +#### **For EVERY Change:** +1. **Update version number** in `CHANGELOG.md` +2. **Increment PATCH** (v1.0.x → v1.0.1) for: + - Bug fixes + - Small improvements + - Code refactors + - Documentation updates + - UI/UX tweaks + +3. **Increment MINOR** (v1.x.0 → v1.1.0) for: + - New features + - New API endpoints + - New components + - New services + - Significant enhancements + +4. **Increment MAJOR** (vx.0.0 → v2.0.0) for: + - Breaking API changes + - Database schema breaking changes + - Architecture overhauls + - Major refactors affecting multiple modules + +#### **Changelog Entry Format:** +```markdown +## v1.2.5 - December 12, 2025 + +### Fixed +- User logout issue when switching accounts +- Payment confirmation modal amount display + +### Changed +- Updated session storage from database to Redis +- Enhanced credit balance widget UI + +### Added +- Plan limits enforcement system +- Monthly reset task for usage tracking +``` + +### **For Major Refactors:** +1. **Create detailed TODO list** before starting +2. **Document current state** in CHANGELOG +3. **Create implementation checklist** (markdown file in root or docs/) +4. **Track progress** with checklist updates +5. **Test thoroughly** before committing +6. **Update CHANGELOG** with all changes made +7. **Update version** to next MINOR or MAJOR + +--- + +## 🏗️ Code Organization Standards + +### Backend Structure +``` +backend/igny8_core/ +├── auth/ # Authentication, users, accounts, plans +├── business/ # Business logic services +│ ├── automation/ # Automation pipeline +│ ├── billing/ # Billing, credits, invoices +│ ├── content/ # Content generation +│ ├── integration/ # External integrations +│ ├── linking/ # Internal linking +│ ├── optimization/ # Content optimization +│ ├── planning/ # Keywords, clusters, ideas +│ └── publishing/ # WordPress publishing +├── ai/ # AI framework (NEW - use this) +├── utils/ # Utility functions +├── tasks/ # Celery tasks +└── modules/ # Legacy modules (being phased out) +``` + +### Frontend Structure +``` +frontend/src/ +├── components/ # Reusable components +├── pages/ # Page components +├── store/ # Zustand state stores +├── services/ # API service layer +├── hooks/ # Custom React hooks +├── utils/ # Utility functions +├── types/ # TypeScript types +└── marketing/ # Marketing site +``` + +--- + +## 🔧 Development Workflow + +### 1. **Planning Phase** +- [ ] Read relevant documentation +- [ ] Understand existing patterns +- [ ] Create TODO list for complex changes +- [ ] Identify affected components/modules +- [ ] Plan database changes (if any) + +### 2. **Implementation Phase** +- [ ] Follow existing code patterns +- [ ] Use proper base classes and mixins +- [ ] Add proper error handling +- [ ] Validate input data +- [ ] Check permissions and scope +- [ ] Write clean, documented code +- [ ] Use type hints (Python) and TypeScript types + +### 3. **Testing Phase** +- [ ] Test locally with development data +- [ ] Test multi-tenancy isolation +- [ ] Test permissions and access control +- [ ] Test error cases +- [ ] Verify no breaking changes +- [ ] Check frontend-backend integration + +### 4. **Documentation Phase** +- [ ] Update CHANGELOG.md +- [ ] Update version number +- [ ] Update relevant docs (if architecture/API changes) +- [ ] Add code comments for complex logic +- [ ] Update API documentation (if endpoints changed) + +--- + +## 🎯 Specific Development Rules + +### Backend Development + +#### **Models:** +```python +# ALWAYS inherit from proper base classes +from igny8_core.auth.models import SiteSectorBaseModel + +class MyModel(SoftDeletableModel, SiteSectorBaseModel): + # Your fields here + pass +``` + +#### **Services:** +```python +# Follow service pattern +class MyService: + def __init__(self): + self.credit_service = CreditService() + self.limit_service = LimitService() + + def my_operation(self, account, site, **kwargs): + # 1. Validate permissions + # 2. Check limits/credits + # 3. Perform operation + # 4. Track usage + # 5. Return result + pass +``` + +#### **API Views:** +```python +# Use proper permission classes +class MyViewSet(viewsets.ModelViewSet): + permission_classes = [IsAuthenticatedAndActive, HasTenantAccess] + + def get_queryset(self): + # ALWAYS scope by account + return MyModel.objects.filter( + site__account=self.request.user.account + ) +``` + +#### **Migrations:** +- Run `python manage.py makemigrations` after model changes +- Test migrations: `python manage.py migrate --plan` +- Never edit existing migrations +- Use data migrations for complex data changes + +### Frontend Development + +#### **Components:** +```typescript +// Use existing component library +import { Card } from '@/components/ui/card'; +import Button from '@/components/ui/button/Button'; + +// Follow naming conventions +export default function MyComponent() { + // Component logic +} +``` + +#### **State Management:** +```typescript +// Use Zustand stores +import { useAuthStore } from '@/store/authStore'; + +const { user, account } = useAuthStore(); +``` + +#### **API Calls:** +```typescript +// Use fetchAPI from services/api.ts +import { fetchAPI } from '@/services/api'; + +const data = await fetchAPI('/v1/my-endpoint/'); +``` + +#### **Styling:** +```typescript +// Use TailwindCSS classes +
+

+ My Heading +

+
+``` + +--- + +## 🚫 Common Pitfalls to Avoid + +### **DON'T:** +- ❌ Skip account scoping in queries +- ❌ Use legacy AI processor (`utils/ai_processor.py`) - use `ai/` framework +- ❌ Hardcode values - use settings or constants +- ❌ Forget error handling +- ❌ Skip permission checks +- ❌ Create duplicate components - reuse existing +- ❌ Use inline styles - use TailwindCSS +- ❌ Forget to update CHANGELOG +- ❌ Use workarounds - fix the root cause +- ❌ Skip migrations after model changes + +### **DO:** +- ✅ Read documentation before coding +- ✅ Follow existing patterns +- ✅ Use proper base classes +- ✅ Check permissions and limits +- ✅ Handle errors gracefully +- ✅ Return valid errors, not fallbacks +- ✅ Update CHANGELOG for every change +- ✅ Test multi-tenancy isolation +- ✅ Use TypeScript types +- ✅ Write clean, documented code + +--- + +## 🔍 Code Review Checklist + +Before committing code, verify: +- [ ] Follows existing code patterns +- [ ] Properly scoped by account/site +- [ ] Permissions checked +- [ ] Error handling implemented +- [ ] No breaking changes +- [ ] CHANGELOG.md updated +- [ ] Version number incremented +- [ ] Documentation updated (if needed) +- [ ] Tested locally +- [ ] No console errors or warnings +- [ ] TypeScript types added/updated +- [ ] Migrations created (if model changes) + +--- + +## 📚 Key Architecture Concepts + +### **Credit System:** +- All AI operations cost credits +- Check credits before operation: `CreditService.check_credits()` +- Deduct after operation: `CreditService.deduct_credits()` +- Track in `CreditUsageLog` table + +### **Limit System:** +- Hard limits: Persistent (sites, users, keywords, clusters) +- Monthly limits: Reset on billing cycle (ideas, words, images) +- Track in `PlanLimitUsage` table +- Check before operation: `LimitService.check_limit()` + +### **AI Framework:** +- Use `ai/engine.py` for AI operations +- Use `ai/functions/` for specific AI tasks +- Use `ai/models.py` for tracking +- Don't use legacy `utils/ai_processor.py` + +### **Multi-Tenancy:** +- Every request has `request.user.account` +- All models scope by account directly or via site +- Use `AccountBaseModel` or `SiteSectorBaseModel` +- Validate ownership before mutations + +--- + +## 🎨 Design System + +### **Colors:** +- Primary: Blue (#0693e3) +- Success: Green (#0bbf87) +- Error: Red (#ef4444) +- Warning: Yellow (#f59e0b) +- Info: Blue (#3b82f6) + +### **Typography:** +- Headings: font-bold +- Body: font-normal +- Small text: text-sm +- Large text: text-lg, text-xl, text-2xl + +### **Spacing:** +- Padding: p-4, p-6 (standard) +- Margin: mt-4, mb-6 (standard) +- Gap: gap-4, gap-6 (standard) + +### **Components:** +- Card: `` with padding and shadow +- Button: ` + + ); + } + + const hardLimitIcons = { + sites: , + users: , + keywords: , + clusters: , + }; + + const monthlyLimitIcons = { + content_ideas: , + content_words: , + images_basic: , + images_premium: , + image_prompts: , + }; + + return ( +
+ {/* Header */} +
+
+

Plan Limits & Usage

+

+ Current Plan: {summary.plan_name} +

+
+ {summary.days_until_reset !== undefined && ( + + Resets in {summary.days_until_reset} days + + )} +
+ + {/* Hard Limits Section */} +
+

+ Account Limits +

+
+ {Object.entries(summary.hard_limits).map(([key, usage]) => ( + + ))} +
+
+ + {/* Monthly Limits Section */} +
+

+ Monthly Usage Limits +

+
+ {Object.entries(summary.monthly_limits).map(([key, usage]) => ( + + ))} +
+
+ + {/* Upgrade CTA if approaching limits */} + {(Object.values(summary.hard_limits).some(u => u.percentage_used >= 80) || + Object.values(summary.monthly_limits).some(u => u.percentage_used >= 80)) && ( + +
+
+ +
+
+

+ Approaching Your Limits +

+

+ You're using a significant portion of your plan's resources. Upgrade to get higher limits + and avoid interruptions. +

+ + View Plans & Upgrade + +
+
+
+ )} +
+ ); +} diff --git a/frontend/src/components/ui/pricing-table/index.tsx b/frontend/src/components/ui/pricing-table/index.tsx index 8c86a54e..be846d3f 100644 --- a/frontend/src/components/ui/pricing-table/index.tsx +++ b/frontend/src/components/ui/pricing-table/index.tsx @@ -18,6 +18,17 @@ export interface PricingPlan { features: string[]; buttonText: string; highlighted?: boolean; + disabled?: boolean; + // Plan limits + max_sites?: number; + max_users?: number; + max_keywords?: number; + max_clusters?: number; + max_content_ideas?: number; + max_content_words?: number; + max_images_basic?: number; + max_images_premium?: number; + included_credits?: number; } interface PricingTableProps { @@ -124,12 +135,68 @@ export function PricingTable({ variant = '1', title, plans, showToggle = false, {feature} ))} + + {/* Plan Limits Section */} + {(plan.max_sites || plan.max_content_words || plan.included_credits) && ( +
+
LIMITS
+ {plan.max_sites && ( +
  • + + + {plan.max_sites === 99999 ? 'Unlimited' : plan.max_sites} Sites + +
  • + )} + {plan.max_users && ( +
  • + + + {plan.max_users === 99999 ? 'Unlimited' : plan.max_users} Team Members + +
  • + )} + {plan.max_content_words && ( +
  • + + + {(plan.max_content_words / 1000).toLocaleString()}K Words/month + +
  • + )} + {plan.max_content_ideas && ( +
  • + + + {plan.max_content_ideas} Ideas/month + +
  • + )} + {plan.max_images_basic && ( +
  • + + + {plan.max_images_basic} Images/month + +
  • + )} + {plan.included_credits && ( +
  • + + + {plan.included_credits.toLocaleString()} Credits/month + +
  • + )} +
    + )} diff --git a/frontend/src/index.css b/frontend/src/index.css index d96cbe89..53731645 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -2,6 +2,7 @@ layer(base); @import "./styles/tokens.css"; +@import "./styles/account-colors.css"; @import "tailwindcss"; @keyframes slide-in-right { diff --git a/frontend/src/pages/account/PlansAndBillingPage.tsx b/frontend/src/pages/account/PlansAndBillingPage.tsx index 91fc3a5a..8636f278 100644 --- a/frontend/src/pages/account/PlansAndBillingPage.tsx +++ b/frontend/src/pages/account/PlansAndBillingPage.tsx @@ -502,6 +502,16 @@ export default function PlansAndBillingPage() { buttonText: plan.id === currentPlanId ? 'Current Plan' : 'Select Plan', highlighted: plan.is_featured || false, disabled: plan.id === currentPlanId || planLoadingId === plan.id, + // Plan limits + max_sites: plan.max_sites, + max_users: plan.max_users, + max_keywords: plan.max_keywords, + max_clusters: plan.max_clusters, + max_content_ideas: plan.max_content_ideas, + max_content_words: plan.max_content_words, + max_images_basic: plan.max_images_basic, + max_images_premium: plan.max_images_premium, + included_credits: plan.included_credits, }; })} showToggle={true} diff --git a/frontend/src/pages/account/UsageAnalyticsPage.tsx b/frontend/src/pages/account/UsageAnalyticsPage.tsx index 11437565..2a82d3ad 100644 --- a/frontend/src/pages/account/UsageAnalyticsPage.tsx +++ b/frontend/src/pages/account/UsageAnalyticsPage.tsx @@ -1,10 +1,10 @@ /** * Usage & Analytics Page - * Tabs: Credit Usage, API Usage, Cost Breakdown + * Tabs: Plan Limits, Credit Usage, API Usage, Cost Breakdown */ import { useState, useEffect } from 'react'; -import { TrendingUp, Activity, DollarSign } from 'lucide-react'; +import { TrendingUp, Activity, DollarSign, BarChart3 } from 'lucide-react'; import PageMeta from '../../components/common/PageMeta'; import { useToast } from '../../components/ui/toast/ToastContainer'; import { getUsageAnalytics, UsageAnalytics } from '../../services/billing.api'; @@ -12,13 +12,14 @@ import { Card } from '../../components/ui/card'; import Badge from '../../components/ui/badge/Badge'; import BillingUsagePanel from '../../components/billing/BillingUsagePanel'; import BillingBalancePanel from '../../components/billing/BillingBalancePanel'; +import UsageLimitsPanel from '../../components/billing/UsageLimitsPanel'; import Button from '../../components/ui/button/Button'; -type TabType = 'credits' | 'api' | 'costs'; +type TabType = 'limits' | 'credits' | 'balance' | 'api' | 'costs'; export default function UsageAnalyticsPage() { const toast = useToast(); - const [activeTab, setActiveTab] = useState('credits'); + const [activeTab, setActiveTab] = useState('limits'); const [analytics, setAnalytics] = useState(null); const [loading, setLoading] = useState(true); const [period, setPeriod] = useState(30); @@ -51,6 +52,7 @@ export default function UsageAnalyticsPage() { } const tabs = [ + { id: 'limits' as TabType, label: 'Plan Limits', icon: }, { id: 'credits' as TabType, label: 'Credit Usage', icon: }, { id: 'balance' as TabType, label: 'Credit Balance', icon: }, { id: 'api' as TabType, label: 'API Usage', icon: }, @@ -64,7 +66,7 @@ export default function UsageAnalyticsPage() {

    Usage & Analytics

    - Monitor credit usage, API calls, and cost breakdown + Monitor plan limits, credit usage, API calls, and cost breakdown

    @@ -112,6 +114,11 @@ export default function UsageAnalyticsPage() { {/* Tab Content */}
    + {/* Plan Limits Tab */} + {activeTab === 'limits' && ( + + )} + {/* Credit Usage Tab */} {activeTab === 'credits' && (
    diff --git a/frontend/src/services/billing.api.ts b/frontend/src/services/billing.api.ts index f7caa896..17a76d33 100644 --- a/frontend/src/services/billing.api.ts +++ b/frontend/src/services/billing.api.ts @@ -865,6 +865,48 @@ export interface Plan { features?: string[]; limits?: Record; display_order?: number; + // Hard Limits + max_sites?: number; + max_users?: number; + max_keywords?: number; + max_clusters?: number; + // Monthly Limits + max_content_ideas?: number; + max_content_words?: number; + max_images_basic?: number; + max_images_premium?: number; + max_image_prompts?: number; + included_credits?: number; +} + +export interface LimitUsage { + display_name: string; + current: number; + limit: number; + remaining: number; + percentage_used: number; +} + +export interface UsageSummary { + account_id: number; + account_name: string; + plan_name: string; + period_start: string; + period_end: string; + days_until_reset: number; + hard_limits: { + sites?: LimitUsage; + users?: LimitUsage; + keywords?: LimitUsage; + clusters?: LimitUsage; + }; + monthly_limits: { + content_ideas?: LimitUsage; + content_words?: LimitUsage; + images_basic?: LimitUsage; + images_premium?: LimitUsage; + image_prompts?: LimitUsage; + }; } export interface Subscription { @@ -942,3 +984,11 @@ export async function cancelSubscription(subscriptionId: number): Promise<{ mess method: 'POST', }); } + +// ============================================================================ +// USAGE SUMMARY (PLAN LIMITS) +// ============================================================================ + +export async function getUsageSummary(): Promise { + return fetchAPI('/v1/billing/usage-summary/'); +} diff --git a/frontend/src/styles/account-colors.css b/frontend/src/styles/account-colors.css new file mode 100644 index 00000000..59108dc0 --- /dev/null +++ b/frontend/src/styles/account-colors.css @@ -0,0 +1,435 @@ +/* =================================================================== + IGNY8 ACCOUNT SECTION - CUSTOM COLOR SCHEMES + =================================================================== + Brand-specific styling for account, billing, and usage pages + Follows IGNY8 design system with enhanced gradients and visual hierarchy + =================================================================== */ + +/* Account Page Container */ +.account-page { + background: linear-gradient(135deg, #f8fafc 0%, #e0e7ff 100%); + min-height: 100vh; +} + +.dark .account-page { + background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); +} + +/* === IGNY8 BRAND GRADIENTS === */ +.igny8-gradient-primary { + background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); +} + +.igny8-gradient-success { + background: linear-gradient(135deg, #10b981 0%, #059669 100%); +} + +.igny8-gradient-warning { + background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); +} + +.igny8-gradient-danger { + background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); +} + +.igny8-gradient-purple { + background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); +} + +.igny8-gradient-teal { + background: linear-gradient(135deg, #14b8a6 0%, #0d9488 100%); +} + +/* === CARD VARIANTS === */ +.igny8-card-premium { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.05) 0%, rgba(37, 99, 235, 0.05) 100%); + border: 1px solid rgba(59, 130, 246, 0.2); + box-shadow: 0 4px 6px -1px rgba(59, 130, 246, 0.1), 0 2px 4px -1px rgba(59, 130, 246, 0.06); +} + +.dark .igny8-card-premium { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(37, 99, 235, 0.1) 100%); + border: 1px solid rgba(59, 130, 246, 0.3); +} + +.igny8-card-success { + background: linear-gradient(135deg, rgba(16, 185, 129, 0.05) 0%, rgba(5, 150, 105, 0.05) 100%); + border: 1px solid rgba(16, 185, 129, 0.2); +} + +.dark .igny8-card-success { + background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(5, 150, 105, 0.1) 100%); + border: 1px solid rgba(16, 185, 129, 0.3); +} + +.igny8-card-warning { + background: linear-gradient(135deg, rgba(245, 158, 11, 0.05) 0%, rgba(217, 119, 6, 0.05) 100%); + border: 1px solid rgba(245, 158, 11, 0.2); +} + +.dark .igny8-card-warning { + background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(217, 119, 6, 0.1) 100%); + border: 1px solid rgba(245, 158, 11, 0.3); +} + +/* === USAGE METRICS === */ +.usage-metric-card { + position: relative; + overflow: hidden; + transition: all 0.3s ease; +} + +.usage-metric-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient(90deg, #3b82f6 0%, #2563eb 100%); +} + +.usage-metric-card:hover { + transform: translateY(-2px); + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); +} + +.usage-metric-card.warning::before { + background: linear-gradient(90deg, #f59e0b 0%, #d97706 100%); +} + +.usage-metric-card.danger::before { + background: linear-gradient(90deg, #ef4444 0%, #dc2626 100%); +} + +.usage-metric-card.success::before { + background: linear-gradient(90deg, #10b981 0%, #059669 100%); +} + +/* === PROGRESS BARS === */ +.igny8-progress-bar { + height: 8px; + background: #e5e7eb; + border-radius: 9999px; + overflow: hidden; + position: relative; +} + +.dark .igny8-progress-bar { + background: #374151; +} + +.igny8-progress-fill { + height: 100%; + transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; +} + +.igny8-progress-fill::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.3) 50%, + rgba(255, 255, 255, 0) 100% + ); + animation: shimmer 2s infinite; +} + +@keyframes shimmer { + 0% { + transform: translateX(-100%); + } + 100% { + transform: translateX(100%); + } +} + +.igny8-progress-fill.primary { + background: linear-gradient(90deg, #3b82f6 0%, #2563eb 100%); +} + +.igny8-progress-fill.warning { + background: linear-gradient(90deg, #f59e0b 0%, #d97706 100%); +} + +.igny8-progress-fill.danger { + background: linear-gradient(90deg, #ef4444 0%, #dc2626 100%); +} + +.igny8-progress-fill.success { + background: linear-gradient(90deg, #10b981 0%, #059669 100%); +} + +/* === STAT NUMBERS === */ +.igny8-stat-number { + font-variant-numeric: tabular-nums; + line-height: 1; + letter-spacing: -0.025em; +} + +.igny8-stat-number.primary { + background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.igny8-stat-number.success { + background: linear-gradient(135deg, #10b981 0%, #059669 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.igny8-stat-number.warning { + background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.igny8-stat-number.danger { + background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* === BADGES === */ +.igny8-badge { + display: inline-flex; + align-items: center; + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.025em; + text-transform: uppercase; +} + +.igny8-badge.primary { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(37, 99, 235, 0.1) 100%); + color: #2563eb; + border: 1px solid rgba(59, 130, 246, 0.2); +} + +.dark .igny8-badge.primary { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.2) 0%, rgba(37, 99, 235, 0.2) 100%); + color: #60a5fa; +} + +.igny8-badge.success { + background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(5, 150, 105, 0.1) 100%); + color: #059669; + border: 1px solid rgba(16, 185, 129, 0.2); +} + +.dark .igny8-badge.success { + background: linear-gradient(135deg, rgba(16, 185, 129, 0.2) 0%, rgba(5, 150, 105, 0.2) 100%); + color: #34d399; +} + +.igny8-badge.warning { + background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(217, 119, 6, 0.1) 100%); + color: #d97706; + border: 1px solid rgba(245, 158, 11, 0.2); +} + +.dark .igny8-badge.warning { + background: linear-gradient(135deg, rgba(245, 158, 11, 0.2) 0%, rgba(217, 119, 6, 0.2) 100%); + color: #fbbf24; +} + +.igny8-badge.danger { + background: linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(220, 38, 38, 0.1) 100%); + color: #dc2626; + border: 1px solid rgba(239, 68, 68, 0.2); +} + +.dark .igny8-badge.danger { + background: linear-gradient(135deg, rgba(239, 68, 68, 0.2) 0%, rgba(220, 38, 38, 0.2) 100%); + color: #f87171; +} + +/* === PLAN CARDS === */ +.igny8-plan-card { + position: relative; + border-radius: 1rem; + overflow: hidden; + transition: all 0.3s ease; +} + +.igny8-plan-card.featured { + border: 2px solid #3b82f6; + box-shadow: 0 20px 25px -5px rgba(59, 130, 246, 0.1), 0 10px 10px -5px rgba(59, 130, 246, 0.04); +} + +.igny8-plan-card.featured::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #3b82f6 0%, #2563eb 50%, #1d4ed8 100%); +} + +.igny8-plan-card:hover { + transform: translateY(-4px); + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); +} + +/* === LIMIT DISPLAY === */ +.igny8-limit-item { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem; + border-radius: 0.5rem; + background: rgba(59, 130, 246, 0.05); + border: 1px solid rgba(59, 130, 246, 0.1); + transition: all 0.2s ease; +} + +.dark .igny8-limit-item { + background: rgba(59, 130, 246, 0.1); + border: 1px solid rgba(59, 130, 246, 0.2); +} + +.igny8-limit-item:hover { + background: rgba(59, 130, 246, 0.1); + border-color: rgba(59, 130, 246, 0.2); + transform: translateX(2px); +} + +.dark .igny8-limit-item:hover { + background: rgba(59, 130, 246, 0.15); + border-color: rgba(59, 130, 246, 0.3); +} + +/* === BILLING HISTORY TABLE === */ +.igny8-billing-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; +} + +.igny8-billing-table thead th { + background: linear-gradient(180deg, #f9fafb 0%, #f3f4f6 100%); + color: #6b7280; + font-weight: 600; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 0.75rem 1rem; + border-bottom: 1px solid #e5e7eb; +} + +.dark .igny8-billing-table thead th { + background: linear-gradient(180deg, #1f2937 0%, #111827 100%); + color: #9ca3af; + border-bottom: 1px solid #374151; +} + +.igny8-billing-table tbody tr { + border-bottom: 1px solid #e5e7eb; + transition: background-color 0.2s ease; +} + +.dark .igny8-billing-table tbody tr { + border-bottom: 1px solid #374151; +} + +.igny8-billing-table tbody tr:hover { + background: rgba(59, 130, 246, 0.02); +} + +.dark .igny8-billing-table tbody tr:hover { + background: rgba(59, 130, 246, 0.05); +} + +.igny8-billing-table tbody td { + padding: 1rem; + color: #374151; +} + +.dark .igny8-billing-table tbody td { + color: #d1d5db; +} + +/* === UPGRADE CTA === */ +.igny8-upgrade-cta { + position: relative; + overflow: hidden; + border-radius: 1rem; + padding: 2rem; + background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); + color: white; +} + +.igny8-upgrade-cta::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + opacity: 0.1; +} + +.igny8-upgrade-cta-content { + position: relative; + z-index: 1; +} + +/* === ANIMATIONS === */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.igny8-fade-in-up { + animation: fadeInUp 0.6s ease-out; +} + +@keyframes pulse { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } +} + +.igny8-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +/* === RESPONSIVE UTILITIES === */ +@media (max-width: 640px) { + .igny8-stat-number { + font-size: 1.5rem; + } + + .usage-metric-card { + padding: 1rem; + } + + .igny8-plan-card { + margin-bottom: 1rem; + } +}