diff --git a/app/automation-v2/page.js b/app/automation-v2/page.js index 9f201a5..5865659 100644 --- a/app/automation-v2/page.js +++ b/app/automation-v2/page.js @@ -14,12 +14,12 @@ const timeframes = [ export default function AutomationPageV2() { const [config, setConfig] = useState({ - mode: 'SIMULATION', + mode: 'LIVE', dexProvider: 'DRIFT', symbol: 'SOLUSD', - selectedTimeframes: ['60'], // Multi-timeframe support + selectedTimeframes: ['5', '15', '30'], // Default to scalping preset tradingAmount: 100, - balancePercentage: 50, // Default to 50% of available balance + balancePercentage: 100, // Default to 100% of available balance }) const [status, setStatus] = useState(null) @@ -299,733 +299,888 @@ export default function AutomationPageV2() { } return ( -
-
- {/* ๐Ÿค– Automation Control Panel */} -
-
- {/* Header with Start/Stop Button */} - {/* Header with Start/Stop Button */} -
-

๐Ÿค– Automation Control

-
- {status?.isActive ? ( - <> +
+
+ {/* Page Header */} +
+

+ ๐Ÿค– AI Trading Automation +

+

Advanced AI-powered trading with real-time analysis and risk management

+
+ + {/* Main Grid Layout */} +
+ {/* Automation Control Panel - Main Column */} +
+
+ {/* Control Header with Enhanced Buttons */} +
+
+
+ ๐Ÿค– +
+
+

Automation Control

+

Configure and manage your AI trading bot

+
+
+ +
+ {status?.isActive ? ( + <> + + + + ) : ( - - - - - ) : ( + )} + + {/* Test & Analysis Buttons */} + - )} -
-
- - {/* Trading Mode - Side by Side Radio Buttons with Logos */} -
- -
- - -
-
- - {/* Symbol and Position Size */} -
-
- - -
- -
- - { - const percentage = parseFloat(e.target.value); - const newAmount = balance ? (parseFloat(balance.availableBalance) * percentage / 100) : 100; - setConfig({ - ...config, - balancePercentage: percentage, - tradingAmount: Math.round(newAmount) - }); - }} - disabled={status?.isActive} - /> -
- 10% - 50% - 100%
-
- - {/* MULTI-TIMEFRAME SELECTION */} -
- - {/* Timeframe Checkboxes */} -
- {timeframes.map(tf => ( -
-
- - {/* Status and Info Panel */} -
- {/* Status */} -
-

๐Ÿค– Bot Status

- -
-
- Status: - - {status?.isActive ? 'RUNNING' : 'STOPPED'} - -
- - {status?.isActive && ( - <> -
- Symbol: - {status.symbol} -
-
- Mode: - + setConfig({...config, mode: 'LIVE'})} + disabled={status?.isActive} + /> +
- {status.mode} - -
- -
- Timeframes: - - {status.timeframes?.map(tf => timeframes.find(t => t.value === tf)?.label || tf).join(', ')} - -
- - )} - - {/* Rate Limit Notification */} - {status?.rateLimitHit && ( -
-
- โš ๏ธ Rate Limit Reached -
- {status.rateLimitMessage && ( -

{status.rateLimitMessage}

- )} -

- Automation stopped automatically. Please recharge your OpenAI account to continue. -

-
- )} -
-
- - {/* AI Reasoning & Decision Analysis Panel - Always Visible */} -
-
-

- ๐Ÿง  AI Trading Analysis -

-
-
- - {status?.lastDecision ? 'Analysis Available' : 'Waiting for Analysis'} - -
-
- - {status?.lastDecision ? ( -
- {/* Decision Summary */} -
-
-
-
- {status.lastDecision.recommendation || 'HOLD'} +
+
+ ๐Ÿ’ฐ +
+
+

Live Trading

+

Real money transactions

+
โš ๏ธ Use real funds carefully
+
- {status.lastDecision.isRetrospective && ( -
- ๐Ÿ“Š RETROACTIVE + {config.mode === 'LIVE' && ( +
+
)} -
- Confidence: -
= 80 ? 'text-green-300' : - status.lastDecision.confidence >= 70 ? 'text-yellow-300' : - 'text-red-300' - }`}> - {status.lastDecision.confidence}% -
-
-
- {new Date(status.lastDecision.timestamp).toLocaleString()} -
-
- - {/* AI Reasoning - Prominent Display */} -
-

- ๐ŸŽฏ - Why This Decision? -

-
-

- {status.lastDecision.reasoning} -

-
-
+ +
+
- {/* Execution Status */} -
-
- - - {status.lastDecision.executed ? 'โœ… Trade Executed' : 'โŒ Not Executed'} - + {/* Symbol and Balance Configuration */} +
+
+ +
+ +
+ + +
- {!status.lastDecision.executed && status.lastDecision.executionError && ( - - {status.lastDecision.executionError} +
+
+ +
+ +
+
+ { + const percentage = parseFloat(e.target.value); + const newAmount = balance ? (parseFloat(balance.availableBalance) * percentage / 100) : 100; + setConfig({ + ...config, + balancePercentage: percentage, + tradingAmount: Math.round(newAmount) + }); + }} + disabled={status?.isActive} + /> +
+
+ + + Conservative (10%) + + + + Balanced (50%) + + + + Aggressive (100%) + +
+
- {/* Trade Details - If Executed */} - {status.lastDecision.executed && status.lastDecision.executionDetails && ( -
- {/* Entry & Exit Strategy */} -
-

- ๐Ÿ“ˆ - Entry & Exit Strategy -

-
-
- Entry Price: - ${status.lastDecision.executionDetails.currentPrice?.toFixed(4)} -
-
- Stop Loss: - ${status.lastDecision.executionDetails.stopLoss?.toFixed(4)} -
-
- Take Profit: - ${status.lastDecision.executionDetails.takeProfit?.toFixed(4)} -
-
- Position Size: - ${status.lastDecision.executionDetails.amount} -
+ {/* Enhanced Multi-Timeframe Selection */} +
+ + + {/* Timeframe Selection Grid */} +
+ {timeframes.map(tf => ( +
+ + ))} +
- {/* AI Leverage Calculation */} -
-

- โšก - AI Leverage Decision -

-
-
- Leverage: - {status.lastDecision.executionDetails.leverage}x -
-
- Position Side: - - {status.lastDecision.executionDetails.side} - -
+ {/* Selected Timeframes Display */} + {config.selectedTimeframes.length > 0 && ( +
+
+
+ Selected Timeframes: + + {config.selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label || tf).filter(Boolean).join(', ')} + +
+
+ ๐Ÿ’ก Multi-timeframe analysis
- {status.lastDecision.executionDetails.aiReasoning && ( -
-

- {status.lastDecision.executionDetails.aiReasoning} -

-
- )}
)} + + {/* Trading Style Presets - Enhanced Cards */} +
+ + + + + +
- ) : ( -
-
๐Ÿค–
-

AI Analysis Standby

-

- The AI will analyze market conditions and provide detailed reasoning for all trading decisions. -

-
-
What you'll see when analysis starts:
-
    -
  • โ€ข Entry Strategy: Why AI chose this entry point
  • -
  • โ€ข Stop Loss Logic: Risk management reasoning
  • -
  • โ€ข Take Profit Target: Profit-taking strategy
  • -
  • โ€ข Leverage Calculation: AI's risk assessment
  • -
  • โ€ข Confidence Analysis: Probability scoring
  • -
+
+
+ + {/* Enhanced Sidebar */} +
+ {/* Bot Status Card */} +
+
+
+ {status?.isActive ? '๐ŸŸข' : 'โšช'} +
+
+

Bot Status

+

Real-time monitoring

+
+
+ +
+
+ Status: + + {status?.isActive ? 'RUNNING' : 'STOPPED'} + +
+ + {status?.isActive && ( + <> +
+ Symbol: + {status.symbol} +
+ +
+ Mode: + + {status.mode} + +
+ +
+
Timeframes:
+
+ {status.timeframes?.map((tf, index) => ( + + {timeframes.find(t => t.value === tf)?.label || tf} + + ))} +
+
+ + )} + + {/* Rate Limit Warning */} + {status?.rateLimitHit && ( +
+
+ โš ๏ธ + Rate Limit Reached +
+ {status.rateLimitMessage && ( +

{status.rateLimitMessage}

+ )} +

+ Automation stopped automatically. Please recharge your OpenAI account to continue. +

+
+ )} +
+
+ + {/* Account Balance Card */} + {balance && ( +
+
+
+ ๐Ÿ’ฐ +
+
+

Account Balance

+

Live Drift Protocol data

+
+
+ +
+
+
Available Balance
+
${parseFloat(balance.availableBalance).toFixed(2)}
+
+ +
+
+
Total Collateral
+
${parseFloat(balance.totalCollateral).toFixed(2)}
+
+ +
+
Open Positions
+
{balance.positions || 0}
+
+
+
+
+ )} + + {/* Position Monitor Card */} + {monitorData && ( +
+
+
+ ๐Ÿ“Š +
+
+

Position Monitor

+

Risk assessment

+
+
+ +
+
+ Has Position: + + {monitorData.hasPosition ? 'โœ… YES' : 'โŒ NO'} + +
+ +
+ Risk Level: + + {monitorData.riskLevel} + +
+ +
+
Next Action:
+
{monitorData.nextAction}
+
+ + {monitorData.orphanedOrderCleanup && ( +
+
+ {monitorData.orphanedOrderCleanup.success ? 'โœ… Cleanup Success' : 'โŒ Cleanup Failed'} +
+
+ {monitorData.orphanedOrderCleanup.message} +
+
+ )} +
+
+ )} + + {/* Open Positions Card */} + {positions.length > 0 && ( +
+
+
+ ๐Ÿ“ˆ +
+
+

Open Positions

+

{positions.length} active trade{positions.length !== 1 ? 's' : ''}

+
+
+ +
+ {positions.map((position, index) => ( +
+
+ {position.symbol} + + {position.side} + +
+ +
+
+ Size: + ${position.size} +
+ + {position.entryPrice && ( +
+ Entry: + ${position.entryPrice} +
+ )} + + {position.markPrice && ( +
+ Mark: + ${position.markPrice} +
+ )} + + {position.pnl !== undefined && ( +
+ PnL: + = 0 ? 'text-green-400' : 'text-red-400' + }`}> + ${position.pnl >= 0 ? '+' : ''}${position.pnl} + +
+ )} +
+
+ ))}
)}
+
- {/* Legacy Last Decision Panel - Hidden when new panel is active */} - {status?.lastDecision && false && ( -
-

๐Ÿง  Last Decision

- -
- {/* Decision Header */} -
-
- - - {status.lastDecision.executed ? 'โœ… EXECUTED' : 'โŒ NOT EXECUTED'} - + {/* Enhanced AI Trading Analysis Panel */} +
+
+
+
+ ๐Ÿง  +
+
+

+ AI Trading Analysis +

+

Real-time market intelligence and decision reasoning

+
+
+
+
+ + {status?.lastDecision ? '๐ŸŸข Analysis Active' : 'โšช Waiting for Analysis'} + +
+
+ + {status?.lastDecision ? ( +
+ {/* Quick Summary Cards */} +
+
+
Recommendation
+
+ {status.lastDecision.recommendation || 'HOLD'}
- + {status.lastDecision.isRetrospective && ( +
+ ๐Ÿ“Š Retrospective Analysis +
+ )} +
+ +
+
Confidence
+
= 80 ? 'text-green-300' : + status.lastDecision.confidence >= 70 ? 'text-yellow-300' : + 'text-red-300' + }`}> + {status.lastDecision.confidence}% +
+
+ Min Required: {status.lastDecision.minConfidenceRequired}% +
+
+ +
+
Execution
+
+ {status.lastDecision.executed ? 'โœ… EXECUTED' : 'โŒ NOT EXECUTED'} +
+
{new Date(status.lastDecision.timestamp).toLocaleTimeString()} - -
- - {/* Analysis Details */} -
-
- Recommendation: - - {status.lastDecision.recommendation || 'HOLD'} - -
- -
- Confidence: -
- = 80 ? 'text-green-400' : - status.lastDecision.confidence >= 70 ? 'text-yellow-400' : - 'text-red-400' - }`}> - {status.lastDecision.confidence}% - - - (min: {status.lastDecision.minConfidenceRequired}%) - -
-
- -
- Reasoning: - {status.lastDecision.reasoning}
- {/* Execution Details (if executed) */} {status.lastDecision.executed && status.lastDecision.executionDetails && ( -
-

๐Ÿ’ฐ Execution Details

- -
-
- Side: - - {status.lastDecision.executionDetails.side} - -
- -
- Amount: - ${status.lastDecision.executionDetails.amount} -
- -
- Entry: - ${status.lastDecision.executionDetails.currentPrice?.toFixed(2)} -
- -
- Leverage: - {status.lastDecision.executionDetails.leverage}x +
+
Leverage
+
+ {status.lastDecision.executionDetails.leverage}x +
+
AI Calculated
+
+ )} +
+ + {/* Main Analysis Content */} +
+ {/* AI Reasoning */} +
+
+

+ ๐ŸŽฏ + Market Analysis & Reasoning +

+
+
+ {status.lastDecision.reasoning}
+
- {/* SL/TP Details */} - {(status.lastDecision.executionDetails.stopLoss || status.lastDecision.executionDetails.takeProfit) && ( -
-
๐Ÿ›ก๏ธ Risk Management
-
- {status.lastDecision.executionDetails.stopLoss && ( -
- Stop Loss: - - ${status.lastDecision.executionDetails.stopLoss.toFixed(2)} - {status.lastDecision.executionDetails.aiStopLossPercent && ( - ({status.lastDecision.executionDetails.aiStopLossPercent}) - )} + {/* Execution Error */} + {!status.lastDecision.executed && status.lastDecision.executionError && ( +
+

+ โŒ + Execution Failed +

+ {status.lastDecision.executionError} +
+ )} +
+ + {/* Trade Execution Details */} + {status.lastDecision.executed && status.lastDecision.executionDetails && ( +
+
+

+ ๐Ÿ“ˆ + Trade Execution Details +

+
+
+
+
Entry Price
+
+ ${status.lastDecision.executionDetails.currentPrice?.toFixed(4)} +
+
+
+
Position Size
+
+ ${status.lastDecision.executionDetails.amount} +
+
+
+
Direction
+
+ {status.lastDecision.executionDetails.side} +
+
+
+
Leverage
+
+ {status.lastDecision.executionDetails.leverage}x +
+
+
+ + {/* Risk Management */} +
+
+ ๐Ÿ›ก๏ธ + Risk Management +
+
+
+ Stop Loss: + + ${status.lastDecision.executionDetails.stopLoss?.toFixed(4)}
- )} - - {status.lastDecision.executionDetails.takeProfit && ( -
- Take Profit: - - ${status.lastDecision.executionDetails.takeProfit.toFixed(2)} +
+ Take Profit: + + ${status.lastDecision.executionDetails.takeProfit?.toFixed(4)}
+
+ {status.lastDecision.executionDetails.txId && ( +
+
+ Transaction ID: + + {status.lastDecision.executionDetails.txId.substring(0, 12)}... + +
+
)}
- - {status.lastDecision.executionDetails.stopLoss && status.lastDecision.executionDetails.takeProfit && ( -
- Risk/Reward: 1:2 ratio -
- )}
- )} +
{/* AI Leverage Reasoning */} - {status.lastDecision.executionDetails.aiReasoning && ( -
-
๐Ÿง  AI Leverage Decision
-
+
+

+ โšก + AI Leverage Analysis +

+
+
{status.lastDecision.executionDetails.aiReasoning}
- )} - - {/* Transaction ID */} - {status.lastDecision.executionDetails.txId && ( -
- TX ID: - - {status.lastDecision.executionDetails.txId.substring(0, 20)}... - +
+
+ )} +
+
+ ) : ( +
+
๐Ÿค–
+

AI Analysis Standby

+

+ The AI will analyze market conditions using advanced technical indicators across multiple timeframes + and provide detailed reasoning for all trading decisions. +

+
+
What you'll see when analysis starts:
+
+
+
+ ๐ŸŽฏ +
+
Entry Strategy
+
Why AI chose this entry point
- )} -
- )} - - {/* Execution Error (if failed) */} - {!status.lastDecision.executed && status.lastDecision.executionError && ( -
-

โŒ Execution Failed

- {status.lastDecision.executionError} -
- )} -
-
- )} - - {/* Position Monitor */} - {monitorData && ( -
-

๐Ÿ“Š Position Monitor

- -
-
- Has Position: - - {monitorData.hasPosition ? 'โœ… YES' : 'โŒ NO'} - -
- -
- Risk Level: - - {monitorData.riskLevel} - -
- -
- Next Action: {monitorData.nextAction} -
- - {monitorData.orphanedOrderCleanup && ( -
-
- {monitorData.orphanedOrderCleanup.success ? 'โœ… Cleanup Success' : 'โŒ Cleanup Failed'}
-
- {monitorData.orphanedOrderCleanup.message} -
-
- )} -
-
- )} - - {/* Balance */} - {balance && ( -
-

๏ฟฝ Account Balance

- -
-
- Available: - ${parseFloat(balance.availableBalance).toFixed(2)} -
- -
- Total: - ${parseFloat(balance.totalCollateral).toFixed(2)} -
- -
- Positions: - {balance.positions || 0} -
-
-
- )} - - {/* Positions */} - {positions.length > 0 && ( -
-

๐Ÿ“ˆ Open Positions

- -
- {positions.map((position, index) => ( -
-
- {position.symbol} - - {position.side} - -
- -
-
- Size: - ${position.size} +
+ ๐Ÿ›ก๏ธ +
+
Risk Management
+
Stop loss and take profit logic
+
+
+
+ ๐Ÿ“Š +
+
Technical Analysis
+
Multi-timeframe indicator consensus
- - {position.entryPrice && ( -
- Entry: - ${position.entryPrice} -
- )} - - {position.markPrice && ( -
- Mark: - ${position.markPrice} -
- )} - - {position.pnl !== undefined && ( -
- PnL: - = 0 ? 'text-green-400' : 'text-red-400' - }`}> - ${position.pnl >= 0 ? '+' : ''}${position.pnl} - -
- )}
- ))} +
+
+ โšก +
+
Leverage Calculation
+
AI's dynamic risk assessment
+
+
+
+ ๐ŸŽฒ +
+
Confidence Scoring
+
Probability-based decision making
+
+
+
+ โœ… +
+
Execution Status
+
Real-time trade confirmation
+
+
+
+
)} diff --git a/app/globals.css b/app/globals.css index 6450dbb..a2a7d6c 100644 --- a/app/globals.css +++ b/app/globals.css @@ -53,43 +53,193 @@ body { box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); } -/* Custom range slider styling */ -input[type="range"] { +/* Enhanced range slider styling */ +.slider { -webkit-appearance: none; appearance: none; - height: 8px; - background: linear-gradient(to right, #3B82F6 0%, #3B82F6 30%, #374151 30%, #374151 100%); - border-radius: 4px; + height: 12px; + border-radius: 6px; outline: none; cursor: pointer; + transition: all 0.3s ease; } -input[type="range"]::-webkit-slider-thumb { +.slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; - width: 20px; - height: 20px; - background: #3B82F6; + width: 24px; + height: 24px; + background: linear-gradient(135deg, #3B82F6, #1D4ED8); border-radius: 50%; cursor: pointer; - border: 2px solid #1e40af; - box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3); + border: 3px solid #ffffff; + box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4), 0 0 0 0 rgba(59, 130, 246, 0.7); + transition: all 0.3s ease; } -input[type="range"]::-moz-range-thumb { - width: 20px; - height: 20px; - background: #3B82F6; +.slider::-moz-range-thumb { + width: 24px; + height: 24px; + background: linear-gradient(135deg, #3B82F6, #1D4ED8); border-radius: 50%; cursor: pointer; - border: 2px solid #1e40af; - box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3); + border: 3px solid #ffffff; + box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4); } -input[type="range"]:focus::-webkit-slider-thumb { - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3); +.slider:hover::-webkit-slider-thumb { + transform: scale(1.1); + box-shadow: 0 6px 16px rgba(59, 130, 246, 0.6), 0 0 0 8px rgba(59, 130, 246, 0.1); } -input[type="range"]:focus::-moz-range-thumb { - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3); +.slider:focus::-webkit-slider-thumb { + box-shadow: 0 6px 16px rgba(59, 130, 246, 0.6), 0 0 0 12px rgba(59, 130, 246, 0.2); } + +.slider:active::-webkit-slider-thumb { + transform: scale(0.95); +} + +/* Glass morphism effects */ +.glass { + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.glass-dark { + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + background: rgba(0, 0, 0, 0.2); + border: 1px solid rgba(255, 255, 255, 0.1); +} + +/* Enhanced card hover effects */ +.enhanced-card { + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + transform-style: preserve-3d; +} + +.enhanced-card:hover { + transform: translateY(-4px) rotateX(5deg); + box-shadow: + 0 20px 40px rgba(0, 0, 0, 0.4), + 0 0 50px rgba(139, 92, 246, 0.2), + inset 0 1px 0 rgba(255, 255, 255, 0.1); +} + +/* Gradient text animations */ +.gradient-text { + background: linear-gradient(270deg, #3B82F6, #8B5CF6, #EC4899, #3B82F6); + background-size: 400% 400%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: gradientShift 6s ease infinite; +} + +@keyframes gradientShift { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +/* Pulse effects for status indicators */ +.pulse-green { + animation: pulseGreen 2s infinite; +} + +.pulse-red { + animation: pulseRed 2s infinite; +} + +@keyframes pulseGreen { + 0%, 100% { + box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7); + } + 70% { + box-shadow: 0 0 0 10px rgba(34, 197, 94, 0); + } +} + +@keyframes pulseRed { + 0%, 100% { + box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); + } + 70% { + box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); + } +} + +/* Floating animation for standby elements */ +.float { + animation: floating 3s ease-in-out infinite; +} + +@keyframes floating { + 0%, 100% { transform: translateY(0px); } + 50% { transform: translateY(-10px); } +} + +/* Enhanced button effects */ +.btn-enhanced { + position: relative; + overflow: hidden; + transition: all 0.3s ease; +} + +.btn-enhanced::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + transition: left 0.5s ease; +} + +.btn-enhanced:hover::before { + left: 100%; +} + +/* Neon glow effects */ +.neon-blue { + box-shadow: + 0 0 10px rgba(59, 130, 246, 0.5), + 0 0 20px rgba(59, 130, 246, 0.3), + 0 0 30px rgba(59, 130, 246, 0.1); +} + +.neon-purple { + box-shadow: + 0 0 10px rgba(139, 92, 246, 0.5), + 0 0 20px rgba(139, 92, 246, 0.3), + 0 0 30px rgba(139, 92, 246, 0.1); +} + +.neon-green { + box-shadow: + 0 0 10px rgba(34, 197, 94, 0.5), + 0 0 20px rgba(34, 197, 94, 0.3), + 0 0 30px rgba(34, 197, 94, 0.1); +} + +/* Typing animation for loading states */ +.typing { + overflow: hidden; + border-right: 2px solid #3B82F6; + white-space: nowrap; + animation: typing 2s steps(40, end), blink 1s infinite; +} + +@keyframes typing { + from { width: 0; } + to { width: 100%; } +} + +@keyframes blink { + 0%, 50% { border-color: transparent; } + 51%, 100% { border-color: #3B82F6; } +} \ No newline at end of file