diff --git a/index.css b/index.css index 4c86657..cb35a9a 100644 --- a/index.css +++ b/index.css @@ -83,6 +83,8 @@ height: 100%; padding: var(--spacing-page); overflow: hidden; + background-color: #0d1416; + position: relative; } @media (min-width: 768px) { @@ -1392,140 +1394,383 @@ Completion Page Components ===================================================== */ -/* Completion Page Container */ -.completion-container { - display: flex; - flex-direction: column; - height: 100%; - padding: var(--spacing-page); - overflow: hidden; +/* Completion Page Title */ +.completion-title { + font-size: 1.75rem; + font-weight: 600; + color: #E5F1F2; + line-height: 1.19; + letter-spacing: -0.006em; + margin: 0 0 1.5rem 0; + text-align: center; + padding: 0 1rem; } @media (min-width: 768px) { - .completion-container { - padding: var(--spacing-page-md); + .completion-title { + font-size: 2.5rem; + margin-bottom: 2rem; } } -/* Completion Content Layout */ -.completion-content { - flex: 1; +/* Completion Container - wrapper for the two cards */ +.completion-container { + margin: 0 auto 1.5rem auto; + max-width: 1400px; + width: calc(100% - 4rem); display: flex; flex-direction: column; - gap: 1.5rem; - max-width: 72rem; - margin-left: auto; - margin-right: auto; - width: 100%; + gap: 1rem; + flex: 1; min-height: 0; - overflow: hidden; } @media (min-width: 1024px) { - .completion-content { + .completion-container { flex-direction: row; + gap: 2rem; + max-width: 1400px; } } -/* Video Preview Card */ -.video-preview-card { - flex: 2; - background-color: var(--color-bg-card); - border-radius: var(--radius-3xl); - padding: var(--spacing-page); - border: 1px solid var(--color-border-white-5); - box-shadow: var(--shadow-xl); +@media (min-width: 1280px) { + .completion-container { + max-width: 1600px; + } +} + +@media (max-width: 480px) { + .completion-container { + width: calc(100% - 2rem); + } +} + +@media (min-width: 481px) and (max-width: 767px) { + .completion-container { + width: calc(100% - 3rem); + } +} + +/* Completion Columns */ +.completion-column { display: flex; flex-direction: column; - overflow: hidden; } -@media (min-width: 768px) { - .video-preview-card { - padding: var(--spacing-page-md); - } -} - -/* Sharing Card */ -.sharing-card { +.completion-column-left { flex: 1; - background-color: var(--color-bg-card); - border-radius: var(--radius-3xl); - padding: var(--spacing-page); - border: 1px solid var(--color-border-white-5); - box-shadow: var(--shadow-xl); - display: flex; - flex-direction: column; - overflow: hidden; - min-width: 280px; + min-width: 0; } -@media (min-width: 768px) { - .sharing-card { - padding: var(--spacing-page-md); +.completion-column-right { + width: 100%; + flex-shrink: 0; +} + +@media (min-width: 1024px) { + .completion-column-right { + width: 400px; } } -/* Social List */ -.social-list { +@media (min-width: 1280px) { + .completion-column-right { + width: 450px; + } +} + +/* Video Preview Card & Sharing Card - uses asset-column base + card styling */ +.video-preview-card, +.sharing-card { + background-color: #01393B; + border-radius: 40px; + padding: 1.5rem; + border: none; + box-shadow: none; + overflow: hidden; + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +@media (min-width: 768px) { + .video-preview-card, + .sharing-card { + padding: 2rem; + } +} + +@media (min-width: 1024px) { + .video-preview-card, + .sharing-card { + padding: 2.5rem; + } +} + +/* Completion Video Wrapper */ +.completion-video-wrapper { flex: 1; min-height: 0; - overflow-y: auto; + display: flex; + flex-direction: column; } -.social-list > * + * { - margin-top: 1rem; -} - -/* Deploy Button */ -.btn-deploy { - margin-top: 1.5rem; +/* Video Container for Completion - always 16:9 aspect ratio (horizontal letterbox) */ +.video-preview-card .video-container { + position: relative; + aspect-ratio: 16 / 9; width: 100%; + max-width: 100%; + margin: 0 auto; + background-color: #000; +} + +@media (min-width: 768px) { + .video-preview-card .video-container { + max-width: 100%; + } +} + +@media (min-width: 1024px) { + .video-preview-card .video-container { + max-width: 100%; + } +} + +/* Video Overlay */ +.video-overlay { + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 100%); + padding: 1.5rem; + pointer-events: none; +} + +.video-overlay-content { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.video-overlay-title { + font-size: 1.25rem; + font-weight: 800; + color: #FFFFFF; + line-height: 1.3; + letter-spacing: -0.006em; + text-align: center; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); + margin: 0; +} + +.video-overlay-logo { + font-size: 0.375rem; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + line-height: 1.3; + letter-spacing: -0.006em; + text-align: center; + align-self: flex-end; +} + +/* AI Optimization Section */ +.ai-optimization-section { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 0.75rem; padding: 1rem; - font-weight: 700; - border-radius: var(--radius-full); - transition: all var(--transition-normal); - transform: scale(1); - font-size: var(--text-base); - box-shadow: var(--shadow-purple); + background-color: #002224; + border-radius: 8px; +} + +@media (min-width: 768px) { + .ai-optimization-section { + flex-direction: row; + gap: 1.25rem; + } +} + +.ai-optimization-title { + font-size: 0.875rem; + font-weight: 600; + color: #94FBE0; + line-height: 1.19; + letter-spacing: -0.006em; + margin: 0; flex-shrink: 0; - background-color: var(--color-purple); - color: var(--color-text-white); - border: none; - cursor: pointer; } -.btn-deploy:hover { - background-color: var(--color-purple-hover); +.ai-optimization-tags { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.75rem; } -.btn-deploy:active { - transform: scale(0.95); +@media (min-width: 768px) { + .ai-optimization-tags { + gap: 1.25rem; + justify-content: flex-start; + } } -.btn-deploy:disabled { - opacity: 1; - cursor: pointer; +.ai-tag { + display: flex; + align-items: center; + gap: 0.5rem; + height: 20px; } -/* Download Button */ -.btn-download { - margin-top: 0.75rem; - width: 100%; +.ai-tag-dot { + width: 6px; + height: 6px; + background-color: #94FBE0; + border-radius: 50%; + flex-shrink: 0; +} + +.ai-tag-text { + font-size: 0.875rem; + font-weight: 400; + color: #E5F1F2; + line-height: 1.19; + letter-spacing: -0.006em; +} + +/* Sharing Card Layout */ +.sharing-card { + justify-content: space-between; +} + +.sharing-content { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.sharing-actions { + display: flex; + flex-direction: column; + gap: 0.75rem; + flex-shrink: 0; +} + +/* Social List New */ +.social-list-new { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.completion-social-card { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; padding: 1rem; + background-color: #002224; + border-radius: 8px; + cursor: pointer; + transition: all var(--transition-normal); +} + +.completion-social-card:hover:not(.disabled) { + background-color: #01393B; +} + +.completion-social-card.disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.completion-social-info { + display: flex; + align-items: center; + gap: 1rem; +} + +.completion-social-logo { + width: 32px; + height: 32px; + object-fit: cover; + object-position: center; + flex-shrink: 0; + border-radius: 4px; background-color: transparent; - border: 1px solid var(--color-border-gray-600); - color: var(--color-text-white); - font-weight: 700; - border-radius: var(--radius-full); - transition: all var(--transition-normal); - font-size: var(--text-base); - flex-shrink: 0; - cursor: pointer; } -.btn-download:hover { - border-color: var(--color-text-gray-500); +.completion-social-name { + font-size: 0.875rem; + font-weight: 400; + color: #FFFFFF; + line-height: 1.19; + letter-spacing: -0.006em; +} + +.completion-social-email { + font-size: 0.75rem; + font-weight: 400; + color: #94FBE0; + line-height: 1.19; + letter-spacing: -0.006em; +} + +/* Completion Buttons */ +.btn-completion-deploy { + width: 100%; + padding: 0.625rem 2.5rem; + background-color: #AE72F9; + border: none; + border-radius: var(--radius-full); + color: #FFFFFF; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: all var(--transition-normal); + line-height: 1.19; + letter-spacing: -0.006em; + height: 48px; +} + +.btn-completion-deploy:hover:not(:disabled) { + background-color: #9B5FE0; +} + +.btn-completion-deploy:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-completion-download { + width: 100%; + padding: 0.625rem 2.5rem; + background-color: transparent; + border: 1px solid #AE72F9; + border-radius: var(--radius-full); + color: #FFFFFF; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: all var(--transition-normal); + line-height: 1.19; + letter-spacing: -0.006em; + height: 48px; +} + +.btn-completion-download:hover:not(:disabled) { + border-color: #9B5FE0; + background-color: rgba(174, 114, 249, 0.1); +} + +.btn-completion-download:disabled { + opacity: 0.5; + cursor: not-allowed; } /* ===================================================== @@ -1986,205 +2231,220 @@ height: 100%; display: flex; flex-direction: column; - background-color: var(--color-bg-dark); + background-color: #002224; color: var(--color-text-white); overflow: hidden; } .welcome-header { - margin-bottom: 1.5rem; + margin-bottom: 2rem; text-align: center; } @media (min-width: 768px) { .welcome-header { - margin-bottom: 2.5rem; + margin-bottom: 3rem; } } .welcome-icon { display: inline-block; - margin-bottom: 0.5rem; - color: var(--color-purple); + margin-bottom: 1rem; + width: 40px; + height: 40px; +} + +.welcome-icon img { + width: 100%; + height: 100%; + object-fit: contain; } .welcome-title { - font-size: var(--text-xl); - font-weight: 700; - margin-bottom: 0.5rem; + font-size: 1.5rem; + font-weight: 600; + margin-bottom: 1rem; + color: #E5F1F2; + letter-spacing: -0.006em; + line-height: 1.19; } @media (min-width: 640px) { .welcome-title { - font-size: 1.5rem; + font-size: 2rem; } } @media (min-width: 768px) { .welcome-title { - font-size: var(--text-3xl); - } -} - -@media (min-width: 1024px) { - .welcome-title { - font-size: var(--text-4xl); + font-size: 2.5rem; } } .welcome-subtitle { - color: var(--color-text-gray-400); - font-size: var(--text-xs); + color: #9BCACC; + font-size: var(--text-sm); + font-weight: 600; + letter-spacing: -0.006em; + line-height: 1.19; } @media (min-width: 640px) { - .welcome-subtitle { - font-size: var(--text-sm); - } -} - -@media (min-width: 768px) { .welcome-subtitle { font-size: var(--text-base); } } +@media (min-width: 768px) { + .welcome-subtitle { + font-size: var(--text-lg); + } +} + /* Feature Cards Grid */ .feature-grid { - display: grid; - grid-template-columns: 1fr; - gap: 0.75rem; + display: flex; + flex-direction: column; + gap: 1rem; width: 100%; - max-width: 64rem; - margin-bottom: 2rem; + max-width: 72rem; + margin: 0 auto 2rem; overflow-y: auto; padding-right: 0.25rem; } @media (min-width: 640px) { .feature-grid { - gap: 1rem; + gap: 1.5rem; } } @media (min-width: 768px) { .feature-grid { - grid-template-columns: repeat(3, 1fr); - gap: 1.5rem; + flex-direction: row; + gap: 2rem; overflow: visible; + justify-content: center; } } /* Feature Card */ .feature-item { position: relative; - background-color: var(--color-bg-card); - border-radius: var(--radius-2xl); - padding: 1rem; + background-color: #01393B; + border-radius: 2.5rem; + padding: 1.5rem 2rem; display: flex; - flex-direction: row; + flex-direction: column; align-items: center; - text-align: left; + text-align: center; transition: all var(--transition-normal); border: 1px solid transparent; -} - -@media (min-width: 640px) { - .feature-item { - padding: 1.5rem; - } + gap: 1.5rem; + width: 100%; } @media (min-width: 768px) { .feature-item { - flex-direction: column; - align-items: center; - text-align: center; - padding: 2rem; - border-radius: var(--radius-3xl); + width: 360px; + min-height: 440px; + padding: 1.5rem 2.5rem; } } .feature-number { - width: 1.5rem; - height: 1.5rem; + width: 2.5rem; + height: 2.5rem; flex-shrink: 0; border-radius: var(--radius-full); - border: 1px solid var(--color-border-gray-600); + border: 1px solid #9BCACC; display: flex; align-items: center; justify-content: center; - margin-right: 1rem; - font-size: 10px; - font-weight: 700; - color: var(--color-text-gray-400); -} - -@media (min-width: 640px) { - .feature-number { - width: 2rem; - height: 2rem; - font-size: var(--text-xs); - } -} - -@media (min-width: 768px) { - .feature-number { - margin-right: 0; - margin-bottom: 1.5rem; - } -} - -.feature-content { - display: flex; - flex-direction: column; -} - -@media (min-width: 768px) { - .feature-content { - align-items: center; - } + font-size: 1.25rem; + font-weight: 600; + color: #9BCACC; + letter-spacing: -0.006em; + line-height: 1.19; } .feature-title { - font-size: var(--text-sm); - font-weight: 700; - margin-bottom: 0.25rem; + font-size: var(--text-base); + font-weight: 600; + color: #E5F1F2; + letter-spacing: -0.006em; + line-height: 1.19; } -@media (min-width: 640px) { +@media (min-width: 768px) { .feature-title { + font-size: 1.5rem; + } +} + +.feature-icon { + width: 160px; + height: 160px; + border-radius: 2.5rem; + display: flex; + align-items: center; + justify-content: center; + padding: 0.5rem; +} + +.feature-icon img { + width: 100%; + height: 100%; + object-fit: contain; +} + +.feature-description { + color: #E5F1F2; + font-size: var(--text-sm); + line-height: 1.45; + letter-spacing: -0.006em; + white-space: pre-line; +} + +@media (min-width: 768px) { + .feature-description { font-size: var(--text-base); } } -@media (min-width: 768px) { - .feature-title { - font-size: var(--text-lg); - margin-bottom: 1rem; - } -} - -.feature-description { - color: var(--color-text-gray-400); - font-size: 10px; - line-height: 1.25; -} - -@media (min-width: 640px) { - .feature-description { - font-size: var(--text-xs); - } +/* Welcome Button */ +.btn-start { + padding: 0.625rem 3rem; + border-radius: var(--radius-full); + background-color: #AE72F9; + color: var(--color-text-white); + font-weight: 600; + font-size: var(--text-sm); + transition: all var(--transition-normal); + border: none; + cursor: pointer; + margin: 0 auto; + letter-spacing: -0.006em; + line-height: 1.19; } @media (min-width: 768px) { - .feature-description { - font-size: var(--text-sm); - line-height: 1.625; + .btn-start { + padding: 0.75rem 3.5rem; } } -/* Welcome Buttons */ +.btn-start:hover { + background-color: #9570f0; + transform: translateY(-2px); + box-shadow: 0 10px 20px rgba(174, 114, 249, 0.3); +} + +.btn-start:active { + transform: scale(0.95); +} + +/* Legacy buttons - keep for other sections */ .welcome-buttons { display: flex; gap: 1rem; @@ -2257,13 +2517,92 @@ height: 100%; display: flex; flex-direction: column; - background-color: var(--color-bg-dark); + background-color: #002224; color: var(--color-text-white); position: relative; overflow: hidden; } -/* Video Frames Container */ +/* Display Frames Container */ +.display-frames { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 1rem; + margin-bottom: 2rem; + width: 100%; + max-width: 72rem; +} + +@media (min-width: 640px) { + .display-frames { + gap: 1.5rem; + } +} + +@media (min-width: 768px) { + .display-frames { + gap: 2rem; + margin-bottom: 3rem; + } +} + +@media (min-width: 1024px) { + .display-frames { + gap: 2rem; + } +} + +/* Display Frame */ +.display-frame { + display: flex; + width: 120px; + height: 213px; + border-radius: 2.5rem; + overflow: hidden; + position: relative; + transition: all var(--transition-normal); +} + +@media (min-width: 640px) { + .display-frame { + width: 160px; + height: 284px; + } +} + +@media (min-width: 768px) { + .display-frame { + width: 200px; + height: 356px; + } +} + +@media (min-width: 1024px) { + .display-frame { + width: 270px; + height: 480px; + } +} + +.display-frame img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.display-frame-hidden-mobile { + display: none; +} + +@media (min-width: 768px) { + .display-frame-hidden-mobile { + display: flex; + } +} + +/* Legacy Video Frames - Keep for backward compatibility */ .video-frames { display: flex; flex-direction: row; @@ -2403,29 +2742,30 @@ /* Display Button */ .display-button { - padding: 0.75rem 3.5rem; + padding: 0.625rem 3rem; border-radius: var(--radius-full); - background-color: var(--color-purple); + background-color: #AE72F9; color: var(--color-text-white); - font-weight: 700; - font-size: var(--text-xs); - letter-spacing: 0.025em; - box-shadow: 0 20px 25px -5px rgba(166, 130, 255, 0.27); + font-weight: 600; + font-size: var(--text-sm); + letter-spacing: -0.006em; + line-height: 1.19; transition: all var(--transition-normal); border: none; cursor: pointer; margin-bottom: 2rem; } -@media (min-width: 640px) { +@media (min-width: 768px) { .display-button { - padding: 0.875rem 4rem; - font-size: var(--text-base); + padding: 0.75rem 3.5rem; } } .display-button:hover { - background-color: var(--color-purple-hover); + background-color: #9570f0; + transform: translateY(-2px); + box-shadow: 0 10px 20px rgba(174, 114, 249, 0.3); } .display-button:active { @@ -2440,7 +2780,7 @@ .login-container { width: 100%; height: 100dvh; - background-color: var(--color-bg-dark); + background-color: #002224; display: flex; flex-direction: column; align-items: center; @@ -2460,38 +2800,36 @@ /* Login Back Button */ .login-back-btn { position: absolute; - top: 0.75rem; - left: 0.75rem; + top: 0.875rem; + left: 1.5rem; display: flex; align-items: center; - gap: 0.375rem; - padding: 0.25rem 0.75rem; + gap: 0.25rem; + padding: 0.5rem 1.25rem 0.5rem 0.5rem; border-radius: var(--radius-full); - border: 1px solid var(--color-border-gray-700); + border: 1px solid #694596; background-color: transparent; - color: var(--color-text-gray-300); - font-size: 9px; - transition: background-color var(--transition-normal); + color: #CFABFB; + font-size: var(--text-sm); + font-weight: 600; + letter-spacing: -0.006em; + line-height: 1.19; + transition: all var(--transition-normal); cursor: pointer; } -@media (min-width: 640px) { - .login-back-btn { - top: 1rem; - left: 1rem; - padding: 0.375rem 1rem; - font-size: 10px; - } -} - -@media (min-width: 768px) { - .login-back-btn { - font-size: var(--text-xs); - } +.login-back-btn img { + width: 20px; + height: 20px; } .login-back-btn:hover { - background-color: rgba(31, 41, 55, 1); + background-color: rgba(105, 69, 150, 0.1); + border-color: #8662C7; +} + +.login-back-btn:active { + transform: scale(0.95); } /* Login Content */ @@ -2500,9 +2838,69 @@ flex-direction: column; align-items: center; text-align: center; - max-width: 42rem; + gap: 4rem; + max-width: 600px; } +@media (min-width: 768px) { + .login-content { + gap: 5rem; + } +} + +/* Login Logo */ +.login-logo { + width: 350px; + height: auto; +} + +@media (min-width: 768px) { + .login-logo { + width: 554px; + } +} + +.login-logo img { + width: 100%; + height: auto; + object-fit: contain; +} + +/* Kakao Button */ +.btn-kakao { + width: 100%; + max-width: 250px; + padding: 0.75rem 1rem; + border-radius: var(--radius-full); + background-color: #FEE500; + color: rgba(0, 0, 0, 0.85); + font-weight: 600; + font-size: var(--text-base); + letter-spacing: 0; + line-height: 1.19; + transition: all var(--transition-normal); + border: none; + cursor: pointer; +} + +@media (min-width: 768px) { + .btn-kakao { + max-width: 296px; + padding: 0.9375rem 1rem; + } +} + +.btn-kakao:hover { + background-color: #FDDC00; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(254, 229, 0, 0.3); +} + +.btn-kakao:active { + transform: scale(0.95); +} + +/* Legacy styles - keep for backward compatibility */ .login-title { font-family: 'Playfair Display', serif; font-style: italic; @@ -2560,38 +2958,6 @@ } } -/* Kakao Button */ -.btn-kakao { - width: 100%; - max-width: 200px; - padding: 0.625rem 1rem; - border-radius: var(--radius-full); - background-color: #fae100; - color: var(--color-bg-dark); - font-weight: 700; - font-size: 10px; - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.2); - transition: all var(--transition-normal); - border: none; - cursor: pointer; -} - -@media (min-width: 640px) { - .btn-kakao { - max-width: 240px; - padding: 0.75rem 1rem; - font-size: var(--text-xs); - } -} - -.btn-kakao:hover { - background-color: #ebd500; -} - -.btn-kakao:active { - transform: scale(0.95); -} - /* ===================================================== Loading Section Components ===================================================== */ @@ -2600,7 +2966,7 @@ .loading-container { width: 100%; height: 100vh; - background-color: var(--color-bg-dark); + background-color: #002224; display: flex; flex-direction: column; align-items: center; @@ -2609,7 +2975,85 @@ padding: 0 1.5rem; } -/* Loading Spinner */ +.loading-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 3rem; + max-width: 600px; +} + +@media (min-width: 768px) { + .loading-content { + gap: 4rem; + } +} + +/* Loading Logo */ +.loading-logo { + width: 200px; + height: auto; +} + +@media (min-width: 768px) { + .loading-logo { + width: 308px; + } +} + +.loading-logo img { + width: 100%; + height: auto; + object-fit: contain; +} + +/* Loading Section */ +.loading-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + width: 100%; +} + +.loading-title { + font-size: 1.5rem; + font-weight: 700; + letter-spacing: -0.006em; + line-height: 1.19; + color: #FFFFFF; + text-align: center; +} + +@media (min-width: 768px) { + .loading-title { + font-size: 2rem; + } +} + +/* Loading Spinner Wrapper */ +.loading-spinner-wrapper { + width: 120px; + height: 120px; + display: flex; + align-items: center; + justify-content: center; +} + +@media (min-width: 768px) { + .loading-spinner-wrapper { + width: 200px; + height: 200px; + } +} + +.loading-spinner-icon { + width: 100%; + height: 100%; + animation: spin 2s linear infinite; +} + +/* Legacy Loading Spinner - Keep for backward compatibility */ .loading-spinner { position: relative; margin-bottom: 2rem; @@ -2641,7 +3085,6 @@ box-shadow: 0 0 15px var(--color-purple); } -/* Loading Text */ .loading-text { text-align: center; } @@ -2650,18 +3093,6 @@ margin-top: 0.5rem; } -.loading-title { - font-size: var(--text-xl); - font-weight: 700; - letter-spacing: -0.025em; -} - -@media (min-width: 640px) { - .loading-title { - font-size: 1.5rem; - } -} - .loading-description { color: var(--color-text-gray-400); font-size: var(--text-sm); @@ -3311,18 +3742,39 @@ } .header-logo { - font-family: 'Playfair Display', serif; - font-style: italic; - font-size: var(--text-xl); - font-weight: 700; - letter-spacing: -0.025em; - color: var(--color-text-white); + display: flex; + align-items: center; + height: 1rem; } -@media (min-width: 640px) { - .header-logo { - font-size: 1.5rem; - } +.header-logo img { + height: 100%; + width: auto; + object-fit: contain; +} + +.header-login-btn { + padding: 0.625rem 1.5rem; + border-radius: var(--radius-full); + background-color: #94FBE0; + color: #000000; + font-weight: 600; + font-size: var(--text-sm); + border: none; + cursor: pointer; + transition: all var(--transition-normal); + letter-spacing: -0.006em; + line-height: 1.19; +} + +.header-login-btn:hover { + background-color: #7fe8cc; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(148, 251, 224, 0.3); +} + +.header-login-btn:active { + transform: scale(0.95); } .header-avatar { @@ -3358,3 +3810,1192 @@ text-align: center; padding: 0 1rem; } + + +/* ===================================================== + Sound Studio Styles + ===================================================== */ + +/* Sound Studio Header */ +.sound-studio-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 2rem; + gap: 1rem; +} + +.btn-back-new { + display: flex; + align-items: center; + gap: 0.25rem; + padding: 0.5rem 1.25rem 0.5rem 0.5rem; + background-color: #462E64; + border: 1px solid #694596; + border-radius: var(--radius-full); + color: #CFABFB; + font-size: var(--text-sm); + font-weight: 600; + cursor: pointer; + transition: all var(--transition-normal); +} + +.btn-back-new:hover { + background-color: #694596; + border-color: #8B5BC7; +} + +/* Progress Indicator */ +.progress-indicator { + display: flex; + flex-direction: column; + gap: 0.25rem; + width: 280px; +} + +.progress-label { + display: flex; + align-items: center; + gap: 1rem; +} + +.progress-text { + font-size: 0.8125rem; + font-weight: 600; + color: var(--color-text-white); +} + +.progress-numbers { + display: flex; + align-items: center; + gap: 0.25rem; +} + +.progress-current { + font-size: 0.8125rem; + font-weight: 600; + color: var(--color-text-white); +} + +.progress-divider, +.progress-total { + font-size: 0.8125rem; + font-weight: 400; + color: #379599; +} + +.progress-bar-wrapper { + position: relative; + width: 100%; + height: 8px; + background-color: #01393B; + border-radius: var(--radius-full); + overflow: hidden; +} + +.progress-bar-track { + width: 100%; + height: 100%; + background: linear-gradient(90deg, #94FBE0 0%, #AE72F9 100%); + border-radius: var(--radius-full); +} + +/* Sound Studio Title */ +.sound-studio-title { + font-size: 2rem; + font-weight: 700; + color: #E5F1F2; + margin: 1rem auto 1.5rem auto; + padding: 0 2rem; + max-width: 1200px; + width: 100%; + line-height: 1.19; + letter-spacing: -0.006em; +} + +/* Sound Studio Container */ +.sound-studio-container { + background-color: #01393B; + border-radius: 40px; + padding: 2rem; + margin: 0 auto 1.5rem auto; + max-width: 1136px; + width: calc(100% - 4rem); + display: flex; + flex-direction: column; + gap: 1.25rem; + flex: 1; + min-height: 0; +} + +/* Sound Studio Columns */ +.sound-studio-columns { + display: flex; + gap: 2.5rem; + flex: 1; + min-height: 0; + overflow: hidden; +} + +/* Sound Column */ +.sound-column { + flex: 1; + display: flex; + flex-direction: column; + gap: 1.25rem; + min-height: 0; + overflow-y: auto; +} + +/* Lyrics Column */ +.lyrics-column { + flex: 1; + display: flex; + flex-direction: column; + gap: 1.25rem; + min-height: 0; +} + +/* Column Title */ +.column-title { + font-size: 1.125rem; + font-weight: 600; + color: var(--color-mint); + line-height: 1.19; + letter-spacing: -0.006em; +} + +/* Sound Studio Section */ +.sound-studio-section { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +/* Input Label */ +.input-label { + font-size: var(--text-sm); + font-weight: 600; + color: #CEE5E6; + line-height: 1.19; + letter-spacing: -0.006em; +} + +/* Sound Type Grid */ +.sound-type-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.75rem; +} + +/* Sound Type Button */ +.sound-type-btn { + padding: 1rem; + background-color: #002224; + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 8px; + color: var(--color-text-white); + font-size: var(--text-sm); + font-weight: 600; + cursor: pointer; + transition: all var(--transition-normal); + text-align: center; + line-height: 1.19; + letter-spacing: -0.006em; +} + +.sound-type-btn:hover:not(:disabled) { + background-color: rgba(0, 34, 36, 0.8); + border-color: rgba(255, 255, 255, 0.1); +} + +.sound-type-btn.active { + border-color: var(--color-mint); +} + +.sound-type-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Genre Grid */ +.genre-grid { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.genre-row { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.75rem; +} + +/* Genre Button */ +.genre-btn { + padding: 0.5rem 1rem; + background-color: #002224; + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 8px; + color: var(--color-text-white); + font-size: var(--text-sm); + font-weight: 600; + cursor: pointer; + transition: all var(--transition-normal); + text-align: center; + line-height: 1.19; + letter-spacing: -0.006em; +} + +.genre-btn:hover:not(:disabled) { + background-color: rgba(0, 34, 36, 0.8); + border-color: rgba(255, 255, 255, 0.1); +} + +.genre-btn.active { + border-color: var(--color-mint); +} + +.genre-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.genre-btn-placeholder { + /* Empty space for grid alignment */ +} + +/* Language Selector */ +.language-selector-wrapper { + position: relative; +} + +.language-selector { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 1rem; + background-color: #002224; + border: 1px solid transparent; + border-radius: 8px; + cursor: pointer; + transition: border-color 0.2s, background-color 0.2s; +} + +.language-selector:hover:not(:disabled) { + background-color: #003A3C; +} + +.language-selector:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.language-display { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.language-flag { + font-size: 1.25rem; + line-height: 1.19; + letter-spacing: -0.006em; +} + +.language-name { + font-size: var(--text-sm); + font-weight: 600; + color: var(--color-text-white); + line-height: 1.19; + letter-spacing: -0.006em; +} + +.language-dropdown-icon { + width: 1rem; + height: 1rem; + opacity: 0.7; + transition: transform 0.2s; +} + +.language-dropdown-icon.open { + transform: rotate(180deg); +} + +/* Language Dropdown Menu */ +.language-dropdown-menu { + position: absolute; + top: calc(100% + 0.5rem); + left: 0; + right: 0; + max-height: 240px; + background-color: #002224; + border: 1px solid #046266; + border-radius: 8px; + overflow-y: auto; + z-index: 100; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + overscroll-behavior: contain; +} + +.language-dropdown-menu::-webkit-scrollbar { + width: 6px; +} + +.language-dropdown-menu::-webkit-scrollbar-track { + background: transparent; +} + +.language-dropdown-menu::-webkit-scrollbar-thumb { + background: #046266; + border-radius: 3px; +} + +.language-dropdown-menu::-webkit-scrollbar-thumb:hover { + background: #379599; +} + +.language-dropdown-item { + width: 100%; + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1rem; + background-color: #002224; + border: none; + cursor: pointer; + transition: background-color 0.2s; +} + +.language-dropdown-item:hover { + background-color: #003A3C; +} + +.language-dropdown-item.active { + background-color: #046266; +} + +.language-dropdown-item .language-flag { + font-size: 1.25rem; +} + +.language-dropdown-item .language-name { + font-size: var(--text-sm); + font-weight: 600; + color: var(--color-text-white); +} + +/* Lyrics Header */ +.lyrics-header { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.lyrics-subtitle { + font-size: var(--text-sm); + font-weight: 600; + color: #CEE5E6; + line-height: 1.19; + letter-spacing: -0.006em; +} + +/* Audio Player */ +.audio-player { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + background-color: #002224; + border-radius: 8px; +} + +.play-btn-new { + width: 1.5rem; + height: 1.5rem; + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + color: var(--color-text-white); + cursor: pointer; + transition: all var(--transition-normal); + flex-shrink: 0; +} + +.play-btn-new:hover:not(.disabled) { + color: var(--color-mint); +} + +.play-btn-new.disabled { + opacity: 0.3; + cursor: not-allowed; +} + +.audio-progress-container { + flex: 1; + height: 4px; + background-color: #046266; + border-radius: var(--radius-full); + position: relative; + cursor: pointer; +} + +.audio-progress-container.disabled { + cursor: not-allowed; +} + +.audio-progress-fill { + height: 100%; + background-color: var(--color-mint); + border-radius: var(--radius-full); + transition: width 0.1s ease-out; +} + +.audio-time { + font-size: var(--text-sm); + font-weight: 400; + color: #046266; + min-width: 40px; + text-align: right; + line-height: 1.19; + letter-spacing: -0.006em; +} + +/* Lyrics Display */ +.lyrics-display { + flex: 1; + display: flex; + background-color: #002224; + border-radius: 8px; + padding: 1rem; + min-height: 200px; + overflow: hidden; +} + +.lyrics-textarea { + width: 100%; + background: transparent; + border: none; + color: var(--color-text-white); + font-size: var(--text-sm); + font-family: inherit; + resize: none; + outline: none; + line-height: 1.6; +} + +.lyrics-placeholder { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: var(--text-sm); + font-weight: 400; + color: #6AB0B3; + text-align: center; + line-height: 1.19; + letter-spacing: -0.006em; +} + +/* Generate Sound Button */ +.btn-generate-sound { + width: 100%; + padding: 0.625rem 2.5rem; + background-color: #01393B; + border: 1px solid var(--color-mint); + border-radius: var(--radius-full); + color: var(--color-mint); + font-size: var(--text-sm); + font-weight: 600; + cursor: pointer; + transition: all var(--transition-normal); + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + line-height: 1.19; + letter-spacing: -0.006em; +} + +.btn-generate-sound:hover:not(.disabled) { + background-color: var(--color-mint); + color: #000000; +} + +.btn-generate-sound.disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Video Generate Button */ +.btn-video-generate { + padding: 0.625rem 2.5rem; + background-color: #462E64; + border: none; + border-radius: var(--radius-full); + color: #8B5BC7; + font-size: var(--text-sm); + font-weight: 600; + cursor: pointer; + transition: all var(--transition-normal); + line-height: 1.19; + letter-spacing: -0.006em; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + min-width: fit-content; +} + +.btn-video-generate:hover:not(.disabled):not(.generating) { + background-color: #694596; +} + +.btn-video-generate.disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-video-generate.generating { + background-color: #AE72F9; + color: #FFFFFF; + padding: 0.625rem 1.5rem; + cursor: default; +} + +.video-gen-text { + font-size: var(--text-sm); + font-weight: 600; + color: #FFFFFF; + line-height: 1.19; + letter-spacing: -0.006em; +} + +.video-gen-progress-bar { + width: 280px; + height: 8px; + background-color: #694596; + border-radius: var(--radius-full); + overflow: hidden; + position: relative; +} + +.video-gen-progress-fill { + height: 100%; + background-color: #FFFFFF; + border-radius: var(--radius-full); + transition: width 0.5s ease-out; +} + +/* Error and Status Messages */ +.error-message-new { + padding: 0.75rem 1rem; + background-color: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + border-radius: var(--radius-lg); + color: #fca5a5; + font-size: var(--text-sm); + text-align: center; +} + +.status-message-new { + padding: 0.75rem 1rem; + background-color: rgba(148, 251, 224, 0.1); + border: 1px solid rgba(148, 251, 224, 0.3); + border-radius: var(--radius-lg); + color: var(--color-mint); + font-size: var(--text-sm); + text-align: center; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +/* Responsive Styles for Sound Studio */ +@media (max-width: 639px) { + .sound-studio-header { + padding: 0.5rem 1rem; + flex-direction: column; + align-items: stretch; + } + + .progress-indicator { + width: 100%; + } + + .sound-studio-title { + font-size: 1.5rem; + padding: 0 1rem; + margin: 0.75rem auto 1rem auto; + } + + .sound-studio-container { + padding: 1.25rem; + width: calc(100% - 2rem); + } + + .sound-studio-columns { + flex-direction: column; + gap: 1.5rem; + } + + .sound-column { + overflow-y: visible; + } + + .sound-type-grid { + gap: 0.5rem; + } + + .sound-type-btn { + padding: 0.75rem 0.5rem; + font-size: var(--text-xs); + } + + .genre-row { + gap: 0.5rem; + } + + .genre-btn { + padding: 0.5rem 0.75rem; + font-size: var(--text-xs); + } + + .lyrics-display { + min-height: 150px; + } +} + +@media (min-width: 640px) and (max-width: 767px) { + .sound-studio-header { + padding: 0.5rem 1.5rem; + } + + .sound-studio-title { + font-size: 1.75rem; + padding: 0 1.5rem; + margin: 0.875rem auto 1.25rem auto; + } + + .sound-studio-container { + padding: 1.5rem; + width: calc(100% - 3rem); + } + + .sound-studio-columns { + flex-direction: column; + gap: 2rem; + } + + .sound-column { + overflow-y: visible; + } +} + +@media (min-width: 768px) and (max-width: 1023px) { + .sound-studio-columns { + flex-direction: column; + gap: 2rem; + } + + .sound-column { + overflow-y: visible; + } +} + +@media (min-width: 1024px) { + .sound-studio-container { + padding: 2.5rem; + } + + .sound-studio-columns { + gap: 3rem; + } + + .column-title { + font-size: 1.25rem; + } + + .lyrics-display { + min-height: 250px; + } +} + +/* Height-based responsive adjustments */ +@media (max-height: 800px) { + .sound-studio-container { + gap: 1rem; + } + + .sound-studio-columns { + gap: 2rem; + } +} + +@media (max-height: 700px) { + .sound-studio-title { + font-size: 1.75rem; + margin: 0.75rem 2rem 1rem 2rem; + } + + .sound-studio-container { + padding: 1.5rem; + gap: 1rem; + } + + .sound-studio-columns { + gap: 1.5rem; + } + + .sound-studio-section { + gap: 0.5rem; + } + + .lyrics-display { + min-height: 180px; + } +} + +@media (max-height: 600px) { + .sound-studio-header { + padding: 0.375rem 2rem; + } + + .sound-studio-title { + font-size: 1.5rem; + margin: 0.5rem 2rem 0.75rem 2rem; + } + + .sound-studio-container { + padding: 1.25rem; + gap: 0.875rem; + } + + .sound-studio-columns { + gap: 1.25rem; + } + + .sound-type-btn, + .genre-btn { + padding: 0.625rem 0.75rem; + } + + .lyrics-display { + min-height: 150px; + } +} + +/* ==================================== + Asset Management (Brand Asset) Styles + ==================================== */ + +/* Asset Header */ +.asset-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 2rem; + margin-bottom: 1rem; +} + +/* Asset Title */ +.asset-title { + font-size: 2rem; + font-weight: 700; + color: #E5F1F2; + margin: 1rem auto 1.5rem auto; + padding: 0 2rem; + max-width: 1200px; + width: 100%; + line-height: 1.19; + letter-spacing: -0.006em; +} + +/* Asset Container */ +.asset-container { + background-color: #01393B; + border-radius: 40px; + padding: 2rem; + margin: 0 auto 1.5rem auto; + max-width: 1136px; + width: calc(100% - 4rem); + display: flex; + flex-direction: column; + gap: 1rem; + flex: 1; + min-height: 0; +} + +@media (min-width: 1024px) { + .asset-container { + flex-direction: row; + } +} + +/* Asset Columns */ +.asset-column { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.asset-column-left { + flex: 1; + min-width: 0; +} + +.asset-column-right { + width: 100%; + flex-shrink: 0; +} + +@media (min-width: 1024px) { + .asset-column-right { + width: 280px; + } +} + +/* Asset Section Title */ +.asset-section-title { + font-size: 1.125rem; + font-weight: 600; + color: #94FBE0; + line-height: 1.19; + letter-spacing: -0.006em; + margin: 0; +} + +/* Asset Image List */ +.asset-image-list { + flex: 1; + min-height: 0; + overflow-y: auto; + background-color: #002224; + border-radius: 8px; + padding: 0.5rem; +} + +.asset-image-list::-webkit-scrollbar { + width: 6px; +} + +.asset-image-list::-webkit-scrollbar-track { + background: transparent; +} + +.asset-image-list::-webkit-scrollbar-thumb { + background: #379599; + border-radius: 3px; +} + +.asset-image-list::-webkit-scrollbar-thumb:hover { + background: #4AABAF; +} + +/* Asset Image Grid */ +.asset-image-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.5rem; +} + +.asset-image-item { + position: relative; + aspect-ratio: 1; + border-radius: 8px; + overflow: hidden; + background-color: #000; +} + +.asset-image-item img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.asset-image-badge { + position: absolute; + top: 8px; + left: 8px; + background-color: #EFE3FE; + color: #AE72F9; + font-size: 0.75rem; + font-weight: 600; + padding: 4px 8px; + border-radius: 4px; + line-height: 1.19; + letter-spacing: -0.006em; +} + +.asset-image-remove { + position: absolute; + top: 8px; + right: 8px; + width: 30px; + height: 30px; + background-color: rgba(0, 0, 0, 0.6); + border: none; + border-radius: 999px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: #FFFFFF; + transition: background-color 0.2s; +} + +.asset-image-remove:hover { + background-color: rgba(0, 0, 0, 0.8); +} + +/* Asset Upload Section */ +.asset-upload-section { + display: flex; + flex-direction: column; + gap: 1.25rem; + flex: 1; +} + +.asset-upload-zone { + flex: 1; + border: 1px dashed #379599; + border-radius: 8px; + background-color: #002224; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: border-color 0.2s, background-color 0.2s; + min-height: 150px; +} + +.asset-upload-zone:hover { + border-color: #4AABAF; + background-color: #003A3C; +} + +.asset-upload-text { + font-size: 0.875rem; + font-weight: 600; + color: #9BCACC; + text-align: center; + line-height: 1.19; + letter-spacing: -0.006em; + margin: 0; +} + +/* Asset Ratio Section */ +.asset-ratio-section { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.asset-ratio-buttons { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.asset-ratio-button { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem; + background-color: #002224; + border: 1px solid transparent; + border-radius: 8px; + cursor: pointer; + transition: border-color 0.2s, background-color 0.2s; + color: #FFFFFF; + font-size: 0.875rem; + font-weight: 600; + line-height: 1.19; + letter-spacing: -0.006em; +} + +.asset-ratio-button:hover { + background-color: #003A3C; +} + +.asset-ratio-button.active { + border-color: #94FBE0; + background-color: #002224; +} + +.asset-ratio-icon { + width: 32px; + height: 32px; + background-color: #002224; + border-radius: 2px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.asset-ratio-icon-vertical .asset-ratio-box { + width: 18px; + height: 32px; + background-color: #046266; + border-radius: 4px; +} + +.asset-ratio-icon-horizontal .asset-ratio-box { + width: 32px; + height: 18px; + background-color: #046266; + border-radius: 4px; +} + +/* Asset Bottom Button */ +.asset-bottom { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + padding: 0 2rem 1.5rem 2rem; +} + +.asset-next-button { + background-color: #AE72F9; + color: #FFFFFF; + font-size: 0.875rem; + font-weight: 600; + padding: 10px 40px; + border: none; + border-radius: 999px; + cursor: pointer; + transition: background-color 0.2s; + line-height: 1.19; + letter-spacing: -0.006em; +} + +.asset-next-button:hover:not(:disabled) { + background-color: #9D5FE8; +} + +.asset-next-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Responsive Styles */ +@media (max-width: 639px) { + .asset-header { + padding: 0.5rem 1rem; + } + + .asset-title { + font-size: 1.5rem; + padding: 0 1rem; + margin: 0.75rem auto 1rem auto; + } + + .asset-container { + flex-direction: column; + padding: 1.25rem; + width: calc(100% - 2rem); + gap: 1.25rem; + } + + .asset-column-right { + width: 100%; + } + + .asset-image-grid { + grid-template-columns: repeat(2, 1fr); + } + + .asset-bottom { + padding: 0 1rem 1rem 1rem; + } +} + +@media (min-width: 640px) and (max-width: 767px) { + .asset-header { + padding: 0.5rem 1.5rem; + } + + .asset-title { + font-size: 1.75rem; + padding: 0 1.5rem; + margin: 0.875rem auto 1.25rem auto; + } + + .asset-container { + flex-direction: column; + padding: 1.5rem; + width: calc(100% - 3rem); + } + + .asset-column-right { + width: 100%; + } + + .asset-bottom { + padding: 0 1.5rem 1.25rem 1.5rem; + } +} + +@media (min-width: 768px) and (max-width: 1023px) { + .asset-container { + padding: 1.75rem; + } +} + +@media (min-width: 1024px) { + .asset-container { + padding: 2.5rem; + } +} + +/* Height-based responsive adjustments */ +@media (max-height: 800px) { + .asset-container { + padding: 1.75rem; + } + + .asset-upload-zone { + min-height: 120px; + } +} + +@media (max-height: 700px) { + .asset-container { + padding: 1.5rem; + } + + .asset-title { + font-size: 1.75rem; + margin: 0.75rem auto 1rem auto; + } + + .asset-upload-zone { + min-height: 100px; + } +} + +@media (max-height: 600px) { + .asset-container { + padding: 1.25rem; + gap: 0.875rem; + } + + .asset-column { + gap: 1rem; + } + + .asset-upload-section, + .asset-ratio-section { + gap: 1rem; + } + + .asset-upload-zone { + min-height: 80px; + } +} diff --git a/public/assets/images/display-frame-1.png b/public/assets/images/display-frame-1.png new file mode 100644 index 0000000..f866bc3 Binary files /dev/null and b/public/assets/images/display-frame-1.png differ diff --git a/public/assets/images/icon-analysis.svg b/public/assets/images/icon-analysis.svg new file mode 100644 index 0000000..f480bf8 --- /dev/null +++ b/public/assets/images/icon-analysis.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/images/icon-back.svg b/public/assets/images/icon-back.svg new file mode 100644 index 0000000..adde346 --- /dev/null +++ b/public/assets/images/icon-back.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/images/icon-content.svg b/public/assets/images/icon-content.svg new file mode 100644 index 0000000..d6f07de --- /dev/null +++ b/public/assets/images/icon-content.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/assets/images/icon-deploy.svg b/public/assets/images/icon-deploy.svg new file mode 100644 index 0000000..ad8bd36 --- /dev/null +++ b/public/assets/images/icon-deploy.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/assets/images/icon-dropdown.svg b/public/assets/images/icon-dropdown.svg new file mode 100644 index 0000000..dac0e57 --- /dev/null +++ b/public/assets/images/icon-dropdown.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/images/loading-logo.svg b/public/assets/images/loading-logo.svg new file mode 100644 index 0000000..8585165 --- /dev/null +++ b/public/assets/images/loading-logo.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/images/loading-spinner.svg b/public/assets/images/loading-spinner.svg new file mode 100644 index 0000000..cfee2a5 --- /dev/null +++ b/public/assets/images/loading-spinner.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/assets/images/login-logo.svg b/public/assets/images/login-logo.svg new file mode 100644 index 0000000..0b27701 --- /dev/null +++ b/public/assets/images/login-logo.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/images/logo.svg b/public/assets/images/logo.svg new file mode 100644 index 0000000..6fd75bc --- /dev/null +++ b/public/assets/images/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/assets/images/social-facebook.png b/public/assets/images/social-facebook.png new file mode 100644 index 0000000..2550667 Binary files /dev/null and b/public/assets/images/social-facebook.png differ diff --git a/public/assets/images/social-facebook.svg b/public/assets/images/social-facebook.svg new file mode 100644 index 0000000..2550667 Binary files /dev/null and b/public/assets/images/social-facebook.svg differ diff --git a/public/assets/images/social-instagram.png b/public/assets/images/social-instagram.png new file mode 100644 index 0000000..2aa7a89 Binary files /dev/null and b/public/assets/images/social-instagram.png differ diff --git a/public/assets/images/social-instagram.svg b/public/assets/images/social-instagram.svg new file mode 100644 index 0000000..2aa7a89 Binary files /dev/null and b/public/assets/images/social-instagram.svg differ diff --git a/public/assets/images/social-youtube.png b/public/assets/images/social-youtube.png new file mode 100644 index 0000000..3286de1 Binary files /dev/null and b/public/assets/images/social-youtube.png differ diff --git a/public/assets/images/social-youtube.svg b/public/assets/images/social-youtube.svg new file mode 100644 index 0000000..3286de1 Binary files /dev/null and b/public/assets/images/social-youtube.svg differ diff --git a/public/assets/images/star-icon.svg b/public/assets/images/star-icon.svg new file mode 100644 index 0000000..a40bfa9 --- /dev/null +++ b/public/assets/images/star-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Header.tsx b/src/components/Header.tsx index aad3b9f..6f8729f 100755 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -4,13 +4,12 @@ import React from 'react'; const Header: React.FC = () => { return (
-
CASTAD
-
- User Profile +
+ CASTAD
+
); }; diff --git a/src/pages/Analysis/LoadingSection.tsx b/src/pages/Analysis/LoadingSection.tsx index 016d9e2..f5c5cbf 100755 --- a/src/pages/Analysis/LoadingSection.tsx +++ b/src/pages/Analysis/LoadingSection.tsx @@ -4,20 +4,24 @@ import React from 'react'; const LoadingSection: React.FC = () => { return (
-
- {/* Spinning Outer Ring */} -
- {/* Pulsing center icon */} -
-
+
+ {/* Logo */} +
+ CASTAD
-
-
-

비즈니스 분석 중

-

- AI가 입력하신 정보를 바탕으로 마케팅 핵심 가치를 추출하고 있습니다... -

+ {/* Loading Spinner and Text */} +
+

브랜드 분석 중

+ +
+ Loading +
+
); diff --git a/src/pages/Dashboard/AssetManagementContent.tsx b/src/pages/Dashboard/AssetManagementContent.tsx index 8eb14c3..186ed68 100755 --- a/src/pages/Dashboard/AssetManagementContent.tsx +++ b/src/pages/Dashboard/AssetManagementContent.tsx @@ -1,5 +1,5 @@ -import React, { useRef, useState } from 'react'; +import React, { useRef, useState, useEffect } from 'react'; import { ImageItem, ImageUrlItem } from '../../types/api'; import { uploadImages } from '../../utils/api'; @@ -11,6 +11,8 @@ interface AssetManagementContentProps { onAddImages: (files: File[]) => void; } +type VideoRatio = 'vertical' | 'horizontal'; + const AssetManagementContent: React.FC = ({ onBack, onNext, @@ -22,6 +24,21 @@ const AssetManagementContent: React.FC = ({ const imageListRef = useRef(null); const [isUploading, setIsUploading] = useState(false); const [uploadError, setUploadError] = useState(null); + const [videoRatio, setVideoRatio] = useState('vertical'); + + // Load video ratio from localStorage on mount + useEffect(() => { + const savedRatio = localStorage.getItem('castad_video_ratio') as VideoRatio; + if (savedRatio === 'vertical' || savedRatio === 'horizontal') { + setVideoRatio(savedRatio); + } + }, []); + + // Save video ratio to localStorage when it changes + const handleVideoRatioChange = (ratio: VideoRatio) => { + setVideoRatio(ratio); + localStorage.setItem('castad_video_ratio', ratio); + }; const getImageSrc = (item: ImageItem): string => { return item.type === 'url' ? item.url : item.preview; @@ -105,54 +122,60 @@ const AssetManagementContent: React.FC = ({ return (
- {/* Back Button */} -
- -
- {/* Header */} -
-

브랜드 에셋

-

- 쉽고 빠르게, 브랜드 소셜 미디어 캠페인을 만드세요. -

-
- - {/* Main Content */} -
- {/* Selected Images Card */} -
-
-

선택된 이미지

- {imageList.length}장 +
+
+ 단계 +
+ 1 + / + 2 +
+
+
+
+
+
+ + {/* Title */} +

브랜드 에셋

+ + {/* Main Content Container */} +
+ {/* Left Column - Selected Images */} +
+

선택된 이미지

{imageList.length > 0 ? ( -
+
{imageList.map((item, i) => ( -
+
{`이미지 {item.type === 'file' && ( -
NEW
+
업로드
)}
))}
- ) : ( -
- 이미지가 없습니다 -
- )} + ) : null}
- {/* Upload Card */} -
-

이미지 업로드

-
-
- - - - - + {/* Right Column - Upload and Video Ratio */} +
+ {/* Image Upload Section */} +
+

이미지 업로드

+
+

+ 이미지를 드래그하여
업로드 +

+
+ +
+ + {/* Video Ratio Section */} +
+

영상 비율

+
+ +
-

- 이미지를 드래그하여
업로드 -

-

- 또는 클릭하여 파일 선택 -

-
{/* Bottom Button */} -
+
{uploadError && (

{uploadError}

)} diff --git a/src/pages/Dashboard/CompletionContent.tsx b/src/pages/Dashboard/CompletionContent.tsx index aa93294..43870fb 100755 --- a/src/pages/Dashboard/CompletionContent.tsx +++ b/src/pages/Dashboard/CompletionContent.tsx @@ -5,6 +5,8 @@ import { generateVideo, waitForVideoComplete } from '../../utils/api'; interface CompletionContentProps { onBack: () => void; songTaskId: string | null; + onVideoStatusChange?: (status: 'idle' | 'generating' | 'complete' | 'error') => void; + onVideoProgressChange?: (progress: number) => void; } type VideoStatus = 'idle' | 'generating' | 'polling' | 'complete' | 'error'; @@ -20,17 +22,38 @@ interface SavedVideoState { timestamp: number; } -const CompletionContent: React.FC = ({ onBack, songTaskId }) => { +const CompletionContent: React.FC = ({ + onBack, + songTaskId, + onVideoStatusChange, + onVideoProgressChange +}) => { const [selectedSocials, setSelectedSocials] = useState([]); const [videoStatus, setVideoStatus] = useState('idle'); const [videoUrl, setVideoUrl] = useState(null); const [errorMessage, setErrorMessage] = useState(null); const [statusMessage, setStatusMessage] = useState(''); + const [renderProgress, setRenderProgress] = useState(0); // 영상 렌더링 진행률 (0-100) const [isPlaying, setIsPlaying] = useState(false); const [progress, setProgress] = useState(0); const videoRef = useRef(null); const hasStartedGeneration = useRef(false); + // Notify parent of video status changes + useEffect(() => { + if (onVideoStatusChange) { + const mappedStatus = videoStatus === 'polling' ? 'generating' : videoStatus; + onVideoStatusChange(mappedStatus); + } + }, [videoStatus, onVideoStatusChange]); + + // Notify parent of progress changes + useEffect(() => { + if (onVideoProgressChange) { + onVideoProgressChange(renderProgress); + } + }, [renderProgress, onVideoProgressChange]); + const saveToStorage = (videoTaskId: string, currentSongTaskId: string, status: VideoStatus, url: string | null) => { const data: SavedVideoState = { videoTaskId, @@ -74,7 +97,11 @@ const CompletionContent: React.FC = ({ onBack, songTaskI setErrorMessage(null); try { - const videoResponse = await generateVideo(songTaskId); + // Get video ratio from localStorage (default to 'vertical' if not set) + const savedRatio = localStorage.getItem('castad_video_ratio'); + const orientation = (savedRatio === 'horizontal' || savedRatio === 'vertical') ? savedRatio : 'vertical'; + + const videoResponse = await generateVideo(songTaskId, orientation); if (!videoResponse.success) { throw new Error(videoResponse.error_message || '영상 생성 요청에 실패했습니다.'); @@ -96,7 +123,7 @@ const CompletionContent: React.FC = ({ onBack, songTaskI } }; - // 상태별 한글 메시지 매핑 + // 상태별 한글 메시지 및 진행률 매핑 const getStatusMessage = (status: string): string => { switch (status) { case 'planned': @@ -114,6 +141,24 @@ const CompletionContent: React.FC = ({ onBack, songTaskI } }; + // 상태별 진행률 계산 + const getProgressForStatus = (status: string): number => { + switch (status) { + case 'planned': + return 20; + case 'waiting': + return 40; + case 'transcribing': + return 60; + case 'rendering': + return 80; + case 'succeeded': + return 100; + default: + return 0; + } + }; + const pollVideoStatus = async (videoTaskId: string, currentSongTaskId: string) => { try { // 영상 생성 상태 폴링 (3분 타임아웃, 3초 간격) @@ -121,6 +166,7 @@ const CompletionContent: React.FC = ({ onBack, songTaskI videoTaskId, (status: string) => { setStatusMessage(getStatusMessage(status)); + setRenderProgress(getProgressForStatus(status)); } ); @@ -227,160 +273,181 @@ const CompletionContent: React.FC = ({ onBack, songTaskI }; const socials = [ - { id: 'Youtube', email: 'o2ocorp@o2o.kr', color: '#ff0000', icon: }, - { id: 'Instagram', email: 'o2ocorp@o2o.kr', color: '#e4405f', icon: }, + { id: 'Youtube', email: 'o2ocorp@o2o.kr', logo: '/assets/images/social-youtube.png' }, + { id: 'Instagram', email: 'o2ocorp@o2o.kr', logo: '/assets/images/social-instagram.png' }, + { id: 'Facebook', email: 'o2ocorp@o2o.kr', logo: '/assets/images/social-facebook.png' }, ]; const isLoading = videoStatus === 'generating' || videoStatus === 'polling'; return ( -
- {/* Back Button */} -
- + +
+
+ 단계 +
+ 2 + / + 2 +
+
+
+
+
+
- {/* Header */} -
-

- {isLoading ? '영상 생성 중' : videoStatus === 'error' ? '영상 생성 실패' : '콘텐츠 제작 완료'} -

-

- {isLoading - ? statusMessage || '잠시만 기다려주세요...' - : videoStatus === 'error' - ? errorMessage || '오류가 발생했습니다.' - : '인스타그램 릴스 및 틱톡에 최적화된 고성능 영상을 제작했습니다.'} -

-
+ {/* Title */} +

+ {isLoading ? '영상 생성 중' : videoStatus === 'error' ? '영상 생성 실패' : '콘텐츠 제작 완료'} +

- {/* Main Content */} -
+ {/* Main Content Container */} +
{/* Left: Video Preview */} -
-

이미지 및 영상

+
+

이미지 및 영상

-
- {isLoading ? ( - /* Loading State */ -
-
-
-
-
+
+
+ {isLoading ? ( + /* Loading State */ +
+
+
+
+
+
+

{statusMessage}

-

{statusMessage}

-
- ) : videoStatus === 'error' ? ( - /* Error State */ -
- - - - -

{errorMessage}

- -
- ) : videoUrl ? ( - /* Video Player */ -
- {/* Tags - only show when complete */} + {/* AI Optimization Tags - only show when complete */} {videoStatus === 'complete' && ( -
- {['AI 최적화', '색상 보정', '다이나믹 자막', '비트 싱크', 'SEO 메타 태그'].map(tag => ( - - {tag} - - ))} +
+

AI 최적화

+
+ {['색상 보정', '다이나믹 자막', '비트 싱크', 'SEO 메타 태그'].map(tag => ( +
+
+ {tag} +
+ ))} +
)}
{/* Right: Sharing */} -
-

공유

+
+
+

공유

-
- {socials.map(social => { - const isSelected = selectedSocials.includes(social.id); - return ( -
videoStatus === 'complete' && toggleSocial(social.id)} - className={`social-card ${isSelected ? 'selected' : ''} ${videoStatus !== 'complete' ? 'disabled' : ''}`} - > +
+ {socials.map(social => { + const isSelected = selectedSocials.includes(social.id); + return (
videoStatus === 'complete' && toggleSocial(social.id)} + className={`completion-social-card ${videoStatus !== 'complete' ? 'disabled' : ''}`} > -
{social.icon}
- {isSelected && ( -
- -
- )} +
+ {social.id} + {social.id} +
+ {social.email}
- - {social.id} - - {social.email} -
- ); - })} + ); + })} +
- +
+ - + +
diff --git a/src/pages/Dashboard/GenerationFlow.tsx b/src/pages/Dashboard/GenerationFlow.tsx index 4e2e56e..605a65d 100755 --- a/src/pages/Dashboard/GenerationFlow.tsx +++ b/src/pages/Dashboard/GenerationFlow.tsx @@ -52,6 +52,8 @@ const GenerationFlow: React.FC = ({ onHome, initialActiveIt const [maxWizardIndex, setMaxWizardIndex] = useState(savedWizardStep ? parseInt(savedWizardStep, 10) : 0); const [songTaskId, setSongTaskId] = useState(savedSongTaskId); const [imageTaskId, setImageTaskId] = useState(savedImageTaskId); + const [videoGenerationStatus, setVideoGenerationStatus] = useState<'idle' | 'generating' | 'complete' | 'error'>('idle'); + const [videoGenerationProgress, setVideoGenerationProgress] = useState(0); // URL 이미지를 ImageItem 형태로 변환하여 초기화 const [imageList, setImageList] = useState( @@ -75,7 +77,8 @@ const GenerationFlow: React.FC = ({ onHome, initialActiveIt file, preview: URL.createObjectURL(file), })); - setImageList(prev => [...prev, ...newImages]); + // 새로 업로드된 이미지를 배열 앞에 추가 (최신 이미지가 상단에 표시) + setImageList(prev => [...newImages, ...prev]); }; // 홈 버튼(로고) 클릭 시 모든 상태 초기화 후 홈으로 이동 @@ -177,6 +180,11 @@ const GenerationFlow: React.FC = ({ onHome, initialActiveIt setActiveItem('대시보드')} onNext={(taskId: string) => { + // Clear video generation state to start fresh + localStorage.removeItem(VIDEO_GENERATION_KEY); + setVideoGenerationStatus('idle'); + setVideoGenerationProgress(0); + setImageTaskId(taskId); localStorage.setItem(IMAGE_TASK_ID_KEY, taskId); scrollToWizardSection(1); @@ -197,6 +205,8 @@ const GenerationFlow: React.FC = ({ onHome, initialActiveIt }} businessInfo={businessInfo} imageTaskId={imageTaskId} + videoGenerationStatus={videoGenerationStatus} + videoGenerationProgress={videoGenerationProgress} />
{/* Step 2: Completion */} @@ -204,6 +214,8 @@ const GenerationFlow: React.FC = ({ onHome, initialActiveIt scrollToWizardSection(1)} songTaskId={songTaskId} + onVideoStatusChange={setVideoGenerationStatus} + onVideoProgressChange={setVideoGenerationProgress} />
diff --git a/src/pages/Dashboard/SoundStudioContent.tsx b/src/pages/Dashboard/SoundStudioContent.tsx index 998e40f..cd90649 100755 --- a/src/pages/Dashboard/SoundStudioContent.tsx +++ b/src/pages/Dashboard/SoundStudioContent.tsx @@ -14,6 +14,8 @@ interface SoundStudioContentProps { onNext: (songTaskId: string) => void; businessInfo?: BusinessInfo; imageTaskId: string | null; + videoGenerationStatus?: 'idle' | 'generating' | 'complete' | 'error'; + videoGenerationProgress?: number; } type GenerationStatus = 'idle' | 'generating_lyric' | 'generating_song' | 'polling' | 'complete' | 'error'; @@ -30,10 +32,26 @@ const STORAGE_KEY = 'castad_song_generation'; const STORAGE_EXPIRY = 30 * 60 * 1000; const MAX_RETRY_COUNT = 3; -const SoundStudioContent: React.FC = ({ onBack, onNext, businessInfo, imageTaskId }) => { +const LANGUAGE_FLAGS: Record = { + '한국어': '🇰🇷', + 'English': '🇺🇸', + '中文': '🇨🇳', + '日本語': '🇯🇵', + 'ไทย': '🇹🇭', + 'Tiếng Việt': '🇻🇳', +}; + +const SoundStudioContent: React.FC = ({ + onBack, + onNext, + businessInfo, + imageTaskId, + videoGenerationStatus = 'idle', + videoGenerationProgress = 0 +}) => { const [selectedType, setSelectedType] = useState('보컬'); const [selectedLang, setSelectedLang] = useState('한국어'); - const [selectedGenre, setSelectedGenre] = useState('AI 추천'); + const [selectedGenre, setSelectedGenre] = useState('자동 선택'); const [progress, setProgress] = useState(0); const [isDragging, setIsDragging] = useState(false); const [status, setStatus] = useState('idle'); @@ -47,9 +65,11 @@ const SoundStudioContent: React.FC = ({ onBack, onNext, const [statusMessage, setStatusMessage] = useState(''); const [retryCount, setRetryCount] = useState(0); const [songTaskId, setSongTaskId] = useState(null); + const [isLanguageDropdownOpen, setIsLanguageDropdownOpen] = useState(false); const progressBarRef = useRef(null); const audioRef = useRef(null); + const languageDropdownRef = useRef(null); const saveToStorage = (taskId: string, sunoTaskId: string, currentLyrics: string, currentStatus: GenerationStatus) => { const data: SavedGenerationState = { @@ -98,6 +118,23 @@ const SoundStudioContent: React.FC = ({ onBack, onNext, } }, []); + // Close language dropdown when clicking outside + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (languageDropdownRef.current && !languageDropdownRef.current.contains(event.target as Node)) { + setIsLanguageDropdownOpen(false); + } + }; + + if (isLanguageDropdownOpen) { + document.addEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [isLanguageDropdownOpen]); + const resumePolling = async (taskId: string, sunoTaskId: string, currentLyrics: string, currentRetryCount: number = 0) => { try { const downloadResponse = await waitForSongComplete( @@ -153,10 +190,14 @@ const SoundStudioContent: React.FC = ({ onBack, onNext, try { const language = LANGUAGE_MAP[selectedLang] || 'Korean'; const genreMap: Record = { - 'AI 추천': 'pop', - '로파이': 'lofi', - '힙합': 'hip-hop', - '어쿠스틱': 'acoustic', + '자동 선택': 'pop', + 'K-POP': 'kpop', + '발라드': 'ballad', + 'Hip-Hop': 'hip-hop', + 'R&B': 'rnb', + 'EDM': 'edm', + 'JAZZ': 'jazz', + 'ROCK': 'rock', }; const songResponse = await generateSong(imageTaskId, { @@ -322,10 +363,14 @@ const SoundStudioContent: React.FC = ({ onBack, onNext, setStatusMessage('음악을 생성하고 있습니다...'); const genreMap: Record = { - 'AI 추천': 'pop', - '로파이': 'lofi', - '힙합': 'hip-hop', - '어쿠스틱': 'acoustic', + '자동 선택': 'pop', + 'K-POP': 'kpop', + '발라드': 'ballad', + 'Hip-Hop': 'hip-hop', + 'R&B': 'rnb', + 'EDM': 'edm', + 'JAZZ': 'jazz', + 'ROCK': 'rock', }; const songResponse = await generateSong(imageTaskId, { @@ -369,217 +414,270 @@ const SoundStudioContent: React.FC = ({ onBack, onNext, const isGenerating = status === 'generating_lyric' || status === 'generating_song' || status === 'polling'; return ( -
+
{audioUrl && (