# Project export: Therms - Thermoregulation Wearables with AI-powered Insights

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2025
- Tagline: A non-invasive wearable using novel thermoregulation research to provide precise heating/cooling to maintain core temperature. Built to enhance physical performance and battle geriatric disorders.
- Devpost: https://devpost.com/software/therms-thermoregulation-wearables-with-ai-powered-insights
- GitHub: https://github.com/arnavnc/therms-platform
- Demo: https://pubmed.ncbi.nlm.nih.gov/9804564/
- Video: https://www.youtube.com/embed/smeznyWVNhM?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Healthcare Grand Prize (Meta Ray Ban Smart Glasses x4) and [2nd place]; Scrapybara: Capy Prize (Japan offsite + $1k credits + Scrapybara Pro 6 months [1st] & $1k credits + Scrapybara Pro 3 months [2nd] & $100 credits + Scrapybara Pro 1 month [3rd]))
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

Our project is inspired by the fascinating thermoregulation research conducted by Professors Craig Heller and Dennis Grahn at Stanford University. Their work captivated us (papers linked below), and we had the opportunity to take Professor Heller’s class, where we directly experienced how temperature affects physical performance, health, and disease. For some scientific context, our body's glabrous skin (hairless areas rich in specialized blood vessels called arteriovenous anastomoses, or AVAs) plays a critical role in heat exchange. The soles of our feet and the palms (made of glabrous skin) of our hands act as thermal windows, regulating heat dissipation and conservation. When the body needs to cool down, AVAs dilate, increasing blood flow to the skin’s surface for heat loss. Conversely, when warmth is needed, AVAs constrict, reducing heat loss. In Professor Heller’s class, we were actually able to test this research by experimenting on ourselves. We pushed our aerobic and anaerobic limits with and without a cooling stimulus applied to our palms. The results were mind-boggling: we could do more reps and sets of strength exercises, and we nearly doubled our running time in a 100°F room with cooling applied. Experiencing this research firsthand fueled our interest in its potential as an incredibly useful healthcare-engineering project. However, the cooling setup we used in class had some serious drawbacks. It relied on a liquid cooling pump attached to tubes which were bulky, messy, and far from portable. Wearing large cooling gloves made using our hands impossible, which was impractical for real-world applications. Accordingly, we set out to solve this problem by shifting the cooling mechanism to the soles of the feet and designing a compact, self-contained system capable of both heating and cooling. Our goal was to create a wearable thermoregulation device that eliminates the need for a liquid cooling pump while maintaining efficiency, portability, and usability. We also saw an opportunity to integrate healthcare analytics, leveraging data from the device to provide valuable insights for both medical and performance-based applications. In short, we had to create a miniaturized HVAC system that seamlessly fits in the sole of a shoe without the bulk, without the mess, and without interfering with daily activities.

### What it does

Therms is a wearable thermoregulation device designed to actively regulate body temperature by targeting the glabrous skin on the soles of the feet. Therms uses thermoelectric Peltier modules to provide rapid, reversible heating and cooling. Our design uses a reversed-polarity model to switch the system between heating and cooling states. An embedded sensor continuously monitors foot temperature, ensuring the system responds in real time to thermal needs. AI-powered analyses of temperature data alongside health metrics (e.g., activity levels, medical conditions) help predict and preemptively adjust stimuli responses, optimizing comfort and performance. Therms also connects to APIs (Terra, Scrapybara, OpenAI) to incorporate additional physiological data, such as heart rate, sleep patterns, and chronic conditions. This enables personalized thermoregulation strategies and early detection of anomalies. For example, if a diabetic user’s foot temperature rises abnormally, Therms can cool the area to prevent tissue damage and AUTOMATICALLY book an appointment with a healthcare professional. Therms has medical applications, supporting individuals with thermoregulation dysfunction, such as those with spinal cord injuries, diabetes, or neurodegenerative disorders, by stabilizing foot temperature and preventing complications. Therms is also focused on performance-based applications to enhance recovery and endurance by optimizing thermal regulation during and after intense activity (soldiers, athletes, manual laborers). Finally, Therms has immense geriatric applications by protecting elderly users, who are particularly vulnerable to hypothermia and hyperthermia, with preemptive adjustments based on real-time data. Overall, Therms combines advanced thermoelectric technology with a deep understanding of human physiology to create a wearable system that actively supports thermal homeostasis. By targeting the glabrous skin on the feet, Therms achieves efficient, non-invasive thermoregulation while integrating AI-powered insights to improve health outcomes and performance.

### How we built it

