HydroFlux 0.0.1
This commit is contained in:
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
9
.idea/Motivation_App.iml
generated
Normal file
9
.idea/Motivation_App.iml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
1186
.idea/caches/deviceStreaming.xml
generated
Normal file
1186
.idea/caches/deviceStreaming.xml
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
.idea/deviceManager.xml
generated
Normal file
13
.idea/deviceManager.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DeviceTable">
|
||||
<option name="columnSorters">
|
||||
<list>
|
||||
<ColumnSorterState>
|
||||
<option name="column" value="Name" />
|
||||
<option name="order" value="ASCENDING" />
|
||||
</ColumnSorterState>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/misc.xml
generated
Normal file
6
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/Motivation_App.iml" filepath="$PROJECT_DIR$/.idea/Motivation_App.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
15
Hydroflux/.gitignore
vendored
Normal file
15
Hydroflux/.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
3
Hydroflux/.idea/.gitignore
generated
vendored
Normal file
3
Hydroflux/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
6
Hydroflux/.idea/AndroidProjectSystem.xml
generated
Normal file
6
Hydroflux/.idea/AndroidProjectSystem.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
||||
123
Hydroflux/.idea/codeStyles/Project.xml
generated
Normal file
123
Hydroflux/.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,123 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
Hydroflux/.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
Hydroflux/.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
6
Hydroflux/.idea/compiler.xml
generated
Normal file
6
Hydroflux/.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
||||
18
Hydroflux/.idea/deploymentTargetSelector.xml
generated
Normal file
18
Hydroflux/.idea/deploymentTargetSelector.xml
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2026-01-17T04:31:29.584970200Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=27151FDH20068C" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
13
Hydroflux/.idea/deviceManager.xml
generated
Normal file
13
Hydroflux/.idea/deviceManager.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DeviceTable">
|
||||
<option name="columnSorters">
|
||||
<list>
|
||||
<ColumnSorterState>
|
||||
<option name="column" value="Name" />
|
||||
<option name="order" value="ASCENDING" />
|
||||
</ColumnSorterState>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
19
Hydroflux/.idea/gradle.xml
generated
Normal file
19
Hydroflux/.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
10
Hydroflux/.idea/migrations.xml
generated
Normal file
10
Hydroflux/.idea/migrations.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
9
Hydroflux/.idea/misc.xml
generated
Normal file
9
Hydroflux/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
17
Hydroflux/.idea/runConfigurations.xml
generated
Normal file
17
Hydroflux/.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
1
Hydroflux/app/.gitignore
vendored
Normal file
1
Hydroflux/app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
60
Hydroflux/app/build.gradle.kts
Normal file
60
Hydroflux/app/build.gradle.kts
Normal file
@@ -0,0 +1,60 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.david.hydroflux"
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.david.hydroflux.v2"
|
||||
minSdk = 28
|
||||
targetSdk = 36
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.health.connect:connect-client:1.1.0") // Official Stable Release
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.compose.ui)
|
||||
implementation(libs.androidx.compose.ui.graphics)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
}
|
||||
21
Hydroflux/app/proguard-rules.pro
vendored
Normal file
21
Hydroflux/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.david.hydroflux
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.david.hydroflux", appContext.packageName)
|
||||
}
|
||||
}
|
||||
55
Hydroflux/app/src/main/AndroidManifest.xml
Normal file
55
Hydroflux/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<!-- Health Connect Permissions -->
|
||||
<uses-permission android:name="android.permission.health.READ_STEPS"/>
|
||||
<uses-permission android:name="android.permission.health.READ_SLEEP"/>
|
||||
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
||||
|
||||
<!-- Required to interact with Health Connect on Android 11+ -->
|
||||
<queries>
|
||||
<package android:name="com.google.android.apps.healthdata" />
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Hydroflux">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Hydroflux">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<!-- Required for Health Connect Visibility -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
|
||||
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".PermissionsRationaleActivity"
|
||||
android:exported="true"
|
||||
android:label="Health Access Rationale"
|
||||
android:theme="@style/Theme.Hydroflux">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
641
Hydroflux/app/src/main/assests/css/style.css
Normal file
641
Hydroflux/app/src/main/assests/css/style.css
Normal file
@@ -0,0 +1,641 @@
|
||||
:root {
|
||||
/* Futuristic Palette */
|
||||
--bg-dark: #050508;
|
||||
--bg-panel: rgba(20, 20, 35, 0.4);
|
||||
--primary-cyan: #00f3ff;
|
||||
--secondary-purple: #bc13fe;
|
||||
--text-main: #e0e0e0;
|
||||
--text-muted: #8a8a9b;
|
||||
--glass-border: rgba(255, 255, 255, 0.08);
|
||||
--neon-shadow: 0 0 10px rgba(0, 243, 255, 0.5);
|
||||
|
||||
/* Spacing */
|
||||
--spacing-sm: 8px;
|
||||
--spacing-md: 16px;
|
||||
--spacing-lg: 24px;
|
||||
|
||||
/* Font */
|
||||
--font-heading: 'Orbitron', sans-serif;
|
||||
--font-body: 'Outfit', sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
/* Remove mobile tap highlight */
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-main);
|
||||
font-family: var(--font-body);
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-image:
|
||||
radial-gradient(circle at 10% 20%, rgba(188, 19, 254, 0.1) 0%, transparent 40%),
|
||||
radial-gradient(circle at 90% 80%, rgba(0, 243, 255, 0.08) 0%, transparent 40%);
|
||||
}
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-family: var(--font-heading);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.glow-text {
|
||||
text-shadow: 0 0 8px rgba(0, 243, 255, 0.3);
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: var(--primary-cyan);
|
||||
}
|
||||
|
||||
/* Glassmorphism Utilities */
|
||||
.glass-header {
|
||||
background: rgba(5, 5, 8, 0.7);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
padding: var(--spacing-md);
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.glass-panel {
|
||||
background: var(--bg-panel);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 20px;
|
||||
padding: var(--spacing-lg);
|
||||
margin: var(--spacing-md);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.glass-nav {
|
||||
background: rgba(10, 10, 16, 0.8);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border-top: 1px solid var(--glass-border);
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding-bottom: max(16px, env(safe-area-inset-bottom));
|
||||
/* Safe area for iOS */
|
||||
}
|
||||
|
||||
/* Content Area */
|
||||
#main-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 80px;
|
||||
/* Space for nav */
|
||||
}
|
||||
|
||||
/* Interactive Elements */
|
||||
.nav-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
padding: 10px;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
color: var(--primary-cyan);
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
box-shadow: 0 0 15px rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.nav-btn svg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes pulse-glow {
|
||||
0% {
|
||||
box-shadow: 0 0 5px rgba(0, 243, 255, 0.5);
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: 0 0 20px rgba(0, 243, 255, 0.8);
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: 0 0 5px rgba(0, 243, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Water Tracker Module Styles --- */
|
||||
|
||||
.water-tracker-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.circle-container {
|
||||
position: relative;
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
border-radius: 50%;
|
||||
border: 4px solid var(--bg-panel);
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
||||
overflow: hidden;
|
||||
/* Clips the wave */
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.water-circle {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Wave Animation */
|
||||
.wave {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
/* Dynamic */
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: rgba(0, 243, 255, 0.4);
|
||||
border-radius: 40%;
|
||||
animation: rotate 6s linear infinite;
|
||||
transition: top 1s ease-in-out;
|
||||
}
|
||||
|
||||
.wave::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 243, 255, 0.3);
|
||||
/* Lighter top layer */
|
||||
border-radius: 40%;
|
||||
animation: rotate 10s linear infinite reverse;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.circle-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 2;
|
||||
/* Above wave */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.water-percentage {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.water-label {
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 2px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.stats-row {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.2rem;
|
||||
color: var(--primary-cyan);
|
||||
}
|
||||
|
||||
/* Controls */
|
||||
.controls-area {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bottle-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.bottle-selector input {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid var(--primary-cyan);
|
||||
color: var(--primary-cyan);
|
||||
padding: 10px;
|
||||
border-radius: 12px;
|
||||
font-family: var(--font-heading);
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
font-size: 1.1rem;
|
||||
outline: none;
|
||||
box-shadow: 0 0 10px rgba(0, 243, 255, 0.1);
|
||||
}
|
||||
|
||||
.bottle-selector input:focus {
|
||||
box-shadow: 0 0 15px rgba(0, 243, 255, 0.3);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid var(--glass-border);
|
||||
color: var(--text-muted);
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.icon-btn.secondary {
|
||||
border-color: rgba(255, 50, 50, 0.3);
|
||||
color: #ff4d4d;
|
||||
}
|
||||
|
||||
.icon-btn:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.glow-btn {
|
||||
position: relative;
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border: 1px solid var(--primary-cyan);
|
||||
color: var(--primary-cyan);
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
padding: 0 40px;
|
||||
height: 60px;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
box-shadow: 0 0 10px rgba(0, 243, 255, 0.2), inset 0 0 20px rgba(0, 243, 255, 0.1);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glow-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(0, 243, 255, 0.4), transparent);
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.glow-btn:hover {
|
||||
background: rgba(0, 243, 255, 0.2);
|
||||
box-shadow: 0 0 30px rgba(0, 243, 255, 0.6);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 8px rgba(0, 243, 255, 0.8);
|
||||
}
|
||||
|
||||
.glow-btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.glow-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.glow-btn svg {
|
||||
filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.8));
|
||||
}
|
||||
|
||||
/* --- Streak Module Styles --- */
|
||||
.streak-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-lg);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.2rem;
|
||||
color: var(--secondary-purple);
|
||||
letter-spacing: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.streak-counter {
|
||||
background: radial-gradient(circle, rgba(188, 19, 254, 0.1) 0%, transparent 70%);
|
||||
padding: 40px;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.streak-days {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 6rem;
|
||||
color: white;
|
||||
line-height: 1;
|
||||
text-shadow: 0 0 20px rgba(188, 19, 254, 0.6);
|
||||
}
|
||||
|
||||
.streak-label {
|
||||
font-size: 1.5rem;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 5px;
|
||||
}
|
||||
|
||||
.streak-detail {
|
||||
font-size: 1rem;
|
||||
color: var(--primary-cyan);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.quote-card {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-left: 3px solid var(--primary-cyan);
|
||||
padding: 20px;
|
||||
font-style: italic;
|
||||
color: #dedede;
|
||||
margin: 20px 0;
|
||||
line-height: 1.6;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.danger-btn {
|
||||
position: relative;
|
||||
background: rgba(255, 50, 50, 0.1);
|
||||
border: 1px solid #ff4d4d;
|
||||
color: #ff4d4d;
|
||||
font-family: var(--font-heading);
|
||||
padding: 15px 40px;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 2px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
margin-top: 30px;
|
||||
box-shadow: 0 0 10px rgba(255, 50, 50, 0.2), inset 0 0 20px rgba(255, 50, 50, 0.1);
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.danger-btn:hover {
|
||||
background: rgba(255, 50, 50, 0.2);
|
||||
box-shadow: 0 0 30px rgba(255, 50, 50, 0.6);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 8px rgba(255, 50, 50, 0.8);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.danger-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* --- Connect Button (Neon Cyan Variant) --- */
|
||||
.connect-glow-btn {
|
||||
position: relative;
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border: 1px solid var(--primary-cyan);
|
||||
color: var(--primary-cyan);
|
||||
font-family: var(--font-heading);
|
||||
padding: 8px 24px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
letter-spacing: 1px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 0 10px rgba(0, 243, 255, 0.2), inset 0 0 10px rgba(0, 243, 255, 0.1);
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.connect-glow-btn:hover {
|
||||
background: rgba(0, 243, 255, 0.2);
|
||||
box-shadow: 0 0 20px rgba(0, 243, 255, 0.6);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 5px rgba(0, 243, 255, 0.8);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.connect-glow-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* --- Concentric Rings --- */
|
||||
.rings-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.02) 0%, transparent 70%);
|
||||
padding: 20px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.concentric-svg {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.ring-bg {
|
||||
fill: none;
|
||||
stroke: rgba(255, 255, 255, 0.05);
|
||||
/* Proper track color */
|
||||
}
|
||||
|
||||
.ring-progress {
|
||||
fill: none;
|
||||
stroke-linecap: round;
|
||||
transition: stroke-dashoffset 1.5s ease-in-out;
|
||||
}
|
||||
|
||||
.ring-progress.cyan {
|
||||
stroke: var(--primary-cyan);
|
||||
filter: drop-shadow(0 0 5px var(--primary-cyan));
|
||||
}
|
||||
|
||||
.ring-progress.purple {
|
||||
stroke: var(--secondary-purple);
|
||||
filter: drop-shadow(0 0 5px var(--secondary-purple));
|
||||
}
|
||||
|
||||
.rings-legend {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.dot.cyan {
|
||||
background: var(--primary-cyan);
|
||||
box-shadow: 0 0 5px var(--primary-cyan);
|
||||
}
|
||||
|
||||
.dot.purple {
|
||||
background: var(--secondary-purple);
|
||||
box-shadow: 0 0 5px var(--secondary-purple);
|
||||
}
|
||||
|
||||
/* --- Combined Stat Card & Charts --- */
|
||||
.stat-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-sub {
|
||||
font-size: 0.8rem;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.icon-box {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cyan-box {
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
color: var(--primary-cyan);
|
||||
}
|
||||
|
||||
.purple-box {
|
||||
background: rgba(188, 19, 254, 0.1);
|
||||
color: var(--secondary-purple);
|
||||
}
|
||||
|
||||
.chart-divider {
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
/* Re-using chart styles but refining for the box */
|
||||
.chart-container.small {
|
||||
height: 100px;
|
||||
margin-top: 0;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.chart-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chart-bar {
|
||||
width: 6px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 3px;
|
||||
transition: height 1s ease-out;
|
||||
}
|
||||
|
||||
.chart-bar.cyan {
|
||||
background: var(--primary-cyan);
|
||||
box-shadow: 0 0 5px rgba(0, 243, 255, 0.3);
|
||||
}
|
||||
|
||||
.chart-bar.purple {
|
||||
background: var(--secondary-purple);
|
||||
box-shadow: 0 0 5px rgba(188, 19, 254, 0.3);
|
||||
}
|
||||
|
||||
.chart-day {
|
||||
margin-top: 8px;
|
||||
font-size: 0.6rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
74
Hydroflux/app/src/main/assests/index.html
Normal file
74
Hydroflux/app/src/main/assests/index.html
Normal file
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>HydroFlux</title>
|
||||
<meta name="description" content="Futuristic Hydration & Fitness Tracker">
|
||||
<meta name="theme-color" content="#0a0a12">
|
||||
|
||||
<!-- PWA Application Settings -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="HydroFlux">
|
||||
|
||||
<!-- Fonts: Orbitron for headers, Outfit for body text -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Outfit:wght@300;400;600&display=swap"
|
||||
rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="css/style.css?v=3">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- Main Layout injected here -->
|
||||
<header class="glass-header">
|
||||
<h1 class="glow-text">HYDRO<span class="highlight">FLUX</span></h1>
|
||||
<div id="connection-status" class="status-indicator"></div>
|
||||
</header>
|
||||
|
||||
<main id="main-content">
|
||||
<!-- Dynamic Content -->
|
||||
<section id="water-section" class="glass-panel">
|
||||
<!-- Water Tracker injected via JS -->
|
||||
<div class="loader">Loading Core...</div>
|
||||
</section>
|
||||
|
||||
<section id="streak-section" class="glass-panel" style="display: none;">
|
||||
<!-- Streak Tracker injected via JS -->
|
||||
</section>
|
||||
|
||||
<section id="fitness-section" class="glass-panel" style="display: none;">
|
||||
<h2>FITNESS DATA</h2>
|
||||
<p style="text-align:center; color: var(--text-muted); margin-top: 20px;">Coming Soon</p>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<nav class="glass-nav">
|
||||
<button class="nav-btn active" data-view="water">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="nav-btn" data-view="streak">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path
|
||||
d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="nav-btn" data-view="fitness">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<script type="module" src="js/app.js?v=2"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
28
Hydroflux/app/src/main/assests/js/app.js
Normal file
28
Hydroflux/app/src/main/assests/js/app.js
Normal file
@@ -0,0 +1,28 @@
|
||||
console.log('HydroFlux Initialized');
|
||||
|
||||
// Simple Navigation Logic
|
||||
document.querySelectorAll('.nav-btn').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
// Toggle Active State
|
||||
document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
|
||||
e.currentTarget.classList.add('active');
|
||||
|
||||
const view = e.currentTarget.dataset.view;
|
||||
|
||||
// Hide all sections
|
||||
document.querySelectorAll('section').forEach(el => el.style.display = 'none');
|
||||
|
||||
// Show target section
|
||||
const target = document.getElementById(`${view}-section`);
|
||||
if (target) target.style.display = 'block';
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize Modules
|
||||
import { WaterTracker } from './modules/water.js?v=2';
|
||||
import { StreakTracker } from './modules/streak.js?v=2';
|
||||
import { FitnessDashboard } from './modules/fitness.js?v=2';
|
||||
|
||||
const waterTracker = new WaterTracker('water-section');
|
||||
const streakTracker = new StreakTracker('streak-section');
|
||||
const fitnessDashboard = new FitnessDashboard('fitness-section');
|
||||
153
Hydroflux/app/src/main/assests/js/modules/fitness.js
Normal file
153
Hydroflux/app/src/main/assests/js/modules/fitness.js
Normal file
@@ -0,0 +1,153 @@
|
||||
export class FitnessDashboard {
|
||||
constructor(containerId) {
|
||||
this.container = document.getElementById(containerId);
|
||||
// Mock Data
|
||||
this.data = {
|
||||
steps: { current: 8432, goal: 10000 },
|
||||
sleep: { current: 6.5, goal: 8 },
|
||||
history: {
|
||||
steps: [4500, 7200, 10500, 8900, 6000, 11200, 8432],
|
||||
sleep: [5.5, 6.0, 7.5, 8.2, 5.0, 9.0, 6.5]
|
||||
}
|
||||
};
|
||||
|
||||
this.render();
|
||||
// Delay animation to allow DOM paint
|
||||
setTimeout(() => this.animate(), 100);
|
||||
}
|
||||
|
||||
render() {
|
||||
const stepPercent = Math.min((this.data.steps.current / this.data.steps.goal) * 100, 100);
|
||||
const sleepPercent = Math.min((this.data.sleep.current / this.data.sleep.goal) * 100, 100);
|
||||
|
||||
// Ring Config
|
||||
const center = 100;
|
||||
const radiusOuter = 80;
|
||||
const radiusInner = 55;
|
||||
|
||||
const circumOuter = 2 * Math.PI * radiusOuter;
|
||||
const circumInner = 2 * Math.PI * radiusInner;
|
||||
|
||||
this.container.innerHTML = `
|
||||
<div class="fitness-container">
|
||||
<h2 class="section-title">DAILY ACTIVITY</h2>
|
||||
|
||||
<!-- Top: Concentric Rings -->
|
||||
<div class="rings-wrapper">
|
||||
<svg class="concentric-svg" viewBox="0 0 200 200">
|
||||
<!-- Outer Track (Steps) -->
|
||||
<circle class="ring-bg" cx="${center}" cy="${center}" r="${radiusOuter}" stroke-width="18"></circle>
|
||||
<!-- Inner Track (Sleep) -->
|
||||
<circle class="ring-bg" cx="${center}" cy="${center}" r="${radiusInner}" stroke-width="18"></circle>
|
||||
|
||||
<!-- Outer Progress (Steps - Cyan) -->
|
||||
<circle class="ring-progress cyan" cx="${center}" cy="${center}" r="${radiusOuter}"
|
||||
stroke-width="18"
|
||||
stroke-dasharray="${circumOuter}"
|
||||
stroke-dashoffset="${circumOuter}"
|
||||
data-offset="${circumOuter - (stepPercent / 100) * circumOuter}"></circle>
|
||||
|
||||
<!-- Inner Progress (Sleep - Purple) -->
|
||||
<circle class="ring-progress purple" cx="${center}" cy="${center}" r="${radiusInner}"
|
||||
stroke-width="18"
|
||||
stroke-dasharray="${circumInner}"
|
||||
stroke-dashoffset="${circumInner}"
|
||||
data-offset="${circumInner - (sleepPercent / 100) * circumInner}"></circle>
|
||||
|
||||
<!-- Icons/Center -->
|
||||
<image href="https://fonts.gstatic.com/s/i/materialicons/bolt/v5/24px.svg" x="90" y="90" height="20" width="20" style="filter: invert(1); opacity: 0.5;" />
|
||||
</svg>
|
||||
|
||||
<!-- Legend to explain the rings -->
|
||||
<div class="rings-legend">
|
||||
<div class="legend-item">
|
||||
<span class="dot cyan"></span> STEPS
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="dot purple"></span> SLEEP
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Middle: Device -->
|
||||
<div class="device-card">
|
||||
<div class="device-info">
|
||||
<span class="device-name">TicWatch Pro 5</span>
|
||||
<span class="device-status">Disconnected</span>
|
||||
</div>
|
||||
<button id="connect-watch-btn" class="connect-glow-btn">LINK</button>
|
||||
</div>
|
||||
|
||||
<!-- Box 1: Steps (Current + History) -->
|
||||
<div class="stat-card">
|
||||
<div class="stat-header">
|
||||
<div>
|
||||
<span class="stat-label">STEPS</span>
|
||||
<div class="stat-value">${this.data.steps.current}</div>
|
||||
<div class="stat-sub">Goal: ${this.data.steps.goal}</div>
|
||||
</div>
|
||||
<div class="icon-box cyan-box">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-divider"></div>
|
||||
|
||||
<div class="chart-container small">
|
||||
${this.generateBars(this.data.history.steps, 12000, 'cyan')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Box 2: Sleep (Current + History) -->
|
||||
<div class="stat-card">
|
||||
<div class="stat-header">
|
||||
<div>
|
||||
<span class="stat-label">SLEEP</span>
|
||||
<div class="stat-value">${this.data.sleep.current}h</div>
|
||||
<div class="stat-sub">Goal: ${this.data.sleep.goal}h</div>
|
||||
</div>
|
||||
<div class="icon-box purple-box">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-divider"></div>
|
||||
|
||||
<div class="chart-container small">
|
||||
${this.generateBars(this.data.history.sleep, 10, 'purple')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.attachEvents();
|
||||
}
|
||||
|
||||
generateBars(data, max, colorClass) {
|
||||
const days = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
|
||||
return data.map((val, index) => {
|
||||
const height = Math.min((val / max) * 100, 100);
|
||||
return `
|
||||
<div class="chart-column">
|
||||
<div class="chart-bar ${colorClass}" style="height: ${height}%"></div>
|
||||
<span class="chart-day">${days[index]}</span>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
animate() {
|
||||
this.container.querySelectorAll('.ring-progress').forEach(ring => {
|
||||
ring.style.strokeDashoffset = ring.dataset.offset;
|
||||
});
|
||||
}
|
||||
|
||||
attachEvents() {
|
||||
const btn = this.container.querySelector('#connect-watch-btn');
|
||||
if (btn) {
|
||||
btn.addEventListener('click', () => {
|
||||
alert("Placeholder: This feature would connect to the WearOS API in the native app.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
102
Hydroflux/app/src/main/assests/js/modules/streak.js
Normal file
102
Hydroflux/app/src/main/assests/js/modules/streak.js
Normal file
@@ -0,0 +1,102 @@
|
||||
export class StreakTracker {
|
||||
constructor(containerId) {
|
||||
this.container = document.getElementById(containerId);
|
||||
this.STORAGE_KEY = 'hydroflux_streak';
|
||||
this.quotes = [
|
||||
"The only easy day was yesterday.",
|
||||
"Discipline is doing what needs to be done, even if you don't want to.",
|
||||
"Your future self is watching you right now through memories.",
|
||||
"Pain is temporary. Quitting lasts forever.",
|
||||
"Suffering is the currency of success.",
|
||||
"Don't stop when you're tired. Stop when you're done.",
|
||||
"You are stronger than your urges.",
|
||||
"Focus on the goal, not the obstacle."
|
||||
];
|
||||
|
||||
this.loadState();
|
||||
this.render();
|
||||
this.startTimer();
|
||||
}
|
||||
|
||||
loadState() {
|
||||
const saved = localStorage.getItem(this.STORAGE_KEY);
|
||||
if (saved) {
|
||||
this.startDate = new Date(parseInt(saved));
|
||||
} else {
|
||||
this.startDate = new Date();
|
||||
this.saveState();
|
||||
}
|
||||
}
|
||||
|
||||
saveState() {
|
||||
localStorage.setItem(this.STORAGE_KEY, this.startDate.getTime().toString());
|
||||
}
|
||||
|
||||
resetStreak() {
|
||||
if (confirm("Are you sure you want to reset your streak?")) {
|
||||
this.startDate = new Date();
|
||||
this.saveState();
|
||||
this.updateUI();
|
||||
|
||||
// Haptic Bad Feedback
|
||||
if (navigator.vibrate) navigator.vibrate([100, 50, 100]);
|
||||
}
|
||||
}
|
||||
|
||||
getDuration() {
|
||||
const now = new Date();
|
||||
const diff = now - this.startDate;
|
||||
|
||||
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
|
||||
|
||||
return { days, hours, minutes };
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
const { days, hours, minutes } = this.getDuration();
|
||||
|
||||
const daysEl = this.container.querySelector('.streak-days');
|
||||
const detailEl = this.container.querySelector('.streak-detail');
|
||||
|
||||
if (daysEl) daysEl.textContent = days;
|
||||
if (detailEl) detailEl.textContent = `${hours}h ${minutes}m`;
|
||||
}
|
||||
|
||||
startTimer() {
|
||||
setInterval(() => this.updateUI(), 60000); // Update every minute
|
||||
}
|
||||
|
||||
getRandomQuote() {
|
||||
return this.quotes[Math.floor(Math.random() * this.quotes.length)];
|
||||
}
|
||||
|
||||
render() {
|
||||
this.container.innerHTML = `
|
||||
<div class="streak-container">
|
||||
<h2 class="section-title">ABSTINENCE STREAK</h2>
|
||||
|
||||
<div class="streak-counter">
|
||||
<div class="streak-days glow-text">0</div>
|
||||
<div class="streak-label">DAYS</div>
|
||||
<div class="streak-detail">${0}h ${0}m</div>
|
||||
</div>
|
||||
|
||||
<div class="quote-card">
|
||||
"${this.getRandomQuote()}"
|
||||
</div>
|
||||
|
||||
<button id="reset-streak-btn" class="danger-btn">
|
||||
RESET STREAK
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.updateUI();
|
||||
|
||||
this.container.querySelector('#reset-streak-btn').addEventListener('click', () => {
|
||||
this.resetStreak();
|
||||
});
|
||||
}
|
||||
}
|
||||
184
Hydroflux/app/src/main/assests/js/modules/water.js
Normal file
184
Hydroflux/app/src/main/assests/js/modules/water.js
Normal file
@@ -0,0 +1,184 @@
|
||||
|
||||
export class WaterTracker {
|
||||
constructor(containerId) {
|
||||
this.container = document.getElementById(containerId);
|
||||
this.state = {
|
||||
current: 0,
|
||||
goal: 3000, // mL
|
||||
bottleSize: 500, // mL
|
||||
};
|
||||
this.STORAGE_KEY = 'hydroflux_data';
|
||||
this.loadState();
|
||||
this.render();
|
||||
this.attachEvents();
|
||||
this.updateUI();
|
||||
}
|
||||
|
||||
loadState() {
|
||||
const saved = localStorage.getItem(this.STORAGE_KEY);
|
||||
if (saved) {
|
||||
const parsed = JSON.parse(saved);
|
||||
this.state = { ...this.state, ...parsed };
|
||||
}
|
||||
}
|
||||
|
||||
saveState() {
|
||||
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.state));
|
||||
this.updateUI();
|
||||
}
|
||||
|
||||
addWater() {
|
||||
this.state.current += this.state.bottleSize;
|
||||
this.saveState();
|
||||
if (navigator.vibrate) navigator.vibrate(50);
|
||||
}
|
||||
|
||||
removeWater() {
|
||||
this.state.current = Math.max(0, this.state.current - this.state.bottleSize);
|
||||
this.saveState();
|
||||
if (navigator.vibrate) navigator.vibrate(50);
|
||||
}
|
||||
|
||||
setBottleSize(size) {
|
||||
if (!size || size <= 0) return;
|
||||
this.state.bottleSize = size;
|
||||
this.saveState();
|
||||
}
|
||||
|
||||
getPercentage() {
|
||||
return Math.min(100, Math.max(0, (this.state.current / this.state.goal) * 100));
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
// Update Text
|
||||
const currentEl = this.container.querySelector('.water-count');
|
||||
const percentageEl = this.container.querySelector('.water-percentage');
|
||||
|
||||
if (currentEl) currentEl.textContent = `${this.state.current} / ${this.state.goal} mL`;
|
||||
if (percentageEl) percentageEl.textContent = `${Math.round(this.getPercentage())}%`;
|
||||
|
||||
// Update Wave Animation Height
|
||||
const wave = this.container.querySelector('.wave');
|
||||
if (wave) {
|
||||
wave.style.top = `${100 - this.getPercentage()}%`;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
this.container.innerHTML = `
|
||||
<div class="water-tracker-container">
|
||||
<!-- Circular Progress -->
|
||||
<div class="circle-container">
|
||||
<div class="water-circle">
|
||||
<div class="wave"></div>
|
||||
<div class="circle-content">
|
||||
<span class="water-percentage">0%</span>
|
||||
<span class="water-label">HYDRATION</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Display -->
|
||||
<div class="stats-row">
|
||||
<span class="water-count">0 / 3000 mL</span>
|
||||
</div>
|
||||
|
||||
<!-- Controls -->
|
||||
<div class="controls-area">
|
||||
<div class="bottle-selector">
|
||||
<label>Bottle (mL):</label>
|
||||
<input type="number" id="bottle-size-input" value="${this.state.bottleSize}" min="1" max="5000">
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button id="remove-water-btn" class="icon-btn secondary" aria-label="Remove Drink">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M5 12h14"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button id="add-water-btn" class="glow-btn">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 5v14M5 12h14"/>
|
||||
</svg>
|
||||
DRINK
|
||||
</button>
|
||||
|
||||
<!-- Notification Toggle -->
|
||||
<button id="notify-btn" class="icon-btn" title="Enable Reminders">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
|
||||
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.checkNotificationStatus();
|
||||
}
|
||||
|
||||
attachEvents() {
|
||||
this.container.querySelector('#add-water-btn').addEventListener('click', () => {
|
||||
this.addWater();
|
||||
});
|
||||
|
||||
this.container.querySelector('#remove-water-btn').addEventListener('click', () => {
|
||||
this.removeWater();
|
||||
});
|
||||
|
||||
this.container.querySelector('#notify-btn').addEventListener('click', (e) => {
|
||||
this.toggleNotifications(e.currentTarget);
|
||||
});
|
||||
|
||||
const input = this.container.querySelector('#bottle-size-input');
|
||||
input.addEventListener('change', (e) => {
|
||||
this.setBottleSize(parseInt(e.target.value));
|
||||
});
|
||||
}
|
||||
|
||||
// --- Notification Logic ---
|
||||
|
||||
toggleNotifications(btn) {
|
||||
if (!("Notification" in window)) {
|
||||
alert("This browser does not support desktop notifications");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Notification.permission === "granted") {
|
||||
alert("Reminders are active! We'll check every hour.");
|
||||
} else if (Notification.permission !== "denied") {
|
||||
Notification.requestPermission().then(permission => {
|
||||
if (permission === "granted") {
|
||||
this.startReminderLoop();
|
||||
btn.style.color = "var(--primary-cyan)";
|
||||
new Notification("HydroFlux", { body: "Smart Hydration Reminders Enabled!" });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
checkNotificationStatus() {
|
||||
if (Notification.permission === "granted") {
|
||||
const btn = this.container.querySelector('#notify-btn');
|
||||
if (btn) btn.style.color = "var(--primary-cyan)";
|
||||
this.startReminderLoop();
|
||||
}
|
||||
}
|
||||
|
||||
startReminderLoop() {
|
||||
// Clear existing to avoid duplicates
|
||||
if (this.reminderInterval) clearInterval(this.reminderInterval);
|
||||
|
||||
// Check every minute if it's been > 1 hour since last drink
|
||||
this.reminderInterval = setInterval(() => {
|
||||
// Pseudo-logic check since we don't store timestamp in this simple version yet
|
||||
// In a real app, you'd check this.state.lastDrinkTime
|
||||
new Notification("HydroFlux Needs You", {
|
||||
body: "Remember to drink water!",
|
||||
icon: "/icon.png"
|
||||
});
|
||||
}, 3600000); // 1 Hour
|
||||
}
|
||||
}
|
||||
17
Hydroflux/app/src/main/assests/manifest.json
Normal file
17
Hydroflux/app/src/main/assests/manifest.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "HydroFlux Motivation",
|
||||
"short_name": "HydroFlux",
|
||||
"background_color": "#050508",
|
||||
"theme_color": "#050508",
|
||||
"display": "standalone",
|
||||
"orientation": "portrait",
|
||||
"scope": "/",
|
||||
"start_url": "/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.svg",
|
||||
"sizes": "192x192",
|
||||
"type": "image/svg+xml"
|
||||
}
|
||||
]
|
||||
}
|
||||
40
Hydroflux/app/src/main/assets/css/fitness_stats.css
Normal file
40
Hydroflux/app/src/main/assets/css/fitness_stats.css
Normal file
@@ -0,0 +1,40 @@
|
||||
/* --- New Fitness Stats Display --- */
|
||||
.rings-stats {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 40px;
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.stat-item.cyan .stat-number {
|
||||
color: var(--primary-cyan);
|
||||
text-shadow: 0 0 10px rgba(0, 243, 255, 0.4);
|
||||
}
|
||||
|
||||
.stat-item.purple .stat-number {
|
||||
color: var(--secondary-purple);
|
||||
text-shadow: 0 0 10px rgba(188, 19, 254, 0.4);
|
||||
}
|
||||
|
||||
.stat-label-small {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
955
Hydroflux/app/src/main/assets/css/style.css
Normal file
955
Hydroflux/app/src/main/assets/css/style.css
Normal file
@@ -0,0 +1,955 @@
|
||||
:root {
|
||||
/* Futuristic Palette */
|
||||
--bg-dark: #050508;
|
||||
--bg-panel: rgba(20, 20, 35, 0.4);
|
||||
--primary-cyan: #00f3ff;
|
||||
--secondary-purple: #bc13fe;
|
||||
--text-main: #e0e0e0;
|
||||
--text-muted: #8a8a9b;
|
||||
--glass-border: rgba(255, 255, 255, 0.08);
|
||||
--neon-shadow: 0 0 10px rgba(0, 243, 255, 0.5);
|
||||
|
||||
/* Spacing */
|
||||
--spacing-sm: 8px;
|
||||
--spacing-md: 16px;
|
||||
--spacing-lg: 24px;
|
||||
|
||||
/* Font */
|
||||
--font-heading: 'Orbitron', sans-serif;
|
||||
--font-body: 'Outfit', sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
/* Remove mobile tap highlight */
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-main);
|
||||
font-family: var(--font-body);
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-image:
|
||||
radial-gradient(circle at 10% 20%, rgba(188, 19, 254, 0.1) 0%, transparent 40%),
|
||||
radial-gradient(circle at 90% 80%, rgba(0, 243, 255, 0.08) 0%, transparent 40%);
|
||||
}
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-family: var(--font-heading);
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.glow-text {
|
||||
text-shadow: 0 0 8px rgba(0, 243, 255, 0.3);
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: var(--primary-cyan);
|
||||
}
|
||||
|
||||
/* Glassmorphism Utilities */
|
||||
.glass-header {
|
||||
background: rgba(5, 5, 8, 0.7);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
padding: var(--spacing-md);
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.glass-panel {
|
||||
background: var(--bg-panel);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 20px;
|
||||
padding: var(--spacing-lg);
|
||||
margin: var(--spacing-md);
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.glass-nav {
|
||||
background: rgba(10, 10, 16, 0.8);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border-top: 1px solid var(--glass-border);
|
||||
padding: var(--spacing-md) var(--spacing-lg);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding-bottom: max(16px, env(safe-area-inset-bottom));
|
||||
/* Safe area for iOS */
|
||||
}
|
||||
|
||||
/* Content Area */
|
||||
#main-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 80px;
|
||||
/* Space for nav */
|
||||
}
|
||||
|
||||
/* Interactive Elements */
|
||||
.nav-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
padding: 10px;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
color: var(--primary-cyan);
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
box-shadow: 0 0 15px rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.nav-btn svg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes pulse-glow {
|
||||
0% {
|
||||
box-shadow: 0 0 5px rgba(0, 243, 255, 0.5);
|
||||
}
|
||||
|
||||
50% {
|
||||
box-shadow: 0 0 20px rgba(0, 243, 255, 0.8);
|
||||
}
|
||||
|
||||
100% {
|
||||
box-shadow: 0 0 5px rgba(0, 243, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Water Tracker Module Styles --- */
|
||||
|
||||
.water-tracker-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-lg);
|
||||
}
|
||||
|
||||
.circle-container {
|
||||
position: relative;
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
border-radius: 50%;
|
||||
border: 4px solid var(--bg-panel);
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
|
||||
overflow: hidden;
|
||||
/* Clips the wave */
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.water-circle {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Wave Animation */
|
||||
.wave {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
/* Dynamic */
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: rgba(0, 243, 255, 0.4);
|
||||
border-radius: 40%;
|
||||
animation: rotate 6s linear infinite;
|
||||
transition: top 1s ease-in-out;
|
||||
}
|
||||
|
||||
.wave::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 243, 255, 0.3);
|
||||
/* Lighter top layer */
|
||||
border-radius: 40%;
|
||||
animation: rotate 10s linear infinite reverse;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.circle-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 2;
|
||||
/* Above wave */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.water-percentage {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.water-label {
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 2px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.stats-row {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.2rem;
|
||||
color: var(--primary-cyan);
|
||||
}
|
||||
|
||||
/* Controls */
|
||||
.controls-area {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-md);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bottle-selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.bottle-selector input {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid var(--primary-cyan);
|
||||
color: var(--primary-cyan);
|
||||
padding: 10px;
|
||||
border-radius: 12px;
|
||||
font-family: var(--font-heading);
|
||||
width: 80px;
|
||||
text-align: center;
|
||||
font-size: 1.1rem;
|
||||
outline: none;
|
||||
box-shadow: 0 0 10px rgba(0, 243, 255, 0.1);
|
||||
}
|
||||
|
||||
.bottle-selector input:focus {
|
||||
box-shadow: 0 0 15px rgba(0, 243, 255, 0.3);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid var(--glass-border);
|
||||
color: var(--text-muted);
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.icon-btn.secondary {
|
||||
border-color: rgba(255, 50, 50, 0.3);
|
||||
color: #ff4d4d;
|
||||
}
|
||||
|
||||
.icon-btn:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.glow-btn {
|
||||
position: relative;
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border: 1px solid var(--primary-cyan);
|
||||
color: var(--primary-cyan);
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
padding: 0 40px;
|
||||
height: 60px;
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
box-shadow: 0 0 10px rgba(0, 243, 255, 0.2), inset 0 0 20px rgba(0, 243, 255, 0.1);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glow-btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(0, 243, 255, 0.4), transparent);
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.glow-btn:hover {
|
||||
background: rgba(0, 243, 255, 0.2);
|
||||
box-shadow: 0 0 30px rgba(0, 243, 255, 0.6);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 8px rgba(0, 243, 255, 0.8);
|
||||
}
|
||||
|
||||
.glow-btn:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.glow-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.glow-btn svg {
|
||||
filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.8));
|
||||
}
|
||||
|
||||
/* --- Streak Module Styles --- */
|
||||
.streak-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--spacing-lg);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.2rem;
|
||||
color: var(--secondary-purple);
|
||||
letter-spacing: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.streak-counter {
|
||||
background: radial-gradient(circle, rgba(188, 19, 254, 0.1) 0%, transparent 70%);
|
||||
padding: 40px;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.streak-days {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 6rem;
|
||||
color: white;
|
||||
line-height: 1;
|
||||
text-shadow: 0 0 20px rgba(188, 19, 254, 0.6);
|
||||
}
|
||||
|
||||
.streak-label {
|
||||
font-size: 1.5rem;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 5px;
|
||||
}
|
||||
|
||||
.streak-detail {
|
||||
font-size: 1rem;
|
||||
color: var(--primary-cyan);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.quote-card {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-left: 3px solid var(--primary-cyan);
|
||||
padding: 20px;
|
||||
font-style: italic;
|
||||
color: #dedede;
|
||||
margin: 20px 0;
|
||||
line-height: 1.6;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.danger-btn {
|
||||
position: relative;
|
||||
background: rgba(255, 50, 50, 0.1);
|
||||
border: 1px solid #ff4d4d;
|
||||
color: #ff4d4d;
|
||||
font-family: var(--font-heading);
|
||||
padding: 30px 20px 15px;
|
||||
/* Increased top padding for camera cutout */
|
||||
border-radius: 30px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 2px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
margin-top: 30px;
|
||||
box-shadow: 0 0 10px rgba(255, 50, 50, 0.2), inset 0 0 20px rgba(255, 50, 50, 0.1);
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.danger-btn:hover {
|
||||
background: rgba(255, 50, 50, 0.2);
|
||||
box-shadow: 0 0 30px rgba(255, 50, 50, 0.6);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 8px rgba(255, 50, 50, 0.8);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.danger-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* --- Connect Button (Neon Cyan Variant) --- */
|
||||
.connect-glow-btn {
|
||||
position: relative;
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
border: 1px solid var(--primary-cyan);
|
||||
color: var(--primary-cyan);
|
||||
font-family: var(--font-heading);
|
||||
padding: 8px 24px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
letter-spacing: 1px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 0 0 10px rgba(0, 243, 255, 0.2), inset 0 0 10px rgba(0, 243, 255, 0.1);
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.connect-glow-btn:hover {
|
||||
background: rgba(0, 243, 255, 0.2);
|
||||
box-shadow: 0 0 20px rgba(0, 243, 255, 0.6);
|
||||
color: #fff;
|
||||
text-shadow: 0 0 5px rgba(0, 243, 255, 0.8);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.connect-glow-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* --- Concentric Rings --- */
|
||||
.rings-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
background: radial-gradient(circle, rgba(255, 255, 255, 0.02) 0%, transparent 70%);
|
||||
padding: 20px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.concentric-svg {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.ring-bg {
|
||||
fill: none;
|
||||
stroke: rgba(255, 255, 255, 0.05);
|
||||
/* Proper track color */
|
||||
}
|
||||
|
||||
.ring-progress {
|
||||
fill: none;
|
||||
stroke-linecap: round;
|
||||
transition: stroke-dashoffset 1.5s ease-in-out;
|
||||
}
|
||||
|
||||
.ring-progress.cyan {
|
||||
stroke: var(--primary-cyan);
|
||||
filter: drop-shadow(0 0 5px var(--primary-cyan));
|
||||
}
|
||||
|
||||
.ring-progress.purple {
|
||||
stroke: var(--secondary-purple);
|
||||
filter: drop-shadow(0 0 5px var(--secondary-purple));
|
||||
}
|
||||
|
||||
.rings-legend {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.dot.cyan {
|
||||
background: var(--primary-cyan);
|
||||
box-shadow: 0 0 5px var(--primary-cyan);
|
||||
}
|
||||
|
||||
.dot.purple {
|
||||
background: var(--secondary-purple);
|
||||
box-shadow: 0 0 5px var(--secondary-purple);
|
||||
}
|
||||
|
||||
/* --- Combined Stat Card & Charts --- */
|
||||
.stat-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 2rem;
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.stat-sub {
|
||||
font-size: 0.8rem;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.icon-box {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cyan-box {
|
||||
background: rgba(0, 243, 255, 0.1);
|
||||
color: var(--primary-cyan);
|
||||
}
|
||||
|
||||
.purple-box {
|
||||
background: rgba(188, 19, 254, 0.1);
|
||||
color: var(--secondary-purple);
|
||||
}
|
||||
|
||||
.chart-divider {
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
/* Re-using chart styles but refining for the box */
|
||||
.chart-container.small {
|
||||
height: 100px;
|
||||
margin-top: 0;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.chart-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chart-bar {
|
||||
width: 6px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 3px;
|
||||
transition: height 1s ease-out;
|
||||
}
|
||||
|
||||
.chart-bar.cyan {
|
||||
background: var(--primary-cyan);
|
||||
box-shadow: 0 0 5px rgba(0, 243, 255, 0.3);
|
||||
}
|
||||
|
||||
.chart-bar.purple {
|
||||
background: var(--secondary-purple);
|
||||
box-shadow: 0 0 5px rgba(188, 19, 254, 0.3);
|
||||
}
|
||||
|
||||
.chart-day {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* --- Calendar Module Styles --- */
|
||||
.calendar-wrapper {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
max-width: 350px;
|
||||
margin-top: 20px;
|
||||
border: 1px solid var(--glass-border);
|
||||
}
|
||||
|
||||
.calendar-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
font-family: var(--font-heading);
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.cal-nav-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
padding: 5px 10px;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.cal-nav-btn:hover {
|
||||
color: var(--primary-cyan);
|
||||
}
|
||||
|
||||
.calendar-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cal-day-label {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.7rem;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cal-day {
|
||||
aspect-ratio: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
font-size: 0.9rem;
|
||||
position: relative;
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
.cal-day.active {
|
||||
background: rgba(188, 19, 254, 0.2);
|
||||
color: #fff;
|
||||
box-shadow: 0 0 10px rgba(188, 19, 254, 0.2);
|
||||
border: 1px solid rgba(188, 19, 254, 0.4);
|
||||
}
|
||||
|
||||
.cal-day.start-date {
|
||||
background: rgba(0, 243, 255, 0.2);
|
||||
border-color: var(--primary-cyan);
|
||||
box-shadow: 0 0 10px rgba(0, 243, 255, 0.3);
|
||||
}
|
||||
|
||||
.cal-day.today::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* --- Modal Styles --- */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 90%;
|
||||
max-width: 400px;
|
||||
background: rgba(10, 10, 20, 0.95);
|
||||
/* Darker for readability */
|
||||
border: 1px solid var(--primary-cyan);
|
||||
box-shadow: 0 0 30px rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid var(--glass-border);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.icon-btn-small {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.setting-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.setting-group label {
|
||||
display: block;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.9rem;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.glow-input {
|
||||
width: 100%;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid var(--glass-border);
|
||||
color: #fff;
|
||||
padding: 15px;
|
||||
border-radius: 12px;
|
||||
font-size: 1.1rem;
|
||||
font-family: var(--font-heading);
|
||||
outline: none;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.glow-input:focus {
|
||||
border-color: var(--primary-cyan);
|
||||
box-shadow: 0 0 15px rgba(0, 243, 255, 0.2);
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Fitness Schedule Styles --- */
|
||||
.workout-card {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glow-card {
|
||||
box-shadow: 0 0 20px rgba(0, 243, 255, 0.1);
|
||||
border-color: rgba(0, 243, 255, 0.3);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.day-badge {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 2px;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.workout-type {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.8rem;
|
||||
line-height: 1.2;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.exercise-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.exercise-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.exercise-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.ex-name {
|
||||
font-size: 1rem;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.ex-meta {
|
||||
font-family: var(--font-heading);
|
||||
color: var(--primary-cyan);
|
||||
font-size: 0.9rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ex-note {
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.subsection-title {
|
||||
font-size: 1rem;
|
||||
color: var(--text-muted);
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.schedule-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.schedule-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.sch-day {
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.sch-type {
|
||||
font-weight: 600;
|
||||
}/ | ||||