After hours of whiteboard sessions and squabbling with one another, we designed a modular wearable system that efficiently manages heat transfer while leveraging AI-powered analytics for personalized thermal regulation. Miniaturizing an HVAC-like system into a shoe sole required replacing traditional liquid cooling with Peltier boards for precise heating and cooling. These boards were placed on top of a heat sink that were connected with a copper foil so that any heat accumulated on the Peltier after the current was turned off was dissipated away. A motor controller dynamically adjusts polarity and current flow to switch between heating and cooling states. A copper foil was also placed on the top of the frame to connect the Peltier boards and distribute heat transfer evenly among the surface area of the foot, while a temperature sensor was placed next to the top copper layer, but insulated from it, to provide real-time thermal feedback. A microcontroller coordinates these components and processes sensor data, while an IMU sensor tracks movement to provide additional activity insights. The entire system is housed in a modular sole frame (durability, extendability, and ease of integration into different footwear). The software ecosystem enables real-time data processing and automation. Firebase handles seamless communication between the device and the web application, while Terra API integrates external physiological data from other wearables. ChatGPT processes the integrated thermal and health data, identifying anomalies and optimizing thermoregulation strategies. Scrapybara automates tasks such as booking medical appointments and managing prescriptions based on detected trends. A Next.js frontend and Node.js backend ensure a responsive and scalable platform for users to monitor and personalize their Therms experience.

### Challenges we ran into

A major challenge came from Peltier technology, which can prove to be incredibly inefficient energy-wise and temperature-wise. When turned off, the boards retained and radiated heat on both sides, causing unwanted warming immediately after cooling. To counter this, we created an optimized heat sink system with our modular sole frame that captured as much heat as possible and dissipated it through a copper layer; this significantly reduced the risk of overheating. The modular design we keep talking about was one of the most challenging parts. We had to design a system that allowed for easy upgrades and customization and incorporate several different hardware modules in a very small space. After multiple iterations of our 3D models, we refined the design to maximize space efficiency while ensuring easy assembly and part replacement. On the software side, we built a pipeline incorporating three different data sources and three third-party APIs: shoe data + user data + Terra API activity data → OpenAI insights → Scrapybara automation. Our streamlined process looked great in theory but proved far more complex in execution. Ensuring data consistency across different technologies was a tedious task, with unexpected bugs that took considerable time and patience to debug.

### Accomplishments we're proud of

We are incredibly proud to have successfully created one of the first completely pump-less, battery-powered heating and cooling systems in a wearable format. Several reports said Peltier technology is inefficient and that using a liquid cooling/heating system that wasn’t mobile was the only way to effectively thermoregulate. We also had no design blueprints to reference, so it was exciting and also gratifying to see our hacky model come to life. Getting the entire design, code, and actual working device done within 36 hours was incredibly rewarding and helped us each hone and develop new skill sets that will serve us for life. Beyond hardware, we integrated incredibly varied technologies like Terra API, Scrapybara, and OpenAI and created a fully working web application portal that can pair shoes, collect user data, provide insights, and display professional data visuals. We are also proud of the research-first approach we took, where we used mathematical and biological models to quantify stimuli responses, healthcare insights, and have a solid grasp of why this technology is so important to human performance and health.

### What we learned

Throughout this project, we gained hands-on experience in developing scalable software and hardware. Our software required multiple APIs and a plethora of data being transmitted to different endpoints, which acted as a forcing function to make our application lightweight and scalable. We can expand the number of shoes, user metrics, wearables integrated through Terra, and much more without any latency using our Firebase + NextJS architecture. We refined our skills in CAD modeling and 3D printing, iterating through multiple designs to achieve a compact and efficient wearable. On the hardware side, we learned how to integrate super niche hardware modules, from thermoelectric modules to motor controllers, while balancing thermal physics, power efficiency, and real-world usability. Additionally, we explored IMU sensor modeling, developing a system that extracts motion data to enhance user tracking and inform advanced healthcare analytics. Finally, working with Terra API and Scrapybara taught us how to streamline data integration across multiple platforms, ensuring seamless communication between hardware, software, and AI-driven automation.

### What's next

We plan to refine and optimize Therms with the goal of commercializing it for users who need to condition/train heavily (athletes and soldiers) or users who have thermoregulation dysfunction symptoms. Our next steps include enhancing energy efficiency, ensuring that heating and cooling functions operate with minimal waste, and redesigning the form factor so the entire system fits nicely within the shoe. From this business and research perspective, we aim to scale Therms into a consumer-ready product that enhances human performance and recovery in athletes, workers, and everyday users by reducing fatigue and optimizing thermal regulation. Additionally, Therms has critical healthcare applications, particularly for senior citizens, providing a continuous, non-invasive way to monitor body temperature and mobility, reducing the risk of temperature-related health issues. By integrating advanced AI-driven insights, real-time health tracking, and an ergonomic design, we envision Therms becoming an essential, wearable thermoregulation solution for both performance enhancement and preventive healthcare.

## README (from the GitHub repository)

This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.


## Detected evidence (automated analysis)

Indexed codebase: 33 recognized source files, 153 KB.
- CSS (language) — detected in the code
- Firebase (technology) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (41 of 41)

```
.gitignore
demo.py
full_code.py
jsconfig.json
main.py
next.config.mjs
package.json
postcss.config.mjs
README.md
serviceAccountKey.json
src/app/api/execute-action/route.js
src/app/api/insights/route.js
src/app/components/ActionsWidget.js
src/app/components/DailySummaryWidget.js
src/app/components/Footer.js
src/app/components/HealthConditionsInput.js
src/app/components/InsightsWidget.js
src/app/components/Navbar.js
src/app/components/StimulusChart.js
src/app/components/TemperatureChart.js
src/app/components/TerraWidget.js
src/app/dashboard/page.js
src/app/globals.css
src/app/layout.js
src/app/lib/auth.js
src/app/lib/createShoeData.js
src/app/lib/data.js
src/app/lib/dataCollection.js
src/app/lib/firebase.js
src/app/lib/openaiPrompt.js
src/app/lib/terraData.js
src/app/lib/withAuth.js
src/app/login/page.js
src/app/page.js
src/app/pair/page.js
src/app/profile/page.js
src/app/signup/page.js
src/app/terra/page.js
src/app/test/page.js
tailwind.config.mjs
trained_model.h5
```

### Dependencies

- package.json: axios@^1.7.9, firebase@^11.3.1, next@15.1.7, openai@^4.85.1, playwright@^1.50.1, postcss@^8, react@^19.0.0, react-dom@^19.0.0, recharts@^2.15.1, scrapybara@^2.2.2, tailwindcss@^3.4.1, zod@^3.24.2

### Recent commits (newest first)

- Update README.md
- Merge branch 'main' of https://github.com/arnavnc/therms-platform
- works!!
- Add files via upload
- new changes
- Create route.js
- APIS WORK!!!
- terra half working - remaining string bug
- terra bug fixing
- testing w/ mock shoe data
- profile page and shoe pairing page
- minor style changes
- terraData mock data + openAI prompt
- Create hero.webp
- finished landing page v1
- added new npm packages
- update signup w/ columns
- Update page.js
- finished all widgets!!!!!!!!!!!!!!!
- openai route

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "therms-platform",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "axios": "^1.7.9",
    "firebase": "^11.3.1",
    "next": "15.1.7",
    "openai": "^4.85.1",
    "playwright": "^1.50.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "recharts": "^2.15.1",
    "scrapybara": "^2.2.2",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "postcss": "^8",
    "tailwindcss": "^3.4.1"
  }
}

```

### main.py

```python
import RPi.GPIO as GPIO
import smbus
import os
import glob
import time
import tensorflow
import numpy as np
from collections import deque

# Setup GPIO mode
GPIO.setmode(GPIO.BCM)

# Define the pin numbers
pin_23 = 23
pin_24 = 24

# Setup pins as output
GPIO.setup(pin_23, GPIO.OUT)
GPIO.setup(pin_24, GPIO.OUT)

# Initialize the I2C bus for MPU6050
bus = smbus.SMBus(1)

# MPU6050 Registers and their Addresses
MPU6050_ADDR = 0x68
PWR_MGMT_1 = 0x6B
ACCEL_XOUT_H = 0x3B
ACCEL_YOUT_H = 0x3D
ACCEL_ZOUT_H = 0x3F
GYRO_XOUT_H = 0x43
GYRO_YOUT_H = 0x45
GYRO_ZOUT_H = 0x47

# Wake up the MPU6050 from sleep mode
bus.write_byte_data(MPU6050_ADDR, PWR_MGMT_1, 0)

def read_word_2c(addr):
    high = bus.read_byte_data(MPU6050_ADDR, addr)
    low = bus.read_byte_data(MPU6050_ADDR, addr+1)
    val = (high << 8) + low
    if (val >= 0x8000):
        return -((65535 - val) + 1)
    else:
        return val

def read_accel_data():
    accel_x = read_word_2c(ACCEL_XOUT_H)
    accel_y = read_word_2c(ACCEL_YOUT_H)
    accel_z = read_word_2c(ACCEL_ZOUT_H)
    return accel_x, accel_y, accel_z

def read_gyro_data():
    gyro_x = read_word_2c(GYRO_XOUT_H)
    gyro_y = read_word_2c(GYRO_YOUT_H)
    gyro_z = read_word_2c(GYRO_ZOUT_H)
    return gyro_x, gyro_y, gyro_z

# Temperature sensor setup
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')

base_dir = '/sys/bus/w1/devices/'
device_folder = glob.glob(base_dir + '28*')[0]
device_file = device_folder + '/w1_slave'

def read_temp_raw():
    f = open(device_file, 'r')
    lines = f.readlines()
    f.close()
    return lines

def read_temp():
    lines = read_temp_raw()
    while lines[0].strip()[-3:] != 'YES':
        time.sleep(0.2)
        lines = read_temp_raw()
    equals_pos = lines[1].find('t=')
    if equals_pos != -1:
        temp_string = lines[1][equals_pos+2:]
        temp_c = float(temp_string) / 1000.0
        temp_f = temp_c * 9.0 / 5.0 + 32.0
        return temp_c, temp_f


try:

    imu_buffer = deque(maxlen=40) 
    model = tensorflow.keras.models.load_model('trained_model.h5')

    while True:
        # Reading temperature
        temp_c, temp_f = read_temp()
        
        # Reading IMU data
        accel_data = read_accel_data()
        gyro_data = read_gyro_data()
        
        # Printing data
        print(f"Temperature: {temp_c}°C / {temp_f}°F")
        print("Accelerometer data: X:{0}, Y:{1}, Z:{2}".format(*accel_data))
        print("Gyroscope data:     X:{0}, Y:{1}, Z:{2}\n".format(*gyro_data))
        
        # Control Peltier based on temperature and activity state
        if temp_f > 95 or state == 0:
            # Temp crosses 95°F or user engages in high activity, pass current one way
            GPIO.output(pin_23, GPIO.HIGH)
            GPIO.output(pin_24, GPIO.LOW)
        elif temp_f < 93:
            # Temp goes below 93°F, pass current in the other direction
            GPIO.output(pin_23, GPIO.LOW)
            GPIO.output(pin_24, GPIO.HIGH)
        else:
            # Temp is between 93°F and 95°F, turn off the Peltiers
            GPIO.output(pin_23, GPIO.LOW)
            GPIO.output(pin_24, GPIO.LOW)

        # Store the new data in the buffer
        imu_buffer.append((*accel_data, *gyro_data))  # Flatten the data tuple

        # Ensure enough data to run the model
        if len(imu_buffer) == 40:
            imu_array = np.array(imu_buffer) 
            imu_array = imu_array.reshape(1, 40, 6) 

            pred = model.predict(imu_array)
            y_pred_class = np.argmax(pred, axis=1)

            if y_pred_class in [0, 2]:
                state = 0 # running
            elif y_pred_class in [1, 4, 5]:
                state = 1 # walking
            else:
                state = 2 # standing
        
        time.sleep(0.05)  # Read data every 0.05 seconds
except KeyboardInterrupt:
    print("Program interrupted. Exiting...")
    # Clean up GPIO on Ctrl+C exit
    GPIO.cleanup()

# Clean up GPIO on normal exit
GPIO.cleanup()
```

### src/app/layout.js

```javascript
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({
  subsets: ["latin"],
  variable: "--font-inter",
});

export const metadata = {
  title: "Therms",
  description: "The all-new AI-powered thermoregulation wearable.",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body className={`${inter.variable} antialiased`} suppressHydrationWarning>
        {children}
      </body>
    </html>
  );
}

```

### src/app/page.js

```javascript
'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import Navbar from './components/Navbar';
import Footer from './components/Footer';
import { auth } from './lib/firebase';

const features = [
  {
    title: "Smart Temperature Control",
    description: "Automatically adjusts to your optimal foot temperature throughout the day",
    icon: (
      <svg className="w-8 h-8 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 16v3a2 2 0 002 2h10a2 2 0 002-2v-3M5 16l3-6M5 16h14M19 16l-3-6M12 3v7m0 0l-3-3m3 3l3-3" />
      </svg>
    )
  },
  {
    title: "Real-time Monitoring",
    description: "Track your foot temperature and comfort levels with detailed analytics",
    icon: (
      <svg className="w-8 h-8 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
      </svg>
    )
  },
  {
    title: "Long Battery Life",
    description: "Up to 12 hours of continuous temperature regulation on a single charge",
    icon: (
      <svg className="w-8 h-8 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
      </svg>
    )
  }
];

export default function Home() {
  const [currentFeature, setCurrentFeature] = useState(0);
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((user) => {
      setUser(user);
      setLoading(false);
    });

    return () => unsubscribe();
  }, []);

  const nextFeature = () => {
    setCurrentFeature((prev) => (prev + 1) % features.length);
  };

  const prevFeature = () => {
    setCurrentFeature((prev) => (prev - 1 + features.length) % features.length);
  };

  return (
    <div className="min-h-screen bg-white flex flex-col">
      <Navbar />
      
      <main className="flex-1 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 py-16">
          {/* Left Column */}
          <div className="flex flex-col justify-center space-y-8">
            <h1 className="text-6xl font-black">
              <span className="bg-gradient-to-r from-red-500 to-blue-500 inline-block text-transparent bg-clip-text">
                Therms.
              </span>
            </h1>
            <p className="text-2xl text-gray-600">
              The all-in-one thermoregulating wearable system
            </p>
            
            {/* Feature Carousel */}
            <div className="bg-gray-50 p-8 rounded-xl relative">
              <button 
                onClick={prevFeature}
                className="absolute left-5 top-1/2 -translate-y-1/2 p-2 rounded-full bg-white shadow-lg hover:bg-gray-100"
              >
                <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
                </svg>
              </button>
              
              <div className="flex gap-6 items-center px-8">
                <div className="p-4">
                  {features[currentFeature].icon}
                </div>
                <div className="flex-1">
                  <h3 className="text-xl font-semibold mb-2 text-gray-800">{features[currentFeature].title}</h3>
                  <p className="text-gray-600">{features[currentFeature].description}</p>
                </div>
              </div>

              <button 
                onClick={nextFeature}
                className="absolute right-5 top-1/2 -translate-y-1/2 p-2 rounded-full bg-white shadow-lg hover:bg-gray-50"
              >
                <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
                </svg>
              </button>
            </div>

            {/* Conditional CTA Buttons */}
            {!loading && (
              <div className="flex gap-4">
                {user ? (
                  <Link
                    href="/dashboard"
                    className="w-full bg-gradient-to-r from-blue-500 to-red-500 text-white px-6 py-3 rounded-lg font-medium hover:brightness-110 transition-all duration-200 text-center"
                  >
                    Go to Dashboard
                  </Link>
                ) : (
                  <>
                    <Link
                      href="/register"
                      className="flex-1 bg-gradient-to-r from-blue-500 to-red-500 text-white px-6 py-3 rounded-lg font-medium hover:brightness-110 transition-all duration-200 text-center"
                    >
                      Get started
                    </Link>
                    <Link
                      href="/login"
                      className="flex-1 bg-gray-100 text-gray-700 px-6 py-3 rounded-lg font-medium hover:bg-gray-200 transition-all duration-200 text-center"
                    >
                      Sign in
                    </Link>
                  </>
                )}
              </div>
            )}
          </div>

          {/* Right Column - Hero Image */}
          <div className="hidden lg:block bg-gray-100 rounded-xl">
            {/* Replace with your hero image */}
            <div className="w-full h-full min-h-[600px] relative">
              <Image
                src="/hero.webp"
                alt="Therms smart shoe"
                fill
   
[truncated — 167 more characters]
```

### src/app/terra/page.js

```javascript
import TerraWidget from "../components/TerraWidget"
import Navbar from "../components/Navbar"

export default function Terra() {
    return (
        <div>
            <Navbar />
            <TerraWidget />
        </div>
    )
}
```

### src/app/test/page.js

```javascript
'use client';
import { useState } from 'react';
import { createShoeDocument } from '../lib/createShoeData';
import Navbar from '../components/Navbar';

export default function TestPage() {
  const [status, setStatus] = useState('');
  const [loading, setLoading] = useState(false);
  const [shoeId, setShoeId] = useState('');

  const handleCreateTestShoe = async () => {
    if (!shoeId.trim()) {
      setStatus('Please enter a shoe ID');
      return;
    }

    setLoading(true);
    setStatus('Creating shoe...');

    try {
      const result = await createShoeDocument(shoeId);
      if (result.success) {
        setStatus('Success: Shoe created with test data!');
      } else {
        setStatus(`Error: ${result.message}`);
      }
    } catch (error) {
      console.error('Error:', error);
      setStatus(`Error: ${error.message}`);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="min-h-screen bg-gray-50">
      <Navbar />
      <div className="max-w-4xl mx-auto p-8">
        <div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
          <h1 className="text-2xl font-bold text-gray-800 mb-6">Test Data Creator</h1>
          
          <div className="space-y-4">
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2" htmlFor="shoeId">
                Shoe ID
              </label>
              <input
                type="text"
                id="shoeId"
                value={shoeId}
                onChange={(e) => setShoeId(e.target.value)}
                className="w-full p-3 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                placeholder="Enter shoe ID for test data"
              />
            </div>

            <button
              onClick={handleCreateTestShoe}
              disabled={loading}
              className="w-full bg-blue-500 text-white p-3 rounded-lg font-medium hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
            >
              {loading ? 'Creating...' : 'Create Test Shoe'}
            </button>

            {status && (
              <div className={`p-4 rounded-lg ${
                status.startsWith('Success') 
                  ? 'bg-green-50 text-green-700' 
                  : status.startsWith('Error') 
                    ? 'bg-red-50 text-red-700'
                    : 'bg-blue-50 text-blue-700'
              }`}>
                {status}
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
} 
```

### src/app/login/page.js

```javascript
'use client';
import { useState } from 'react';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from '../lib/firebase';
import { useRouter } from 'next/navigation';
import Link from 'next/link';

export default function Login() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const router = useRouter();

  const handleLogin = async (e) => {
    e.preventDefault();
    try {
      await signInWithEmailAndPassword(auth, email, password);
      router.push('/dashboard');
    } catch (err) {
      setError(err.message);
    }
  };

  return (
    <div className="min-h-screen flex items-center justify-center bg-gradient-to-r from-blue-500 to-red-600">
      <div className="bg-white p-8 rounded-xl shadow-lg w-96 border border-white/20">
        <div className="text-center mb-8">
          <h1 className="text-2xl font-semibold text-gray-800">Welcome Back</h1>
          <p className="mt-2 text-sm text-gray-600">Sign in to your account to continue</p>
        </div>
        {error && <p className="text-red-500 text-sm mb-4 bg-red-50 p-3 rounded-lg">{error}</p>}
        <form onSubmit={handleLogin} className="space-y-6">
          <div>
            <label className="block text-sm font-medium text-gray-700 mb-2" htmlFor="email">
              Email
            </label>
            <input
              type="email"
              id="email"
              className="text-black w-full p-3 border border-gray-200 rounded-lg bg-gray-50 focus:bg-white transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
              placeholder="Enter your email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />
          </div>
          <div>
            <label className="block text-sm font-medium text-gray-700 mb-2" htmlFor="password">
              Password
            </label>
            <input
              type="password"
              id="password"
              className="text-black w-full p-3 border border-gray-200 rounded-lg bg-gray-50 focus:bg-white transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
              placeholder="Enter your password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
          </div>
          <button
            type="submit"
            className="w-full bg-gradient-to-r from-blue-500 to-red-500 text-white p-3 rounded-lg font-medium hover:brightness-110 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-200"
          >
            Sign In
          </button>
        </form>
        <div className="mt-6 text-center">
          <Link href="/signup" className="text-sm text-blue-500 hover:brightness-50 bg-gradient-to-r from-red-500 to-blue-500 inline-block text-transparent bg-clip-text">
            Don't have an account? Sign up
          </Link>
        </div>
      </div>
    </div>
  );
}
```

### src/app/dashboard/page.js

```javascript
'use client';
import { useState, useEffect } from 'react';
import { auth, db } from '../lib/firebase';
import { doc, getDoc, updateDoc } from 'firebase/firestore';
import { useSearchParams } from 'next/navigation';
import Navbar from '../components/Navbar';
import TemperatureChart from '../components/TemperatureChart';
import StimulusChart from '../components/StimulusChart';
import InsightsWidget from '../components/InsightsWidget';
import ActionsWidget from '../components/ActionsWidget';
import { withAuth } from '../lib/withAuth';
import DailySummaryWidget from '../components/DailySummaryWidget';

function Dashboard() {
  const [userData, setUserData] = useState(null);
  const [selectedShoeId, setSelectedShoeId] = useState(null);
  const [loading, setLoading] = useState(true);
  const searchParams = useSearchParams();
  const [updateAttempted, setUpdateAttempted] = useState(false);

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged(async (user) => {
      if (user) {
        try {
          const userDoc = await getDoc(doc(db, 'users', user.uid));
          if (userDoc.exists()) {
            const data = userDoc.data();
            setUserData(data);
            // Set first shoe as default if available
            if (data.shoes && data.shoes.length > 0) {
              setSelectedShoeId(data.shoes[0].id);
            }
          }
        } catch (error) {
          console.error('Error fetching user data:', error);
        }
      }
      setLoading(false);
    });

    return () => unsubscribe();
  }, []);

  useEffect(() => {
    const handleTerraConnection = async () => {
      const terraSuccess = searchParams.get('terra_success');
      const terraUserId = searchParams.get('user_id');
      const sessionId = localStorage.getItem('terraSessionId');
      const referenceId = localStorage.getItem('terraReferenceId');

      console.log('Checking Terra connection...');
      console.log({
        terraSuccess,
        terraUserId,
        sessionId,
        referenceId,
        updateAttempted
      });

      if (terraSuccess === 'true' && terraUserId && sessionId && !updateAttempted) {
        try {
          console.log('Attempting to update Terra connection...');
          
          // Wait for auth to initialize
          await new Promise((resolve) => {
            const unsubscribe = auth.onAuthStateChanged((user) => {
              unsubscribe();
              resolve(user);
            });
          });

          const user = auth.currentUser;
          console.log('Current user:', user?.uid);

          if (user) {
            const userDocRef = doc(db, 'users', user.uid);
            await updateDoc(userDocRef, {
              terraId: terraUserId,
              terraConnected: true
            });
            console.log('Successfully updated Terra connection');
          }

          // Clean up
          localStorage.removeItem('terraSessionId');
          localStorage.removeItem('terraReferenceId');
          setUpdateAttempted(true);
        } catch (error) {
          console.error('Error updating Terra connection:', error);
        }
      }
    };

    handleTerraConnection();
  }, [searchParams, updateAttempted]);

  if (loading) {
    return (
      <div className="min-h-screen bg-gray-50">
        <Navbar />
        <div className="flex justify-center items-center h-[calc(100vh-4rem)]">
          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-gray-50">
      <Navbar />
      <div className="max-w-7xl mx-auto p-8">
        <div className="flex justify-between items-center mb-8">
          <h1 className="text-2xl font-bold text-gray-800">Dashboard</h1>
          {userData?.shoes?.length > 1 && (
            <select
              value={selectedShoeId}
              onChange={(e) => setSelectedShoeId(e.target.value)}
              className="bg-white border border-gray-200 rounded-lg px-4 py-2 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
            >
              {userData.shoes.map((shoe) => (
                <option key={shoe.id} value={shoe.id}>
                  {shoe.name}
                </option>
              ))}
            </select>
          )}
        </div>
        
        <div className="space-y-4">
          <div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
            <DailySummaryWidget shoeId={selectedShoeId} />
          </div>
          
          <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
            <div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
              <h2 className="text-lg font-semibold mb-4 text-gray-800">Foot Temperature Over Time</h2>
              <TemperatureChart shoeId={selectedShoeId} />
            </div>
            <div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
              <h2 className="text-lg font-semibold mb-4 text-gray-800">Heating/Cooling State Over Time</h2>
              <StimulusChart shoeId={selectedShoeId} />
            </div>
            <div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
              <h2 className="text-lg font-semibold mb-4 text-gray-800">Insights</h2>
              <InsightsWidget shoeId={selectedShoeId} />
            </div>
            <div className="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
              <div className="flex justify-between items-start">
                <h2 className="text-lg font-semibold text-gray-800 mb-4">Recommended Actions</h2>
                <div className="relative group">
                  <svg 
                    className="w-5 h-5 text-gray-400 cursor-help" 
                    fill="none" 
                    stroke="currentColor" 
                    viewBox="0 0 24 24" 
                    xmlns="http://www.w3.org/2000/svg"
                  >
      
[truncated — 944 more characters]
```

### src/app/signup/page.js

```javascript
'use client'; // Mark as Client Component
import { useState } from 'react';
import { createUserWithEmailAndPassword } from 'firebase/auth';
import { auth, db } from '../lib/firebase';
import { doc, setDoc } from 'firebase/firestore';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import HealthConditionsInput from '../components/HealthConditionsInput';

export default function Signup() {
  const [formData, setFormData] = useState({
    email: '',
    password: '',
    name: '',
    age: '',
    sex: '',
    healthConditions: [],
    insightPreference: 'health'
  });
  const [error, setError] = useState('');
  const router = useRouter();

  const handleChange = (e) => {
    setFormData({
      ...formData,
      [e.target.name]: e.target.value
    });
  };

  const handleSignup = async (e) => {
    e.preventDefault();
    try {
      const userCredential = await createUserWithEmailAndPassword(auth, formData.email, formData.password);
      const user = userCredential.user;
      
      await setDoc(doc(db, 'users', user.uid), {
        email: formData.email,
        name: formData.name,
        age: formData.age,
        height: '',
        weight: '',
        sex: formData.sex,
        terraId: 'placeholder',
        shoes: [],
        healthConditions: formData.healthConditions,
        insightPreference: formData.insightPreference // Added to user document
      });

      router.push('/dashboard');
    } catch (err) {
      setError(err.message);
    }
  };

  const handleSexChange = (e) => {
    const value = e.target.value.toUpperCase();
    if (value === '' || value === 'M' || value === 'F') {
      setFormData({
        ...formData,
        sex: value
      });
    }
  };

  return (
    <div className="min-h-screen flex items-center justify-center bg-gradient-to-r from-blue-500 to-red-600 p-6">
      <div className="bg-white p-8 rounded-xl shadow-lg max-w-4xl w-full border border-white/20">
        <div className="text-center mb-8">
          <h1 className="text-2xl font-semibold text-gray-800">Create Account</h1>
          <p className="mt-2 text-sm text-gray-600">Join us to get started with THERMS</p>
        </div>
        
        {error && <p className="text-red-500 text-sm mb-4 bg-red-50 p-3 rounded-lg">{error}</p>}
        
        <form onSubmit={handleSignup} className="grid grid-cols-2 gap-6">
          {/* Left Column */}
          <div className="space-y-6">
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2" htmlFor="name">
                Full Name
              </label>
              <input
                type="text"
                id="name"
                className="text-black w-full p-3 border border-gray-200 rounded-lg bg-gray-50 focus:bg-white transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                placeholder="Enter your full name"
                value={formData.name}
                onChange={handleChange}
                name="name"
                required
              />
            </div>

            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2" htmlFor="age">
                Age
              </label>
              <input
                type="number"
                id="age"
                min="0"
                max="120"
                className="text-black w-full p-3 border border-gray-200 rounded-lg bg-gray-50 focus:bg-white transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                placeholder="Enter your age"
                value={formData.age}
                onChange={handleChange}
                name="age"
                required
              />
            </div>

            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2" htmlFor="sex">
                Sex (M/F)
              </label>
              <input
                type="text"
                id="sex"
                maxLength="1"
                className="text-black w-full p-3 border border-gray-200 rounded-lg bg-gray-50 focus:bg-white transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                placeholder="M or F"
                value={formData.sex}
                onChange={handleSexChange}
                name="sex"
                required
              />
            </div>

            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2" htmlFor="email">
                Email
              </label>
              <input
                type="email"
                id="email"
                className="text-black w-full p-3 border border-gray-200 rounded-lg bg-gray-50 focus:bg-white transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                placeholder="Enter your email"
                value={formData.email}
                onChange={handleChange}
                name="email"
                required
              />
            </div>
          </div>

          {/* Right Column */}
          <div className="space-y-6">
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2" htmlFor="password">
                Password
              </label>
              <input
                type="password"
                id="password"
                className="text-black w-full p-3 border border-gray-200 rounded-lg bg-gray-50 focus:bg-white transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                placeholder="Enter your password"
                value={formData.password}
                onChange={handleChange}
                name="password"
                required
              />
            </div>

            <div>
              <la
[truncated — 1925 more characters]
```

### src/app/pair/page.js

```javascript
'use client';
import { useState, useEffect } from 'react';
import { auth, db } from '../lib/firebase';
import { doc, getDoc, updateDoc, arrayUnion } from 'firebase/firestore';
import { useRouter } from 'next/navigation';
import Navbar from '../components/Navbar';
import { withAuth } from '../lib/withAuth';

function Pair() {
  const [shoeId, setShoeId] = useState('');
  const [shoeName, setShoeName] = useState('');
  const [error, setError] = useState('');
  const [success, setSuccess] = useState('');
  const [isValidating, setIsValidating] = useState(false);
  const [foundShoe, setFoundShoe] = useState(null);
  const [userShoes, setUserShoes] = useState([]);
  const [loading, setLoading] = useState(true);
  const router = useRouter();

  useEffect(() => {
    const fetchUserShoes = async () => {
      const user = auth.currentUser;
      if (user) {
        const userDoc = await getDoc(doc(db, 'users', user.uid));
        if (userDoc.exists()) {
          const userData = userDoc.data();
          setUserShoes(userData.shoes || []);
        }
      }
      setLoading(false);
    };
    fetchUserShoes();
  }, []);

  const validateShoeId = async (e) => {
    e.preventDefault();
    setIsValidating(true);
    setError('');
    setSuccess('');
    setFoundShoe(null);

    // Check if user already has 2 shoes
    if (userShoes.length >= 2) {
      setError('You can only pair up to 2 shoes. Please unpair a shoe before adding a new one.');
      setIsValidating(false);
      return;
    }

    // Check if shoe is already paired
    if (userShoes.some(shoe => shoe.id === shoeId)) {
      setError('This shoe has already been paired to your account.');
      setIsValidating(false);
      return;
    }

    try {
      const shoeDoc = await getDoc(doc(db, 'shoes', shoeId));
      if (!shoeDoc.exists()) {
        setError('Invalid shoe ID. Please check and try again.');
        setIsValidating(false);
        return;
      }
      setFoundShoe(shoeDoc.data());
      setIsValidating(false);
    } catch (err) {
      setError('Error validating shoe ID. Please try again.');
      setIsValidating(false);
      console.error('Error:', err);
    }
  };

  const handlePairShoe = async (e) => {
    e.preventDefault();
    setError('');
    setSuccess('');

    if (!shoeName.trim()) {
      setError('Please enter a name for your shoe.');
      return;
    }

    try {
      const user = auth.currentUser;
      if (!user) {
        router.push('/login');
        return;
      }

      // Update user's shoes array in Firestore
      await updateDoc(doc(db, 'users', user.uid), {
        shoes: arrayUnion({
          id: shoeId,
          name: shoeName.trim()
        })
      });

      setSuccess('Shoe paired successfully!');
      setTimeout(() => {
        router.push('/dashboard');
      }, 2000);
    } catch (err) {
      setError('Error pairing shoe. Please try again.');
      console.error('Error:', err);
    }
  };

  if (loading) {
    return (
      <div className="min-h-screen bg-gradient-to-r from-blue-500 to-red-600">
        <Navbar />
        <div className="flex items-center justify-center min-h-[calc(100vh-4rem)]">
          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white"></div>
        </div>
      </div>
    );
  }

  if (userShoes.length >= 2) {
    return (
      <div className="min-h-screen bg-gradient-to-r from-blue-500 to-red-600">
        <Navbar />
        <div className="flex items-center justify-center min-h-[calc(100vh-4rem)]">
          <div className="bg-white p-8 rounded-xl shadow-lg w-96 border border-white/20">
            <div className="text-center">
              <h1 className="text-2xl font-semibold text-gray-800 mb-4">Maximum Shoes Paired</h1>
              <p className="text-gray-600 mb-6">You can only pair up to 2 shoes. Please unpair a shoe before adding a new one.</p>
              <a 
                href="/dashboard" 
                className="text-blue-500 hover:text-blue-600 font-medium"
              >
                Return to Dashboard
              </a>
            </div>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-gradient-to-r from-blue-500 to-red-600">
      <Navbar />
      <div className="flex items-center justify-center min-h-[calc(100vh-4rem)]">
        <div className="bg-white p-8 rounded-xl shadow-lg w-96 border border-white/20">
          <div className="mb-6">
            <h1 className="text-2xl font-semibold text-gray-800">Pair Your Shoe</h1>
            <p className="mt-2 text-sm text-gray-600">Enter your shoe's unique identifier to begin</p>
          </div>

          {error && (
            <div className="mb-4 p-3 bg-red-50 text-red-500 text-sm rounded-lg">
              {error}
            </div>
          )}

          {success && (
            <div className="mb-4 p-3 bg-green-50 text-green-500 text-sm rounded-lg">
              {success}
            </div>
          )}

          {!foundShoe ? (
            <form onSubmit={validateShoeId} className="space-y-4">
              <div>
                <label className="block text-sm font-medium text-gray-700 mb-2" htmlFor="shoeId">
                  Shoe ID
                </label>
                <input
                  type="text"
                  id="shoeId"
                  value={shoeId}
                  onChange={(e) => setShoeId(e.target.value)}
                  className="text-black w-full p-3 border border-gray-200 rounded-lg bg-gray-50 focus:bg-white transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                  placeholder="Enter your shoe ID"
                  required
                />
                <p className="mt-2 text-xs text-gray-500">You can find this ID on the bottom of your shoe</p>
              </div>
              <button
                type="submit"
                disabled={isValidating}
                cla
[truncated — 2291 more characters]
```

[23 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]