- +
+
-

- PostPyro -

-

- Async PostgreSQL Driver for Python, Built with Rust -

+ + + Beta — async rewrite, breaking changes expected before 1.0 + +

Async PostgreSQL for Python, built in Rust

- Built with PyO3 and sqlx, PostPyro is a fully async driver - every I/O method is - async def and releases the GIL while waiting on Postgres - with - DB-API 2.0-flavored exception handling. + PostPyro wraps sqlx in a PyO3/pyo3-asyncio binding. Every I/O method + is async def and releases the GIL while waiting on Postgres, parameters bind + natively — datetimes, UUIDs, Decimal, JSON, arrays, bytes — and errors surface + through a DB-API 2.0-flavored exception hierarchy.

-
-
- 🔥 - Rust Powered -
-
- ⚡ - High Performance -
-
- 🛡️ - Memory Safe -
-
- 📦 - Zero Dependencies -
+
+ + + Memory-safe Rust core + + + + Fully async, GIL released on I/O + + + + Zero Python dependencies +
- - - + + +
quick_example.py
@@ -102,24 +98,19 @@

import PostPyro async def main(): - # Connect to PostgreSQL (returns a Pool) pool = await PostPyro.connect("postgresql://user:pass@localhost/db") - # Query data - users = await pool.query("SELECT * FROM users WHERE active = $1", [True]) + users = await pool.query( + "SELECT * FROM users WHERE active = $1", [True] + ) for user in users: print(f"User: {user['name']} ({user['email']})") - # Execute updates - affected = await pool.execute( - "UPDATE users SET last_login = NOW() WHERE id = $1", - [user_id] - ) - - # Use transactions tx = await pool.transaction() async with tx: - await tx.execute("INSERT INTO orders (user_id) VALUES ($1)", [user_id]) + await tx.execute( + "INSERT INTO orders (user_id) VALUES ($1)", [user_id] + ) await tx.execute("UPDATE inventory SET stock = stock - 1") await pool.close() @@ -131,89 +122,93 @@

- -
+ +
-

Key Features

+
+

Key features

+

Everything the driver handles for you, out of the box.

+
-
🔥
-

Rust Core

-

- The driver itself is a PyO3 extension module - query execution and type - conversion happen in compiled Rust, not interpreted Python. -

+
+ +
+

Rust core

+

The driver is a PyO3 extension module — query execution and type conversion happen in compiled Rust, not interpreted Python.

-
🛡️
-

Memory Safe

-

- Rust's ownership system prevents memory leaks and segfaults. -

+
+ +
+

Memory safe

+

Rust's ownership system rules out the leaks and segfaults hand-rolled C bindings are prone to.

-
🌐
-

Broad Type Support

-

- Booleans, integers, floats, numeric, text, dates/times, UUIDs, and JSON/JSONB - convert automatically. See the Type System reference below for the full list. -

+
+ +
+

Broad type support

+

Booleans, numerics, text, dates/times, UUIDs, Decimal, JSON/JSONB, BYTEA, and arrays convert automatically — see the type reference.

-
⚡
-

Fully Async

-

- Every I/O method is async def, built on sqlx's connection pooling - and Tokio runtime, and releases the GIL while waiting on Postgres. -

+
+ +
+

Fully async

+

Every I/O method is async def, built on sqlx's pooling and the Tokio runtime, releasing the GIL while waiting on Postgres.

-
🔒
-

Type Conversion

-

- Postgres values decode to native Python types automatically. Bound parameters other than - bool/int/float/str/None need - an explicit ::type cast in the SQL text - see the Type System reference below. -

+
+ +
+

Native parameter binding

+

datetime, uuid.UUID, Decimal, dict, homogeneous list/tuple (as a real array), and bytes bind directly — no manual ::type cast in the SQL text.

-
🎯
-

DB-API 2.0 Compliant

-

- Standard Python database interface ensures compatibility with existing code. -

+
+ +
+

DB-API 2.0 exceptions

+

Standard exception hierarchy (IntegrityError, OperationalError, ...) mapped from Postgres SQLSTATE codes.

- -
+ +
-

Installation

-
-

⚠️ Pre-release

-

- This page documents the async 2.0.0-dev API (python/PostPyro/__init__.pyi - in the repo). It hasn't been published to PyPI yet - pip install PostPyro - currently installs the older, synchronous 1.0.0 driver, which does not have this API. - Build from source (below) to use the async driver described here. -

+
+

Installation

-
-
-

📦 Install from PyPI (1.0.0, sync - not this API)

+ +
+ +
+

Pre-release

+

This page documents the async 2.0.0-dev API (python/PostPyro/__init__.pyi in the repo). It hasn't shipped to PyPI yet — pip install PostPyro currently installs the older, synchronous 1.0.0 driver, which doesn't have this API. Build from source below to use the async driver described here.

+
+
+ +
+
+

+ + From PyPI (1.0.0, sync — not this API) +

pip install PostPyro
-
-
-

🔧 Build from Source

+
+

+ + Build from source (async, this API) +

# Install Rust
 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
@@ -222,20 +217,16 @@ 

🔧 Build from Source

git clone https://github.com/magi8101/PostPyro.git cd PostPyro pip install -e .
-
+

Requirements

    @@ -247,28 +238,26 @@

    Requirements

- -
+ +
-

Quick Start

-
-
- - - - -
- -
-
-
import asyncio
+                
+

Quick start

+
+
+ + + + +
+ +
+
import asyncio
 import PostPyro
 
 async def main():
-    # Connect to database (returns a Pool)
     pool = await PostPyro.connect("postgresql://user:password@localhost:5432/mydb")
 
-    # Create a table
     await pool.execute("""
         CREATE TABLE users (
             id SERIAL PRIMARY KEY,
@@ -278,840 +267,445 @@ 

Quick Start

) """) - # Insert data affected = await pool.execute( "INSERT INTO users (name, email, age) VALUES ($1, $2, $3)", ["Alice", "alice@example.com", 30] ) print(f"Inserted {affected} rows") - # Close the pool await pool.close() asyncio.run(main())
-
-
+
-
-
-
# Query multiple rows
+                
+
# Query multiple rows
 users = await pool.query("SELECT * FROM users WHERE age > $1", [25])
 for user in users:
     print(f"ID: {user['id']}, Name: {user['name']}, Age: {user['age']}")
 
-# Query single row (raises ProgrammingError on zero matches;
-# extra matches beyond the first are silently discarded)
+# Query a single row - raises ProgrammingError on zero matches;
+# extra matches beyond the first are silently discarded
 user = await pool.query_one("SELECT * FROM users WHERE id = $1", [1])
 print(f"Found user: {user['name']}")
 
 # There's no execute_batch() - run each statement individually
-names_and_ages = [("Bob", 25), ("Charlie", 35), ("Diana", 28)]
-inserted = 0
-for name, age in names_and_ages:
-    inserted += await pool.execute(
+for name, age in [("Bob", 25), ("Charlie", 35), ("Diana", 28)]:
+    await pool.execute(
         "INSERT INTO users (name, age) VALUES ($1, $2)", [name, age]
     )
-print(f"Inserted {inserted} rows")
 
-# Pool status check
 if not pool.is_closed():
-    print("✅ Pool is open")
-
-
+ print("Pool is open") +
-
-
-
# pool.transaction() is a real BEGIN round-trip, so it must be
+                
+
# pool.transaction() is a real BEGIN round-trip, so it must be
 # awaited before it can be used as a context manager -
 # `async with pool.transaction():` raises TypeError.
 tx = await pool.transaction()
 async with tx:
     await tx.execute("INSERT INTO users (name) VALUES ($1)", ["Alice"])
     await tx.execute("UPDATE accounts SET balance = balance + 100 WHERE user_id = $1", [1])
-    # Automatically commits on success, rolls back on exception
+    # Commits automatically on success, rolls back on exception
 
-# Manual transaction management (commit()/rollback() also
-# work outside a `with` block)
+# Manual transaction management also works outside a `with` block
 tx = await pool.transaction()
 try:
     await tx.execute("INSERT INTO orders (user_id, amount) VALUES ($1, $2)", [1, 99.99])
-    await tx.execute("UPDATE inventory SET stock = stock - 1 WHERE product_id = $1", [123])
     await tx.commit()
 except Exception as e:
     await tx.rollback()
-    print(f"Transaction failed: {e}")
-
-# Query within a transaction
-tx = await pool.transaction()
-async with tx:
-    users = await tx.query("SELECT * FROM users WHERE created_today = true")
-    for user in users:
-        await tx.execute("UPDATE users SET welcomed = true WHERE id = $1", [user['id']])
-
-
+ print(f"Transaction failed: {e}") +
-
-
-
# bind_params only auto-converts bool/int/float/str/None -
-# anything else (date, datetime, uuid, dict/list for JSON) has
-# to be passed as a string with an explicit Postgres cast.
-import json
+                
+
# Non-primitive types bind natively - no str() + ::type cast needed.
 import uuid
-from datetime import date, datetime
-
-await pool.execute("""
-    INSERT INTO complex_data (
-        json_col, uuid_col, date_col, timestamp_col
-    ) VALUES ($1::jsonb, $2::uuid, $3::date, $4::timestamp)
-""", [
-    json.dumps({"name": "John", "scores": [85, 92, 78]}),  # JSON
-    str(uuid.uuid4()),                                      # UUID
-    date.today().isoformat(),                               # Date
-    datetime.now().isoformat(),                             # Timestamp
-])
-
-# Reading back decodes to the right Python type automatically -
-# this direction IS automatic (see the Type System reference below).
-
-# Convert Row to dict for pandas integration
-import pandas as pd
-rows = await pool.query("SELECT * FROM users")
-df = pd.DataFrame([row.to_dict() for row in rows])
-
-
+from datetime import datetime, timezone +from decimal import Decimal + +await pool.execute( + "INSERT INTO events (at, uid, price, meta, blob) VALUES ($1,$2,$3,$4,$5)", + [ + datetime.now(timezone.utc), # -> TIMESTAMPTZ + uuid.uuid4(), # -> UUID + Decimal("9.99"), # -> NUMERIC, exact + {"ok": True, "tags": ["a", "b"]},# -> JSONB + b"\x00payload", # -> BYTEA + ], +) + +# A homogeneous list/tuple binds as a real Postgres array +await pool.execute( + "INSERT INTO tags (id, names, scores) VALUES ($1, $2, $3)", + [1, ["red", "blue"], [10, 20, None]], # None -> NULL array element +) + +# Reading back decodes to the right Python type automatically +row = await pool.query_one("SELECT * FROM events WHERE id = $1", [1]) +assert isinstance(row["at"], datetime)
- -
+ +
-

API Reference

-
-
+
+

API reference

+
+ +
-

Module Functions

- +

Module functions

-
- await PostPyro.connect(dsn, max_size=10, min_size=0) -
-

- Create a connection pool. This is the only way to get a Pool - - there's no synchronous constructor, since establishing the first connection - is itself async. -

+
await PostPyro.connect(dsn, max_size=10, min_size=0)
+

Create a connection pool. This is the only way to get a Pool — there's no synchronous constructor, since establishing the first connection is itself async.

-
Parameters:
+
Parameters
    -
  • dsn (str): PostgreSQL connection string
  • -
  • max_size (int, optional): maximum pool connections (default 10)
  • -
  • min_size (int, optional): minimum pool connections (default 0)
  • +
  • dsn (str) — PostgreSQL connection string
  • +
  • max_size (int, optional) — maximum pool connections (default 10)
  • +
  • min_size (int, optional) — minimum pool connections (default 0)
-
-
Returns:
-

Pool object

-
-
Example:
+
Example
pool = await PostPyro.connect("postgresql://user:pass@localhost:5432/mydb", max_size=20)
-
-
- PostPyro.__version__ / .apilevel / .threadsafety / .paramstyle -
-

Module-level constants. No get_version() function - read __version__ directly.

+
PostPyro.__version__ / .apilevel / .threadsafety / .paramstyle
+

Module-level constants. No get_version() function — read __version__ directly.

-
Example:
+
Example
print(PostPyro.__version__)   # driver version
-print(PostPyro.apilevel)      # "2.0" - DB-API 2.0-flavored
-print(PostPyro.paramstyle)    # "numeric" - uses $1, $2, ... parameters
+print(PostPyro.apilevel) # "2.0" +print(PostPyro.paramstyle) # "numeric" - $1, $2, ... placeholders
-
-

Pool Class

-

- Obtained via await PostPyro.connect(...). Pool and the - old ConnectionPool class were unified into this one type - there's - no separate Connection class to construct. -

+
+

Pool class

+

Obtained via await PostPyro.connect(...). Pool and the old ConnectionPool class were unified — there's no separate Connection class to construct.

-
- await pool.execute(query, params=None) -
-

Execute INSERT, UPDATE, DELETE, or DDL statements.

-
-
Parameters:
-
    -
  • query (str): SQL query string
  • -
  • params (list, optional): Query parameters using $1, $2, ... placeholders
  • -
-
-
-
Returns:
-

Number of rows affected (int)

-
+
await pool.execute(query, params=None)
+

Run INSERT, UPDATE, DELETE, or DDL. Returns the number of affected rows (int).

-
Examples:
-
# INSERT
-affected = await pool.execute("INSERT INTO users (name, age) VALUES ($1, $2)", ["Alice", 30])
-
-# UPDATE
-affected = await pool.execute("UPDATE users SET age = $1 WHERE name = $2", [31, "Alice"])
-
-# DELETE
-affected = await pool.execute("DELETE FROM users WHERE age < $1", [18])
+
Example
+
affected = await pool.execute("UPDATE users SET age = $1 WHERE name = $2", [31, "Alice"])
-
-
- await pool.query(query, params=None) -
-

Execute SELECT queries and return all matching rows.

-
-
Parameters:
-
    -
  • query (str): SQL SELECT statement
  • -
  • params (list, optional): Query parameters
  • -
-
-
-
Returns:
-

List of Row objects

-
+
await pool.query(query, params=None)
+

Run a SELECT and return all matching rows as a list of Row.

-
Example:
-
rows = await pool.query("SELECT id, name, age FROM users WHERE age > $1", [25])
-for row in rows:
-    print(f"ID: {row['id']}, Name: {row['name']}, Age: {row['age']}")
+
Example
+
rows = await pool.query("SELECT id, name FROM users WHERE age > $1", [25])
-
-
- await pool.query_one(query, params=None) -
-

Execute a SELECT and return its first row.

-
-
Parameters:
-
    -
  • query (str): SQL SELECT statement
  • -
  • params (list, optional): Query parameters
  • -
-
-
-
Returns:
-

Single Row object

-
-
-
Raises:
-

- ProgrammingError if zero rows are returned. If more than one - row matches, the extras are silently discarded rather than raising - only - use this when your query itself guarantees at most one row (a primary-key - lookup, LIMIT 1, etc.). -

-
+
await pool.query_one(query, params=None)
+

Run a SELECT and return its first row.

+
Raises

ProgrammingError on zero rows. Extra matches beyond the first are silently discarded — only use this when the query itself guarantees at most one row.

-
Example:
-
user = await pool.query_one("SELECT * FROM users WHERE id = $1", [1])
-print(f"User name: {user['name']}")
+
Example
+
user = await pool.query_one("SELECT * FROM users WHERE id = $1", [1])
-
-
- await pool.transaction() -
-

- Start a new transaction (a real BEGIN). Await it, then use the - result as an async with block - - async with pool.transaction(): raises TypeError - because the un-awaited coroutine has no __aenter__. -

-
-
Returns:
-

Transaction object (async context manager)

-
+
await pool.transaction()
+

Start a transaction (a real BEGIN). Await it, then use the result as async with — async with pool.transaction(): raises TypeError since the un-awaited coroutine has no __aenter__.

-
Example:
+
Example
tx = await pool.transaction()
 async with tx:
-    await tx.execute("INSERT INTO users (name) VALUES ($1)", ["Alice"])
-    await tx.execute("UPDATE accounts SET balance = balance - 100 WHERE user_id = $1", [1])
-    # Automatically commits on success, rolls back on exception
+ await tx.execute("INSERT INTO users (name) VALUES ($1)", ["Alice"])
-
-
- await pool.close() -
+
await pool.close()

Close the pool and free its connections.

-
-
Example:
-
await pool.close()
-
-
-
- pool.is_closed() -
-

Check whether the pool has been closed. Synchronous - no await.

-
-
Returns:
-

bool

-
-
-
Example:
-
if not pool.is_closed():
-    await pool.query("SELECT 1")
-
+
pool.is_closed()
+

Synchronous — no await. Returns bool.

-

Row Class

-

Represents a single row from a query result with dict-like interface.

- +

Row class

+

A single query result row with a dict-like interface.

-
- row[key] -
-

Access column values by index or name. Negative indices (row[-1]) work like a Python list.

+
row[key]
+

Access by column name or index. Negative indices work like a Python list.

-
Example:
+
Example
row = await pool.query_one("SELECT id, name, email FROM users WHERE id = $1", [1])
-
-# Access by column name
 print(row['name'])
-print(row['email'])
-
-# Access by index
 print(row[0])   # id
-print(row[1])   # name
 print(row[-1])  # last column
-
-
- row.to_dict() -
-

Convert row to a Python dictionary.

-
-
Returns:
-

Dictionary with column names as keys

-
+
row.to_dict() / .keys() / .values() / .items()
+

keys()/values()/items() return plain lists, not dict views. to_dict() gives a real dict for pandas/json.dumps.

-
Example:
-
user_dict = row.to_dict()
-print(user_dict)  # {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'}
+
Example
+
user_dict = row.to_dict()  # {'id': 1, 'name': 'Alice', ...}
-

Transaction Class

-

- Obtained via await pool.transaction(). Using a transaction after - it has been committed or rolled back raises ProgrammingError - rather than hanging or panicking. -

- -
-
- await tx.execute(query, params=None) -
-

Execute a statement within the transaction.

-
-
Example:
-
tx = await pool.transaction()
-async with tx:
-    await tx.execute("INSERT INTO users (name) VALUES ($1)", ["Alice"])
-    await tx.execute("UPDATE accounts SET balance = balance - $1 WHERE id = $2", [100, 1])
-
-
- +

Transaction class

+

Obtained via await pool.transaction(). Using it after commit/rollback raises ProgrammingError rather than hanging.

-
- await tx.query(query, params=None) / await tx.query_one(query, params=None) -
-

Execute a query within the transaction. Same semantics as pool.query/pool.query_one.

-
-
Example:
-
tx = await pool.transaction()
-async with tx:
-    users = await tx.query("SELECT * FROM users WHERE created_today = true")
-    for user in users:
-        await tx.execute("UPDATE users SET welcomed = true WHERE id = $1", [user['id']])
-
+
await tx.execute(...) / await tx.query(...) / await tx.query_one(...)
+

Same semantics as the Pool methods, scoped to the transaction.

-
-
- await tx.commit() / await tx.rollback() / tx.is_active() -
-

- Explicit commit/rollback - also work outside a with block. - is_active() is synchronous and returns False after - commit/rollback. -

-
-
Example:
-
tx = await pool.transaction()
-try:
-    await tx.execute("INSERT INTO orders (user_id) VALUES ($1)", [1])
-    await tx.commit()
-except Exception:
-    await tx.rollback()
-print(tx.is_active())  # False
-
+
await tx.commit() / await tx.rollback() / tx.is_active()
+

Explicit commit/rollback — also work outside a with block. is_active() is synchronous, False after commit/rollback.

-

Error Handling

-

PostPyro maps sqlx/Postgres errors onto a DB-API 2.0-flavored exception hierarchy, so you can catch by class instead of parsing SQLSTATE codes yourself.

- -
-

Exception Hierarchy

-
DatabaseError                    # Base database error
-├── InterfaceError              # Driver interface problems
-├── DataError                   # Data processing errors
-├── OperationalError            # Database operation errors
-├── IntegrityError              # Constraint violations
-├── InternalError               # Internal database errors
-├── ProgrammingError            # SQL programming errors
-└── NotSupportedError           # Unsupported operations
-
- +

Error handling

+

Postgres/sqlx errors map onto a DB-API 2.0-flavored hierarchy, so you catch by class instead of parsing SQLSTATE yourself.

+
DatabaseError # base +├── InterfaceError # driver interface problems +├── DataError # bad/unrepresentable data +├── OperationalError # connection / resource issues +├── IntegrityError # constraint violations +├── InternalError # internal database errors +├── ProgrammingError # SQL / usage errors +└── NotSupportedError # unsupported feature/type
-
Error Handling Example:
-
import PostPyro
-
-async def main():
-    pool = await PostPyro.connect("postgresql://user:pass@localhost/db")
-    try:
-        await pool.execute("INSERT INTO users (email) VALUES ($1)", ["invalid-email"])
-
-    except PostPyro.IntegrityError as e:
-        print(f"Constraint violation: {e}")
-
-    except PostPyro.OperationalError as e:
-        print(f"Database operation failed: {e}")
-
-    except PostPyro.ProgrammingError as e:
-        print(f"SQL syntax error: {e}")
-
-    except PostPyro.DatabaseError as e:
-        print(f"General database error: {e}")
+
Example
+
try:
+    await pool.execute("INSERT INTO users (email) VALUES ($1)", ["invalid-email"])
+except PostPyro.IntegrityError as e:
+    print(f"Constraint violation: {e}")
+except PostPyro.DatabaseError as e:
+    print(f"Database error: {e}")
-

Type System

-

PostPyro automatically converts between Python and PostgreSQL types.

- -
+

Type system

+

PostPyro converts between Python and PostgreSQL types automatically in both directions.

+
- - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + +
PostgreSQL TypePython TypeExample
PostgreSQL typePython typeExample
BOOLEANboolTrue, False
SMALLINT, INTEGER, BIGINTint42, -123, 9223372036854775807
REAL, DOUBLE PRECISIONfloat3.14, 2.718
NUMERICdecimal.DecimalDecimal('1234.5678')
TEXT, VARCHAR, CHAR, NAMEstr"Hello World"
DATEdatetime.datedate(2023, 12, 25)
TIMEdatetime.timetime(14, 30, 0)
TIMESTAMPdatetime.datetimedatetime(2023, 12, 25, 14, 30)
TIMESTAMPTZdatetime.datetimeWith timezone info (UTC)
UUIDstr'550e8400-e29b-...'
JSON, JSONBdict, list, etc.{"key": "value"}, [1, 2, 3]
BOOLEANboolTrue, False
SMALLINT, INTEGER, BIGINTint42, -123
REAL, DOUBLE PRECISIONfloat3.14
NUMERICdecimal.DecimalDecimal('1234.5678')
TEXT, VARCHAR, CHAR, NAMEstr"Hello"
DATEdatetime.datedate(2023, 12, 25)
TIMEdatetime.timetime(14, 30, 0)
TIMESTAMPdatetime.datetime (naive)datetime(2023, 12, 25, 14, 30)
TIMESTAMPTZdatetime.datetime (aware)With tzinfo, normalized to UTC
UUIDuuid.UUID in / str outuuid.uuid4()
JSON, JSONBdict, non-homogeneous list/tuple{"key": "value"}
BYTEAbytes (bytearray/memoryview in)b"\x00\x01"
BOOL[], INT2/4/8[], FLOAT4/8[], TEXT[]-familyhomogeneous list/tuple[1, 2, 3], ["a", "b"]
-

- Any other type (BYTEA, arrays, INET/CIDR, - custom/enum types, etc.) isn't decodable yet - reading such a column raises - PostPyro.NotSupportedError naming the type, rather than silently - returning the wrong value or None. -

+

Anything else (INET/CIDR, custom/enum types, nested arrays) isn't decodable yet — reading such a column raises PostPyro.NotSupportedError naming the type, rather than silently returning the wrong value.

- -
+ +
-

Examples

+
+

Examples

+
-

🐼 Pandas Integration

-
-
import asyncio
-import pandas as pd
-import PostPyro
-
-async def main():
-    pool = await PostPyro.connect("postgresql://user:pass@localhost/db")
-
-    # Query to DataFrame
-    rows = await pool.query("SELECT * FROM sales_data")
-    df = pd.DataFrame([row.to_dict() for row in rows])
-
-    print(df.head())
-    await pool.close()
-
-asyncio.run(main())
-
+

+ + Pandas integration +

+
rows = await pool.query("SELECT * FROM sales_data")
+df = pd.DataFrame([row.to_dict() for row in rows])
+print(df.head())
-
-

🚀 FastAPI Integration

-
-
from fastapi import FastAPI, HTTPException
-import PostPyro
-
-app = FastAPI()
-
-@app.on_event("startup")
+                        

+ + FastAPI integration +

+
@app.on_event("startup")
 async def startup():
-    app.state.pool = await PostPyro.connect("postgresql://user:pass@localhost/db")
+    app.state.pool = await PostPyro.connect(DSN)
 
 @app.get("/users/{user_id}")
 async def get_user(user_id: int):
     try:
         user = await app.state.pool.query_one(
-            "SELECT id, name, email FROM users WHERE id = $1",
-            [user_id]
+            "SELECT id, name, email FROM users WHERE id = $1", [user_id]
         )
         return user.to_dict()
     except PostPyro.DatabaseError:
-        raise HTTPException(status_code=404, detail="User not found")
-
+ raise HTTPException(404, "User not found")
-
-

🔧 Pooling Is Built In

-
-
import asyncio
-import PostPyro
-
-async def main():
-    # Pool is already a connection pool - no wrapper class needed.
-    # max_size/min_size configure how many connections it keeps.
-    pool = await PostPyro.connect(
-        "postgresql://user:pass@localhost/db",
-        max_size=20,
-        min_size=2,
-    )
-
-    # Concurrent queries share the pool automatically
-    results = await asyncio.gather(
-        pool.query("SELECT * FROM users WHERE id = $1", [1]),
-        pool.query("SELECT * FROM users WHERE id = $1", [2]),
-    )
-
-    if not pool.is_closed():
-        await pool.close()
-
-asyncio.run(main())
-
+

+ + Pooling is built in +

+
pool = await PostPyro.connect(DSN, max_size=20, min_size=2)
+
+results = await asyncio.gather(
+    pool.query("SELECT * FROM users WHERE id = $1", [1]),
+    pool.query("SELECT * FROM users WHERE id = $1", [2]),
+)
-
-

💾 Batch Processing

-
-
async def bulk_insert_users(pool, users_data):
-    # Single transaction - all rows commit or roll back together
-    tx = await pool.transaction()
-    async with tx:
-        for user_data in users_data:
-            await tx.execute(
-                "INSERT INTO users (name, email, age) VALUES ($1, $2, $3)",
-                [user_data['name'], user_data['email'], user_data['age']]
-            )
-
-    # There's no execute_batch() - looping awaited execute() calls
-    # is the working pattern (still parameterized, no string formatting)
-    for user_data in users_data:
-        await pool.execute(
+                        

+ + Batch processing +

+
tx = await pool.transaction()
+async with tx:
+    for row in users_data:
+        await tx.execute(
             "INSERT INTO users (name, email, age) VALUES ($1, $2, $3)",
-            [user_data['name'], user_data['email'], user_data['age']]
-        )
-
+ [row['name'], row['email'], row['age']] + ) + # All rows commit or roll back together
- -
+ +
-

Performance

-
-
-

Why PostPyro is Faster

-
-
-
🦀
-
-

Rust Backend

-

Native performance without Python interpreter overhead

-
-
-
-
⚡
-
-

Zero-Copy Operations

-

Direct memory mapping between PostgreSQL and Python

-
-
-
-
🌊
-
-

Async I/O

-

sqlx + Tokio-powered async networking, releasing the GIL while waiting on Postgres

-
-
-
-
📦
-
-

No Dependencies

-

No external Python dependencies

-
-
-
-
+
+

Performance

+
-
-

Driver Comparison

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FeaturePostPyropsycopg2asyncpgpsycopg3
LanguageRust + PythonC + PythonCythonC + Python
Performance⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Memory Safety✅ Rust❌ Manual C⚠️ Cython❌ Manual C
Installation📦 Wheel🔧 Compilation📦 Wheel🔧 Compilation
Dependencies🎯 Zero📦 Many📦 Few📦 Many
Async✅ Fully Async❌ Sync✅ Fully Async⚠️ Sync or Async
-
+
+
+
+

Rust backend

+

Native performance without Python interpreter overhead.

- -
-

Performance Benchmark

-
-
import asyncio
-import time
-import PostPyro
-
-async def main():
-    pool = await PostPyro.connect("postgresql://user:pass@localhost/db")
-
-    # Benchmark: Insert 10,000 records concurrently across the pool
-    start_time = time.time()
-
-    await asyncio.gather(*(
-        pool.execute("INSERT INTO benchmark (value) VALUES ($1)", [i])
-        for i in range(10000)
-    ))
-
-    elapsed = time.time() - start_time
-    print(f"Inserted 10,000 records in {elapsed:.2f} seconds")
-    print(f"Rate: {10000/elapsed:.0f} inserts/second")
-
-    await pool.close()
-
-asyncio.run(main())
-
+
+
+

Async I/O

+

sqlx + Tokio-powered networking, GIL released while waiting on Postgres.

+
+
+

Binary protocol

+

Direct wire-format encode/decode instead of text parsing round trips.

+
+
+
+

Zero dependencies

+

No external Python packages required at runtime.

+
+
+ +

Driver comparison

+
+ + + + + + + + + +
FeaturePostPyropsycopg2asyncpgpsycopg3
LanguageRust + PythonC + PythonCythonC + Python
Memory safety Rust Manual C— Cython Manual C
InstallationWheelCompilationWheelCompilation
DependenciesZeroSeveralFewSeveral
Async Fully async Sync Fully async— Sync or async
+ +
# Insert 10,000 records concurrently across the pool
+start = time.time()
+await asyncio.gather(*(
+    pool.execute("INSERT INTO benchmark (value) VALUES ($1)", [i])
+    for i in range(10000)
+))
+elapsed = time.time() - start
+print(f"Inserted 10,000 records in {elapsed:.2f}s ({10000/elapsed:.0f}/s)")
- + + - \ No newline at end of file + diff --git a/docs/script.js b/docs/script.js index bcdea90..b508711 100644 --- a/docs/script.js +++ b/docs/script.js @@ -1,447 +1,205 @@ -// Navigation and Mobile Menu -document.addEventListener('DOMContentLoaded', function() { +// Mobile menu, nav-link scroll spy, quick-start tabs, copy-to-clipboard, +// API sidebar scroll spy, scroll-to-top, and a basic in-page text search. +document.addEventListener('DOMContentLoaded', function () { // Mobile hamburger menu const hamburger = document.querySelector('.hamburger'); const navMenu = document.querySelector('.nav-menu'); - + if (hamburger && navMenu) { - hamburger.addEventListener('click', function() { + hamburger.addEventListener('click', function () { navMenu.classList.toggle('active'); hamburger.classList.toggle('active'); }); } - - // Smooth scrolling for navigation links + + // Smooth scrolling + active state for top nav links const navLinks = document.querySelectorAll('.nav-link[href^="#"]'); - navLinks.forEach(link => { - link.addEventListener('click', function(e) { + + function setActiveNavLink(targetId) { + navLinks.forEach(function (link) { + link.classList.toggle('active', link.getAttribute('href') === targetId); + }); + } + + navLinks.forEach(function (link) { + link.addEventListener('click', function (e) { e.preventDefault(); const targetId = this.getAttribute('href'); const targetSection = document.querySelector(targetId); - - if (targetSection) { - const offsetTop = targetSection.offsetTop - 80; // Account for fixed navbar - window.scrollTo({ - top: offsetTop, - behavior: 'smooth' - }); - - // Close mobile menu if open - navMenu.classList.remove('active'); - hamburger.classList.remove('active'); - - // Update active nav link - updateActiveNavLink(targetId); - } - }); - }); - - // Update active navigation link on scroll - function updateActiveNavLink(targetId) { - navLinks.forEach(link => { - link.classList.remove('active'); - if (link.getAttribute('href') === targetId) { - link.classList.add('active'); - } - }); - } - - // Intersection Observer for nav link highlighting - const sections = document.querySelectorAll('section[id]'); - const observerOptions = { - rootMargin: '-80px 0px -50% 0px', - threshold: 0 - }; - - const observer = new IntersectionObserver(function(entries) { - entries.forEach(entry => { - if (entry.isIntersecting) { - const id = '#' + entry.target.id; - updateActiveNavLink(id); - } + if (!targetSection) return; + + window.scrollTo({ + top: targetSection.offsetTop - 72, + behavior: 'smooth', + }); + + if (navMenu) navMenu.classList.remove('active'); + if (hamburger) hamburger.classList.remove('active'); + setActiveNavLink(targetId); }); - }, observerOptions); - - sections.forEach(section => { - observer.observe(section); }); -}); -// Tab functionality for Quick Start section -function showTab(tabName) { - // Hide all tab content - const tabContents = document.querySelectorAll('.tab-content'); - tabContents.forEach(content => { - content.classList.remove('active'); + const sectionObserver = new IntersectionObserver( + function (entries) { + entries.forEach(function (entry) { + if (entry.isIntersecting) setActiveNavLink('#' + entry.target.id); + }); + }, + { rootMargin: '-80px 0px -50% 0px', threshold: 0 } + ); + document.querySelectorAll('section[id]').forEach(function (section) { + sectionObserver.observe(section); }); - - // Remove active class from all tab buttons - const tabBtns = document.querySelectorAll('.tab-btn'); - tabBtns.forEach(btn => { - btn.classList.remove('active'); + + // Quick-start tabs + const tabButtons = document.querySelectorAll('.tab-btn'); + tabButtons.forEach(function (btn) { + btn.addEventListener('click', function () { + const tabName = btn.getAttribute('data-tab'); + + document.querySelectorAll('.tab-content').forEach(function (content) { + content.classList.remove('active'); + }); + tabButtons.forEach(function (b) { + b.classList.remove('active'); + }); + + const selected = document.getElementById('tab-' + tabName); + if (selected) selected.classList.add('active'); + btn.classList.add('active'); + + if (typeof Prism !== 'undefined') Prism.highlightAll(); + }); }); - - // Show selected tab content - const selectedTab = document.getElementById(`tab-${tabName}`); - if (selectedTab) { - selectedTab.classList.add('active'); - } - - // Add active class to clicked button - event.target.classList.add('active'); - - // Re-highlight code syntax - if (typeof Prism !== 'undefined') { - Prism.highlightAll(); - } -} -// Copy to clipboard functionality -function copyToClipboard(text) { - if (navigator.clipboard && window.isSecureContext) { - // Modern approach - navigator.clipboard.writeText(text).then(() => { - showCopyFeedback(event.target); - }).catch(err => { - console.error('Failed to copy text: ', err); - fallbackCopyTextToClipboard(text); + // Copy-to-clipboard buttons + document.querySelectorAll('.copy-btn').forEach(function (btn) { + const text = btn.getAttribute('data-copy') || ''; + btn.addEventListener('click', function () { + const done = function () { + const original = btn.innerHTML; + btn.innerHTML = + ''; + btn.style.color = 'var(--success)'; + setTimeout(function () { + btn.innerHTML = original; + btn.style.color = ''; + }, 1600); + }; + + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(text).then(done).catch(function () { + fallbackCopy(text, done); + }); + } else { + fallbackCopy(text, done); + } }); - } else { - // Fallback for older browsers - fallbackCopyTextToClipboard(text); - } -} + }); -function fallbackCopyTextToClipboard(text) { - const textArea = document.createElement('textarea'); - textArea.value = text; - textArea.style.position = 'fixed'; - textArea.style.left = '-999999px'; - textArea.style.top = '-999999px'; - document.body.appendChild(textArea); - textArea.focus(); - textArea.select(); - - try { - const successful = document.execCommand('copy'); - if (successful) { - showCopyFeedback(event.target); + function fallbackCopy(text, done) { + const textarea = document.createElement('textarea'); + textarea.value = text; + textarea.style.position = 'fixed'; + textarea.style.left = '-9999px'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + done(); + } catch (err) { + console.error('Copy failed:', err); } - } catch (err) { - console.error('Fallback: Oops, unable to copy', err); + document.body.removeChild(textarea); } - - document.body.removeChild(textArea); -} -function showCopyFeedback(button) { - const originalHTML = button.innerHTML; - button.innerHTML = ` - - - - `; - button.style.color = '#06d6a0'; - - setTimeout(() => { - button.innerHTML = originalHTML; - button.style.color = ''; - }, 2000); -} - -// API Reference navigation -document.addEventListener('DOMContentLoaded', function() { + // API reference sidebar const apiLinks = document.querySelectorAll('.api-link'); - const apiSections = document.querySelectorAll('.api-section'); - - // Smooth scrolling for API navigation - apiLinks.forEach(link => { - link.addEventListener('click', function(e) { + apiLinks.forEach(function (link) { + link.addEventListener('click', function (e) { e.preventDefault(); const targetId = this.getAttribute('href'); const targetSection = document.querySelector(targetId); - - if (targetSection) { - const offsetTop = targetSection.offsetTop - 100; - window.scrollTo({ - top: offsetTop, - behavior: 'smooth' - }); - - // Update active API link - apiLinks.forEach(l => l.classList.remove('active')); - this.classList.add('active'); - } + if (!targetSection) return; + + window.scrollTo({ top: targetSection.offsetTop - 88, behavior: 'smooth' }); + apiLinks.forEach(function (l) { + l.classList.remove('active'); + }); + this.classList.add('active'); }); }); - - // Intersection Observer for API sections - const apiObserverOptions = { - rootMargin: '-100px 0px -50% 0px', - threshold: 0 - }; - - const apiObserver = new IntersectionObserver(function(entries) { - entries.forEach(entry => { - if (entry.isIntersecting) { + + const apiObserver = new IntersectionObserver( + function (entries) { + entries.forEach(function (entry) { + if (!entry.isIntersecting) return; const id = '#' + entry.target.id; - apiLinks.forEach(link => { - link.classList.remove('active'); - if (link.getAttribute('href') === id) { - link.classList.add('active'); - } + apiLinks.forEach(function (link) { + link.classList.toggle('active', link.getAttribute('href') === id); }); - } - }); - }, apiObserverOptions); - - apiSections.forEach(section => { + }); + }, + { rootMargin: '-100px 0px -50% 0px', threshold: 0 } + ); + document.querySelectorAll('.api-section').forEach(function (section) { apiObserver.observe(section); }); -}); -// Scroll to top functionality -function createScrollToTop() { - const scrollToTopBtn = document.createElement('button'); - scrollToTopBtn.innerHTML = '↑'; - scrollToTopBtn.className = 'scroll-to-top'; - scrollToTopBtn.style.cssText = ` - position: fixed; - bottom: 20px; - right: 20px; - width: 50px; - height: 50px; - border-radius: 50%; - background: var(--primary); - color: white; - border: none; - font-size: 20px; - cursor: pointer; - opacity: 0; - visibility: hidden; - transition: all 0.3s ease; - z-index: 1000; - box-shadow: var(--shadow-lg); - `; - - document.body.appendChild(scrollToTopBtn); - - // Show/hide scroll to top button - window.addEventListener('scroll', function() { - if (window.pageYOffset > 300) { - scrollToTopBtn.style.opacity = '1'; - scrollToTopBtn.style.visibility = 'visible'; - } else { - scrollToTopBtn.style.opacity = '0'; - scrollToTopBtn.style.visibility = 'hidden'; - } - }); - - // Scroll to top on click - scrollToTopBtn.addEventListener('click', function() { - window.scrollTo({ - top: 0, - behavior: 'smooth' + // Scroll-to-top button + const scrollTopBtn = document.getElementById('scroll-top'); + if (scrollTopBtn) { + window.addEventListener('scroll', function () { + scrollTopBtn.classList.toggle('visible', window.pageYOffset > 400); }); - }); -} + scrollTopBtn.addEventListener('click', function () { + window.scrollTo({ top: 0, behavior: 'smooth' }); + }); + } -// Initialize scroll to top -document.addEventListener('DOMContentLoaded', createScrollToTop); + // Basic in-page text search (highlights matches, jumps to the first one) + const searchInput = document.getElementById('doc-search'); + if (searchInput) { + let highlighted = []; -// Navbar background on scroll -document.addEventListener('DOMContentLoaded', function() { - const navbar = document.querySelector('.navbar'); - - window.addEventListener('scroll', function() { - if (window.scrollY > 50) { - navbar.style.background = 'rgba(15, 15, 35, 0.98)'; - navbar.style.backdropFilter = 'blur(15px)'; - } else { - navbar.style.background = 'rgba(15, 15, 35, 0.95)'; - navbar.style.backdropFilter = 'blur(10px)'; + function clearHighlights() { + highlighted.forEach(function (el) { + el.classList.remove('search-hit'); + }); + highlighted = []; } - }); -}); -// Animate elements on scroll -function animateOnScroll() { - const elements = document.querySelectorAll('.feature-card, .example-card, .reason'); - - const animationObserver = new IntersectionObserver(function(entries) { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.style.opacity = '1'; - entry.target.style.transform = 'translateY(0)'; - } + searchInput.addEventListener('input', function (e) { + clearHighlights(); + const term = e.target.value.trim().toLowerCase(); + if (term.length < 2) return; + + const candidates = document.querySelectorAll('main h1, main h2, main h3, main p, main code'); + let scrolled = false; + candidates.forEach(function (el) { + if (el.textContent.toLowerCase().includes(term)) { + el.classList.add('search-hit'); + highlighted.push(el); + if (!scrolled) { + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + scrolled = true; + } + } + }); }); - }, { - threshold: 0.1, - rootMargin: '0px 0px -50px 0px' - }); - - elements.forEach(element => { - element.style.opacity = '0'; - element.style.transform = 'translateY(20px)'; - element.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; - animationObserver.observe(element); - }); -} - -// Initialize animations -document.addEventListener('DOMContentLoaded', animateOnScroll); -// Search functionality (basic) -function initSearch() { - const searchInput = document.createElement('input'); - searchInput.type = 'text'; - searchInput.placeholder = 'Search documentation...'; - searchInput.className = 'search-input'; - searchInput.style.cssText = ` - background: var(--bg-secondary); - border: 1px solid var(--border-color); - color: var(--text-primary); - padding: var(--space-sm) var(--space-md); - border-radius: var(--radius-md); - font-family: inherit; - font-size: 0.875rem; - width: 200px; - transition: var(--transition); - `; - - // Add search to navigation (if space allows) - const navMenu = document.querySelector('.nav-menu'); - if (navMenu && window.innerWidth > 1024) { - const searchContainer = document.createElement('li'); - searchContainer.appendChild(searchInput); - navMenu.insertBefore(searchContainer, navMenu.lastElementChild); - } - - // Simple search functionality - searchInput.addEventListener('input', function(e) { - const searchTerm = e.target.value.toLowerCase().trim(); - - if (searchTerm.length < 2) { - clearSearchHighlights(); - return; - } - - // Simple text search in content - const contentElements = document.querySelectorAll('h1, h2, h3, h4, h5, h6, p, code'); - let found = false; - - contentElements.forEach(element => { - const text = element.textContent.toLowerCase(); - if (text.includes(searchTerm)) { - element.style.background = 'rgba(255, 107, 53, 0.2)'; - element.style.borderRadius = '4px'; - element.style.padding = '2px 4px'; - - if (!found) { - element.scrollIntoView({ behavior: 'smooth', block: 'center' }); - found = true; - } + document.addEventListener('keydown', function (e) { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault(); + searchInput.focus(); + } + if (e.key === 'Escape' && document.activeElement === searchInput) { + searchInput.blur(); + searchInput.value = ''; + clearHighlights(); } }); - }); - - function clearSearchHighlights() { - const highlightedElements = document.querySelectorAll('[style*="rgba(255, 107, 53, 0.2)"]'); - highlightedElements.forEach(element => { - element.style.background = ''; - element.style.borderRadius = ''; - element.style.padding = ''; - }); - } - - // Clear highlights when search is empty - searchInput.addEventListener('blur', function() { - if (!this.value.trim()) { - clearSearchHighlights(); - } - }); -} - -// Initialize search on larger screens -document.addEventListener('DOMContentLoaded', function() { - if (window.innerWidth > 1024) { - initSearch(); - } -}); - -// Performance monitoring (basic) -document.addEventListener('DOMContentLoaded', function() { - // Log page load performance - window.addEventListener('load', function() { - if (typeof performance !== 'undefined' && performance.timing) { - const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart; - console.log(`PostPyro Docs loaded in ${loadTime}ms`); - } - }); -}); - -// Theme toggle (future enhancement placeholder) -function initThemeToggle() { - // Placeholder for future dark/light theme toggle - // Currently using dark theme by default -} - -// Keyboard shortcuts -document.addEventListener('keydown', function(e) { - // Cmd/Ctrl + K to focus search - if ((e.metaKey || e.ctrlKey) && e.key === 'k') { - e.preventDefault(); - const searchInput = document.querySelector('.search-input'); - if (searchInput) { - searchInput.focus(); - } } - - // Escape to clear search highlights - if (e.key === 'Escape') { - const searchInput = document.querySelector('.search-input'); - if (searchInput && document.activeElement === searchInput) { - searchInput.blur(); - } - } -}); - -// Error handling for missing elements -function safeQuerySelector(selector, callback) { - const element = document.querySelector(selector); - if (element && typeof callback === 'function') { - callback(element); - } -} -// Initialize all functionality when DOM is ready -document.addEventListener('DOMContentLoaded', function() { - console.log('PostPyro Documentation loaded successfully! 🔥'); - - // Re-highlight all code blocks - if (typeof Prism !== 'undefined') { - Prism.highlightAll(); - } + if (typeof Prism !== 'undefined') Prism.highlightAll(); }); - -// Service Worker registration (for offline support - future enhancement) -if ('serviceWorker' in navigator) { - window.addEventListener('load', function() { - // navigator.registerServiceWorker('/sw.js') - Future enhancement - }); -} - -// Analytics placeholder (if needed in future) -function initAnalytics() { - // Placeholder for analytics tracking - // Track page views, section visits, etc. -} - -// Export functions for testing (if needed) -if (typeof module !== 'undefined' && module.exports) { - module.exports = { - showTab, - copyToClipboard, - showCopyFeedback - }; -} \ No newline at end of file diff --git a/docs/styles.css b/docs/styles.css index 70f73f9..9f64057 100644 --- a/docs/styles.css +++ b/docs/styles.css @@ -1,4 +1,9 @@ -/* Reset and Base Styles */ +/* ========================================================================== + PostPyro Docs — design tokens + Dev-tool dark theme: near-black surfaces, one accent, flat borders + instead of gradients/glow. Icons are inline SVG, not emoji. + ========================================================================== */ + * { margin: 0; padding: 0; @@ -6,435 +11,424 @@ } :root { - /* Colors */ - --primary: #ff6b35; - --primary-light: #ff8c42; - --primary-dark: #e55a2b; - --secondary: #004e89; - --secondary-light: #1a759f; - --secondary-dark: #003566; - --accent: #ffd23f; - --success: #06d6a0; - --warning: #f18701; - --danger: #e63946; - - /* Backgrounds */ - --bg-primary: #0f0f23; - --bg-secondary: #1a1a2e; - --bg-tertiary: #16213e; - --bg-card: #0e3460; - --bg-code: #1e1e3f; - --bg-hover: rgba(255, 107, 53, 0.1); - - /* Text Colors */ - --text-primary: #ffffff; - --text-secondary: #e0e6ed; - --text-muted: #8892b0; - --text-code: #64ffda; - + /* Surfaces */ + --bg: #0a0a0b; + --bg-raised: #111113; + --bg-card: #16161a; + --bg-code: #0d0d0f; + --bg-hover: #1c1c20; + /* Borders */ - --border-color: #233554; - --border-light: #364e7a; - - /* Gradients */ - --gradient-primary: linear-gradient(135deg, var(--primary), var(--primary-light)); - --gradient-secondary: linear-gradient(135deg, var(--secondary), var(--secondary-light)); - --gradient-hero: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 50%, #16213e 100%); - - /* Fonts */ - --font-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; - --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace; - + --border: #232327; + --border-hover: #313136; + + /* Text */ + --text-primary: #f4f4f5; + --text-secondary: #a1a1aa; + --text-muted: #6b6b74; + + /* Accent — single flat color, no gradients */ + --accent: #ff6a3d; + --accent-hover: #ff8158; + --accent-dim: rgba(255, 106, 61, 0.12); + --accent-text: #ffb199; + + /* Status */ + --success: #3ecf8e; + --danger: #f0555a; + + /* Type */ + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', Consolas, monospace; + /* Spacing */ - --space-xs: 0.25rem; - --space-sm: 0.5rem; + --space-2xs: 0.25rem; + --space-xs: 0.5rem; + --space-sm: 0.75rem; --space-md: 1rem; --space-lg: 1.5rem; --space-xl: 2rem; - --space-xxl: 3rem; - --space-xxxl: 4rem; - - /* Borders */ + --space-2xl: 3rem; + --space-3xl: 4.5rem; + + /* Radius */ --radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px; - --radius-xl: 16px; - - /* Shadows */ - --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1); - --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15); - --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.2); - --shadow-xl: 0 16px 48px rgba(0, 0, 0, 0.25); - - /* Transitions */ - --transition: all 0.2s ease; - --transition-slow: all 0.3s ease; -} - -/* Base Typography */ -body { - font-family: var(--font-primary); - background: var(--bg-primary); - color: var(--text-primary); - line-height: 1.6; - font-size: 16px; - overflow-x: hidden; -} - -h1, h2, h3, h4, h5, h6 { - font-weight: 600; - line-height: 1.2; - margin-bottom: var(--space-md); -} - -h1 { font-size: 3rem; } -h2 { font-size: 2.5rem; } -h3 { font-size: 2rem; } -h4 { font-size: 1.5rem; } -h5 { font-size: 1.25rem; } -h6 { font-size: 1rem; } - -p { - margin-bottom: var(--space-md); - color: var(--text-secondary); -} - -a { - color: var(--primary); - text-decoration: none; - transition: var(--transition); -} - -a:hover { - color: var(--primary-light); -} -code { - font-family: var(--font-mono); - background: var(--bg-code); - padding: 2px 6px; - border-radius: var(--radius-sm); - font-size: 0.875em; - color: var(--text-code); + --transition: all 0.15s ease; + --nav-height: 64px; } -pre { - background: var(--bg-code); - border-radius: var(--radius-md); - padding: var(--space-lg); - overflow-x: auto; - margin: var(--space-md) 0; - border: 1px solid var(--border-color); +html { + scroll-behavior: smooth; } -pre code { - background: none; - padding: 0; - border-radius: 0; - font-size: 0.875rem; +body { + font-family: var(--font-sans); + background: var(--bg); + color: var(--text-primary); + line-height: 1.6; + font-size: 16px; + overflow-x: hidden; } -/* Container */ .container { max-width: 1200px; margin: 0 auto; padding: 0 var(--space-lg); + width: 100%; } -/* Buttons */ -.btn { - display: inline-flex; - align-items: center; - gap: var(--space-sm); - padding: var(--space-md) var(--space-xl); - border-radius: var(--radius-md); - font-weight: 500; - text-decoration: none; - transition: var(--transition); - border: none; - cursor: pointer; - font-size: 1rem; - font-family: inherit; -} - -.btn-primary { - background: var(--gradient-primary); - color: white; - box-shadow: var(--shadow-md); +h1, h2, h3, h4, h5, h6 { + font-weight: 600; + line-height: 1.25; + letter-spacing: -0.01em; } -.btn-primary:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-lg); - color: white; +a { + color: inherit; + text-decoration: none; } -.btn-secondary { - background: transparent; - color: var(--text-primary); - border: 2px solid var(--border-light); +code, pre, .mono { + font-family: var(--font-mono); } -.btn-secondary:hover { - border-color: var(--primary); - color: var(--primary); -} +/* ========================================================================== + Navbar + ========================================================================== */ -/* Navigation */ .navbar { - position: fixed; + position: sticky; top: 0; - left: 0; - right: 0; - background: rgba(15, 15, 35, 0.95); + z-index: 100; + height: var(--nav-height); + display: flex; + align-items: center; + background: rgba(10, 10, 11, 0.85); backdrop-filter: blur(10px); - border-bottom: 1px solid var(--border-color); - z-index: 1000; - transition: var(--transition); + border-bottom: 1px solid var(--border); } .nav-container { + width: 100%; + max-width: 1200px; + margin: 0 auto; + padding: 0 var(--space-lg); display: flex; align-items: center; justify-content: space-between; - padding: var(--space-md) var(--space-lg); - max-width: 1200px; - margin: 0 auto; + gap: var(--space-lg); } .nav-brand { display: flex; align-items: center; - gap: var(--space-md); - font-weight: 700; - font-size: 1.25rem; + gap: var(--space-sm); + flex-shrink: 0; } -.logo { - font-size: 1.5rem; +.brand-mark { + width: 22px; + height: 22px; + color: var(--accent); } .brand-name { + font-weight: 700; + font-size: 1rem; color: var(--text-primary); } -.version { - background: var(--primary); - color: white; - padding: 2px 8px; +.version-badge { + font-family: var(--font-mono); + font-size: 0.6875rem; + color: var(--accent-text); + background: var(--accent-dim); + border: 1px solid rgba(255, 106, 61, 0.25); border-radius: var(--radius-sm); - font-size: 0.75rem; - font-weight: 500; + padding: 2px 6px; } .nav-menu { display: flex; align-items: center; - gap: var(--space-xl); + gap: var(--space-lg); list-style: none; + flex: 1; + justify-content: center; } .nav-link { + font-size: 0.875rem; color: var(--text-secondary); - font-weight: 500; + padding: var(--space-xs) 0; + border-bottom: 2px solid transparent; transition: var(--transition); - padding: var(--space-sm) 0; - position: relative; + white-space: nowrap; } .nav-link:hover, .nav-link.active { - color: var(--primary); + color: var(--text-primary); } -.nav-link::after { - content: ''; - position: absolute; - bottom: -2px; - left: 0; - width: 0; - height: 2px; - background: var(--primary); +.nav-link.active { + border-bottom-color: var(--accent); +} + +.nav-search { + display: flex; + align-items: center; + gap: var(--space-xs); + background: var(--bg-raised); + border: 1px solid var(--border); + border-radius: var(--radius-md); + padding: 6px var(--space-sm); + color: var(--text-muted); + width: 200px; + flex-shrink: 0; transition: var(--transition); } -.nav-link:hover::after, -.nav-link.active::after { +.nav-search:focus-within { + border-color: var(--border-hover); +} + +.nav-search svg { + flex-shrink: 0; +} + +.nav-search input { + background: none; + border: none; + outline: none; + color: var(--text-primary); + font-family: inherit; + font-size: 0.8125rem; width: 100%; } -.github-link { +.nav-search input::placeholder { + color: var(--text-muted); +} + +.nav-search kbd { + font-family: var(--font-mono); + font-size: 0.6875rem; + color: var(--text-muted); + background: var(--bg-hover); + border: 1px solid var(--border); + border-radius: 4px; + padding: 1px 5px; + flex-shrink: 0; +} + +.nav-actions { display: flex; align-items: center; - gap: var(--space-sm); - padding: var(--space-sm) var(--space-md); - background: var(--bg-secondary); + gap: var(--space-md); + flex-shrink: 0; +} + +.icon-link { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; border-radius: var(--radius-md); - color: var(--text-primary); + color: var(--text-secondary); transition: var(--transition); } -.github-link:hover { - background: var(--bg-tertiary); - color: var(--primary); +.icon-link:hover { + color: var(--text-primary); + background: var(--bg-hover); } -/* Hamburger Menu */ .hamburger { display: none; flex-direction: column; - cursor: pointer; gap: 4px; + background: none; + border: none; + cursor: pointer; + padding: var(--space-xs); } -.bar { - width: 25px; - height: 3px; +.hamburger .bar { + width: 20px; + height: 2px; background: var(--text-primary); + border-radius: 1px; + transition: var(--transition); +} + +.hamburger.active .bar:nth-child(1) { transform: translateY(6px) rotate(45deg); } +.hamburger.active .bar:nth-child(2) { opacity: 0; } +.hamburger.active .bar:nth-child(3) { transform: translateY(-6px) rotate(-45deg); } + +/* ========================================================================== + Buttons + ========================================================================== */ + +.btn { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + padding: 0.625rem 1.125rem; + border-radius: var(--radius-md); + font-size: 0.9375rem; + font-weight: 500; + border: 1px solid transparent; + cursor: pointer; transition: var(--transition); - border-radius: 2px; } -/* Main Content */ -.main-content { - margin-top: 80px; +.btn-primary { + background: var(--accent); + color: #140a06; } -/* Hero Section */ -.hero { - background: var(--gradient-hero); - padding: var(--space-xxxl) 0; - position: relative; - overflow: hidden; +.btn-primary:hover { + background: var(--accent-hover); } -.hero::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - radial-gradient(circle at 20% 20%, rgba(255, 107, 53, 0.1) 0%, transparent 50%), - radial-gradient(circle at 80% 80%, rgba(0, 78, 137, 0.1) 0%, transparent 50%); - pointer-events: none; +.btn-secondary { + background: transparent; + border-color: var(--border); + color: var(--text-primary); +} + +.btn-secondary:hover { + border-color: var(--border-hover); + background: var(--bg-hover); +} + +/* ========================================================================== + Hero + ========================================================================== */ + +.hero { + padding: var(--space-3xl) 0 var(--space-2xl); + border-bottom: 1px solid var(--border); } .hero .container { display: grid; - grid-template-columns: 1fr 1fr; - gap: var(--space-xxxl); + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: var(--space-2xl); align-items: center; - position: relative; - z-index: 1; } -.hero-content { - max-width: 600px; +/* Grid items default to min-width:auto, which lets an unwrapped
 force
+   the whole grid wider than the viewport - the actual root cause of the
+   mobile overflow bug this rewrite fixes. min-width: 0 lets both columns
+   shrink below their content's natural width so the code panel scrolls
+   inside itself instead of blowing out the page. */
+.hero-content,
+.hero-code {
+    min-width: 0;
 }
 
-.hero-title {
-    font-size: 4rem;
-    font-weight: 800;
+.eyebrow {
+    display: inline-flex;
+    align-items: center;
+    gap: var(--space-xs);
+    font-size: 0.8125rem;
+    color: var(--accent-text);
+    background: var(--accent-dim);
+    border: 1px solid rgba(255, 106, 61, 0.2);
+    border-radius: 999px;
+    padding: 4px 10px;
     margin-bottom: var(--space-lg);
-    line-height: 1.1;
 }
 
-.gradient-text {
-    background: var(--gradient-primary);
-    -webkit-background-clip: text;
-    -webkit-text-fill-color: transparent;
-    background-clip: text;
+.hero-title {
+    font-size: clamp(2.25rem, 5vw, 3.25rem);
+    font-weight: 700;
+    line-height: 1.1;
+    margin-bottom: var(--space-md);
+    color: var(--text-primary);
 }
 
-.hero-subtitle {
-    font-size: 1.5rem;
+.hero-description {
+    font-size: 1.0625rem;
     color: var(--text-secondary);
-    margin-bottom: var(--space-lg);
-    font-weight: 500;
+    line-height: 1.7;
+    margin-bottom: var(--space-xl);
+    max-width: 46ch;
 }
 
-.hero-description {
-    font-size: 1.125rem;
-    color: var(--text-muted);
-    margin-bottom: var(--space-xl);
-    line-height: 1.7;
+.hero-description code {
+    color: var(--accent-text);
+    font-size: 0.875em;
 }
 
 .hero-actions {
     display: flex;
-    gap: var(--space-lg);
-    margin-bottom: var(--space-xxl);
+    flex-wrap: wrap;
+    gap: var(--space-md);
+    margin-bottom: var(--space-2xl);
 }
 
-.hero-stats {
-    display: grid;
-    grid-template-columns: repeat(4, 1fr);
+.hero-meta {
+    display: flex;
+    flex-wrap: wrap;
     gap: var(--space-lg);
 }
 
-.stat {
-    text-align: center;
-}
-
-.stat-number {
-    display: block;
-    font-size: 2rem;
-    margin-bottom: var(--space-sm);
-}
-
-.stat-label {
-    font-size: 0.875rem;
+.hero-meta-item {
+    display: flex;
+    align-items: center;
+    gap: var(--space-xs);
+    font-size: 0.8125rem;
     color: var(--text-muted);
-    font-weight: 500;
 }
 
-/* Hero Code */
-.hero-code {
-    position: relative;
+.hero-meta-item svg {
+    color: var(--text-secondary);
+    flex-shrink: 0;
 }
 
 .code-window {
     background: var(--bg-code);
+    border: 1px solid var(--border);
     border-radius: var(--radius-lg);
-    border: 1px solid var(--border-color);
     overflow: hidden;
-    box-shadow: var(--shadow-xl);
 }
 
 .code-header {
     display: flex;
     align-items: center;
     justify-content: space-between;
-    padding: var(--space-md) var(--space-lg);
-    background: var(--bg-secondary);
-    border-bottom: 1px solid var(--border-color);
+    padding: var(--space-sm) var(--space-md);
+    background: var(--bg-raised);
+    border-bottom: 1px solid var(--border);
 }
 
 .code-dots {
     display: flex;
-    gap: var(--space-sm);
+    gap: 6px;
 }
 
 .dot {
-    width: 12px;
-    height: 12px;
+    width: 10px;
+    height: 10px;
     border-radius: 50%;
+    background: var(--border-hover);
 }
 
-.dot.red { background: #ff5f56; }
-.dot.yellow { background: #ffbd2e; }
-.dot.green { background: #27ca3f; }
-
 .code-title {
     color: var(--text-muted);
-    font-size: 0.875rem;
-    font-family: var(--font-mono);
+    font-size: 0.75rem;
 }
 
 .code-content {
     padding: var(--space-lg);
+    overflow-x: auto;
 }
 
 .code-content pre {
@@ -442,746 +436,678 @@ pre code {
     border: none;
     padding: 0;
     margin: 0;
+    overflow: visible;
 }
 
-/* Features Section */
-.features {
-    padding: var(--space-xxxl) 0;
-    background: var(--bg-secondary);
+/* ==========================================================================
+   Section shell
+   ========================================================================== */
+
+section {
+    padding: var(--space-3xl) 0;
+    border-bottom: 1px solid var(--border);
+}
+
+.section-header {
+    margin-bottom: var(--space-2xl);
+    max-width: 60ch;
 }
 
 .section-title {
-    text-align: center;
-    margin-bottom: var(--space-xxl);
-    color: var(--text-primary);
+    font-size: clamp(1.5rem, 3vw, 2rem);
+    margin-bottom: var(--space-sm);
+}
+
+.section-description {
+    color: var(--text-secondary);
+    font-size: 1rem;
+    line-height: 1.7;
 }
 
+/* ==========================================================================
+   Feature grid
+   ========================================================================== */
+
 .features-grid {
     display: grid;
-    grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
-    gap: var(--space-xl);
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+    gap: var(--space-lg);
 }
 
 .feature-card {
     background: var(--bg-card);
-    padding: var(--space-xl);
+    border: 1px solid var(--border);
     border-radius: var(--radius-lg);
-    border: 1px solid var(--border-color);
+    padding: var(--space-lg);
     transition: var(--transition);
 }
 
 .feature-card:hover {
-    transform: translateY(-4px);
-    box-shadow: var(--shadow-lg);
-    border-color: var(--primary);
+    border-color: var(--border-hover);
 }
 
 .feature-icon {
-    font-size: 3rem;
-    margin-bottom: var(--space-lg);
-}
-
-.feature-title {
-    color: var(--text-primary);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 36px;
+    height: 36px;
+    border-radius: var(--radius-md);
+    background: var(--accent-dim);
+    color: var(--accent);
     margin-bottom: var(--space-md);
 }
 
+.feature-title {
+    font-size: 1rem;
+    margin-bottom: var(--space-xs);
+}
+
 .feature-description {
-    color: var(--text-muted);
+    color: var(--text-secondary);
+    font-size: 0.9375rem;
     line-height: 1.6;
 }
 
-/* Installation Section */
-.installation {
-    padding: var(--space-xxxl) 0;
+.feature-description code {
+    color: var(--accent-text);
+    font-size: 0.875em;
 }
 
-.installation-content {
-    display: grid;
-    grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
-    gap: var(--space-xl);
+/* ==========================================================================
+   Callouts / notices
+   ========================================================================== */
+
+.callout {
+    display: flex;
+    gap: var(--space-md);
+    background: var(--bg-card);
+    border: 1px solid var(--border);
+    border-left: 3px solid var(--accent);
+    border-radius: var(--radius-md);
+    padding: var(--space-md) var(--space-lg);
     margin-bottom: var(--space-xl);
 }
 
-.install-method {
-    background: var(--bg-secondary);
-    padding: var(--space-xl);
-    border-radius: var(--radius-lg);
-    border: 1px solid var(--border-color);
+.callout svg {
+    color: var(--accent);
+    flex-shrink: 0;
+    margin-top: 2px;
 }
 
-.install-title {
-    color: var(--text-primary);
-    margin-bottom: var(--space-lg);
-    font-size: 1.25rem;
+.callout-title {
+    font-size: 0.9375rem;
+    margin-bottom: var(--space-2xs);
 }
 
-.code-block {
-    position: relative;
+.callout p {
+    color: var(--text-secondary);
+    font-size: 0.9375rem;
+    line-height: 1.6;
 }
 
-.copy-btn {
-    position: absolute;
-    top: var(--space-md);
-    right: var(--space-md);
-    background: var(--bg-tertiary);
-    border: 1px solid var(--border-color);
-    color: var(--text-muted);
-    padding: var(--space-sm);
-    border-radius: var(--radius-sm);
-    cursor: pointer;
-    transition: var(--transition);
+.callout code {
+    color: var(--accent-text);
+    font-size: 0.875em;
 }
 
-.copy-btn:hover {
+/* ==========================================================================
+   Installation
+   ========================================================================== */
+
+.install-grid {
+    display: grid;
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+    gap: var(--space-lg);
+    margin-bottom: var(--space-xl);
+}
+
+.install-card {
     background: var(--bg-card);
-    color: var(--primary);
+    border: 1px solid var(--border);
+    border-radius: var(--radius-lg);
+    padding: var(--space-lg);
+    min-width: 0;
+}
+
+.install-title {
+    font-size: 0.9375rem;
+    margin-bottom: var(--space-md);
+    display: flex;
+    align-items: center;
+    gap: var(--space-xs);
+    color: var(--text-secondary);
 }
 
 .requirements {
-    background: var(--bg-secondary);
-    padding: var(--space-xl);
+    background: var(--bg-card);
+    border: 1px solid var(--border);
     border-radius: var(--radius-lg);
-    border: 1px solid var(--border-color);
+    padding: var(--space-lg);
 }
 
 .requirements h4 {
-    color: var(--text-primary);
-    margin-bottom: var(--space-md);
+    font-size: 0.9375rem;
+    margin-bottom: var(--space-sm);
 }
 
 .requirements ul {
     list-style: none;
+    color: var(--text-secondary);
+    font-size: 0.9375rem;
+    display: grid;
+    gap: var(--space-xs);
 }
 
-.requirements li {
-    padding: var(--space-sm) 0;
+.requirements li::before {
+    content: '—';
+    color: var(--text-muted);
+    margin-right: var(--space-xs);
+}
+
+/* ==========================================================================
+   Code blocks (generic)
+   ========================================================================== */
+
+pre {
+    background: var(--bg-code);
+    border: 1px solid var(--border);
+    border-radius: var(--radius-md);
+    padding: var(--space-lg);
+    overflow-x: auto;
+    min-width: 0;
+}
+
+pre code {
+    font-size: 0.875rem;
     color: var(--text-secondary);
+    background: none;
 }
 
-.requirements li::before {
-    content: '✓';
-    color: var(--success);
-    font-weight: bold;
-    margin-right: var(--space-sm);
+.code-block {
+    position: relative;
+    min-width: 0;
 }
 
-/* Quick Start Section */
-.quickstart {
-    padding: var(--space-xxxl) 0;
-    background: var(--bg-secondary);
+.copy-btn {
+    position: absolute;
+    top: var(--space-sm);
+    right: var(--space-sm);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 28px;
+    height: 28px;
+    background: var(--bg-raised);
+    border: 1px solid var(--border);
+    border-radius: var(--radius-sm);
+    color: var(--text-muted);
+    cursor: pointer;
+    transition: var(--transition);
 }
 
-.quickstart-tabs {
-    max-width: 1000px;
-    margin: 0 auto;
+.copy-btn:hover {
+    color: var(--text-primary);
+    border-color: var(--border-hover);
 }
 
+/* ==========================================================================
+   Quick start tabs
+   ========================================================================== */
+
 .tab-buttons {
     display: flex;
-    justify-content: center;
-    gap: var(--space-sm);
-    margin-bottom: var(--space-xl);
     flex-wrap: wrap;
+    gap: var(--space-2xs);
+    border-bottom: 1px solid var(--border);
+    margin-bottom: var(--space-lg);
 }
 
 .tab-btn {
-    background: var(--bg-tertiary);
+    background: none;
+    border: none;
+    border-bottom: 2px solid transparent;
     color: var(--text-muted);
-    border: 1px solid var(--border-color);
-    padding: var(--space-md) var(--space-lg);
-    border-radius: var(--radius-md);
+    font-family: inherit;
+    font-size: 0.875rem;
+    padding: var(--space-sm) var(--space-md);
     cursor: pointer;
     transition: var(--transition);
-    font-family: inherit;
-    font-size: 1rem;
+    margin-bottom: -1px;
 }
 
-.tab-btn.active,
 .tab-btn:hover {
-    background: var(--primary);
-    color: white;
-    border-color: var(--primary);
+    color: var(--text-secondary);
+}
+
+.tab-btn.active {
+    color: var(--text-primary);
+    border-bottom-color: var(--accent);
 }
 
 .tab-content {
     display: none;
+    min-width: 0;
 }
 
 .tab-content.active {
     display: block;
 }
 
-.code-example {
-    background: var(--bg-code);
-    border-radius: var(--radius-lg);
-    border: 1px solid var(--border-color);
-    overflow: hidden;
-}
-
-.code-example pre {
-    background: none;
-    border: none;
-    margin: 0;
-}
+/* ==========================================================================
+   API reference
+   ========================================================================== */
 
-/* API Reference Section */
-.api-reference {
-    padding: var(--space-xxxl) 0;
-}
-
-.api-nav {
+.api-layout {
     display: grid;
-    grid-template-columns: 250px 1fr;
-    gap: var(--space-xl);
-    max-width: 1200px;
-    margin: 0 auto;
+    grid-template-columns: 220px minmax(0, 1fr);
+    gap: var(--space-2xl);
+    align-items: start;
 }
 
 .api-sidebar {
-    background: var(--bg-secondary);
-    padding: var(--space-lg);
-    border-radius: var(--radius-lg);
-    border: 1px solid var(--border-color);
-    height: fit-content;
     position: sticky;
-    top: 100px;
+    top: calc(var(--nav-height) + var(--space-lg));
 }
 
 .api-menu {
     list-style: none;
-}
-
-.api-menu li {
-    margin-bottom: var(--space-sm);
+    display: grid;
+    gap: var(--space-2xs);
+    border-left: 1px solid var(--border);
 }
 
 .api-link {
     display: block;
-    padding: var(--space-sm) var(--space-md);
+    font-size: 0.875rem;
     color: var(--text-muted);
-    border-radius: var(--radius-sm);
+    padding: var(--space-2xs) var(--space-md);
+    border-left: 2px solid transparent;
+    margin-left: -1px;
     transition: var(--transition);
 }
 
-.api-link:hover,
+.api-link:hover {
+    color: var(--text-secondary);
+}
+
 .api-link.active {
-    background: var(--bg-hover);
-    color: var(--primary);
+    color: var(--text-primary);
+    border-left-color: var(--accent);
 }
 
 .api-content {
     min-width: 0;
-}
-
-.api-section {
-    margin-bottom: var(--space-xxl);
+    display: grid;
+    gap: var(--space-2xl);
 }
 
 .api-section-title {
-    color: var(--text-primary);
-    margin-bottom: var(--space-xl);
-    padding-bottom: var(--space-md);
-    border-bottom: 2px solid var(--border-color);
+    font-size: 1.25rem;
+    margin-bottom: var(--space-md);
+    padding-bottom: var(--space-sm);
+    border-bottom: 1px solid var(--border);
 }
 
-.section-description {
-    color: var(--text-muted);
-    margin-bottom: var(--space-lg);
-    font-size: 1.125rem;
+.api-method {
+    padding: var(--space-lg) 0;
+    border-bottom: 1px solid var(--border);
 }
 
-.api-method {
-    background: var(--bg-secondary);
-    border: 1px solid var(--border-color);
-    border-radius: var(--radius-lg);
-    padding: var(--space-xl);
-    margin-bottom: var(--space-xl);
+.api-method:last-child {
+    border-bottom: none;
+    padding-bottom: 0;
 }
 
 .method-signature {
-    margin-bottom: var(--space-lg);
+    margin-bottom: var(--space-sm);
 }
 
 .method-name {
-    background: var(--bg-code);
-    color: var(--text-code);
-    padding: var(--space-sm) var(--space-md);
+    font-size: 0.9375rem;
+    color: var(--accent-text);
+    background: var(--accent-dim);
+    padding: var(--space-xs) var(--space-sm);
     border-radius: var(--radius-sm);
-    font-family: var(--font-mono);
-    font-size: 1.125rem;
-    font-weight: 500;
+    display: inline-block;
+    word-break: break-word;
 }
 
 .method-description {
-    margin-bottom: var(--space-lg);
-    font-size: 1.125rem;
+    color: var(--text-secondary);
+    font-size: 0.9375rem;
+    line-height: 1.6;
+    margin-bottom: var(--space-md);
 }
 
-.method-params,
-.method-returns,
-.method-raises,
-.method-example {
-    margin-bottom: var(--space-lg);
+.method-description code {
+    color: var(--text-primary);
+    font-size: 0.875em;
 }
 
 .method-params h5,
 .method-returns h5,
 .method-raises h5,
 .method-example h5 {
-    color: var(--text-primary);
-    margin-bottom: var(--space-sm);
-    font-size: 1rem;
-    font-weight: 600;
+    font-size: 0.75rem;
+    text-transform: uppercase;
+    letter-spacing: 0.04em;
+    color: var(--text-muted);
+    margin-bottom: var(--space-xs);
 }
 
 .method-params ul {
     list-style: none;
-    padding-left: var(--space-md);
-}
-
-.method-params li {
     color: var(--text-secondary);
-    margin-bottom: var(--space-sm);
+    font-size: 0.875rem;
+    display: grid;
+    gap: var(--space-2xs);
+    margin-bottom: var(--space-md);
 }
 
-.error-hierarchy {
-    background: var(--bg-code);
-    border-radius: var(--radius-lg);
-    border: 1px solid var(--border-color);
-    padding: var(--space-lg);
-    margin-bottom: var(--space-xl);
+.method-params code,
+.method-returns code {
+    color: var(--accent-text);
 }
 
-.error-hierarchy h4 {
-    color: var(--text-primary);
+.method-returns,
+.method-raises {
     margin-bottom: var(--space-md);
+    font-size: 0.875rem;
+    color: var(--text-secondary);
 }
 
-.type-table {
+.method-example {
+    margin-top: var(--space-md);
+}
+
+/* Type table */
+
+.type-table-wrap {
     overflow-x: auto;
-    margin: var(--space-xl) 0;
+    border: 1px solid var(--border);
+    border-radius: var(--radius-lg);
+    margin-bottom: var(--space-lg);
 }
 
-.type-table table {
+table {
     width: 100%;
     border-collapse: collapse;
-    background: var(--bg-secondary);
-    border-radius: var(--radius-lg);
-    overflow: hidden;
-    border: 1px solid var(--border-color);
+    font-size: 0.875rem;
 }
 
-.type-table th,
-.type-table td {
-    padding: var(--space-md);
+thead th {
     text-align: left;
-    border-bottom: 1px solid var(--border-color);
-}
-
-.type-table th {
-    background: var(--bg-tertiary);
-    color: var(--text-primary);
-    font-weight: 600;
+    font-size: 0.75rem;
+    text-transform: uppercase;
+    letter-spacing: 0.04em;
+    color: var(--text-muted);
+    background: var(--bg-raised);
+    padding: var(--space-sm) var(--space-md);
+    border-bottom: 1px solid var(--border);
+    white-space: nowrap;
 }
 
-.type-table td {
+tbody td {
+    padding: var(--space-sm) var(--space-md);
+    border-bottom: 1px solid var(--border);
     color: var(--text-secondary);
+    vertical-align: top;
 }
 
-.type-table tr:last-child td {
+tbody tr:last-child td {
     border-bottom: none;
 }
 
-/* Examples Section */
-.examples {
-    padding: var(--space-xxxl) 0;
-    background: var(--bg-secondary);
+td code {
+    color: var(--accent-text);
+    font-size: 0.8125rem;
+}
+
+/* Error hierarchy tree */
+
+.error-tree {
+    background: var(--bg-code);
+    border: 1px solid var(--border);
+    border-radius: var(--radius-md);
+    padding: var(--space-lg);
+    font-family: var(--font-mono);
+    font-size: 0.875rem;
+    color: var(--text-secondary);
+    line-height: 1.9;
+    margin-bottom: var(--space-lg);
+    overflow-x: auto;
 }
 
+.error-tree .exc-name { color: var(--text-primary); }
+.error-tree .exc-comment { color: var(--text-muted); }
+
+/* ==========================================================================
+   Examples
+   ========================================================================== */
+
 .examples-grid {
     display: grid;
-    grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
-    gap: var(--space-xl);
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+    gap: var(--space-lg);
 }
 
 .example-card {
     background: var(--bg-card);
-    border: 1px solid var(--border-color);
+    border: 1px solid var(--border);
     border-radius: var(--radius-lg);
-    overflow: hidden;
-    transition: var(--transition);
-}
-
-.example-card:hover {
-    transform: translateY(-4px);
-    box-shadow: var(--shadow-lg);
-}
-
-.example-title {
-    color: var(--text-primary);
     padding: var(--space-lg);
-    margin: 0;
-    background: var(--bg-tertiary);
-    border-bottom: 1px solid var(--border-color);
-}
-
-.example-code {
-    padding: var(--space-lg);
-}
-
-.example-code pre {
-    background: var(--bg-code);
-    margin: 0;
-}
-
-/* Performance Section */
-.performance {
-    padding: var(--space-xxxl) 0;
+    min-width: 0;
 }
 
-.performance-content {
-    max-width: 1000px;
-    margin: 0 auto;
+.example-title {
+    display: flex;
+    align-items: center;
+    gap: var(--space-xs);
+    font-size: 0.9375rem;
+    margin-bottom: var(--space-md);
+    color: var(--text-secondary);
 }
 
-.performance-intro {
-    margin-bottom: var(--space-xxl);
+.example-title svg {
+    color: var(--accent);
 }
 
-.performance-intro h3 {
-    text-align: center;
-    margin-bottom: var(--space-xl);
-    color: var(--text-primary);
-}
+/* ==========================================================================
+   Performance
+   ========================================================================== */
 
 .performance-reasons {
     display: grid;
-    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+    grid-template-columns: repeat(4, minmax(0, 1fr));
     gap: var(--space-lg);
+    margin-bottom: var(--space-2xl);
 }
 
 .reason {
     display: flex;
-    align-items: flex-start;
-    gap: var(--space-md);
-    background: var(--bg-secondary);
-    padding: var(--space-lg);
-    border-radius: var(--radius-lg);
-    border: 1px solid var(--border-color);
+    flex-direction: column;
+    gap: var(--space-sm);
 }
 
 .reason-icon {
-    font-size: 2rem;
-    flex-shrink: 0;
-}
-
-.reason-content h4 {
-    color: var(--text-primary);
-    margin-bottom: var(--space-sm);
-}
-
-.reason-content p {
-    color: var(--text-muted);
-    margin: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 36px;
+    height: 36px;
+    border-radius: var(--radius-md);
+    background: var(--bg-card);
+    border: 1px solid var(--border);
+    color: var(--accent);
 }
 
-.performance-comparison {
-    margin-bottom: var(--space-xxl);
+.reason h4 {
+    font-size: 0.9375rem;
 }
 
-.performance-comparison h3 {
-    text-align: center;
-    margin-bottom: var(--space-xl);
-    color: var(--text-primary);
+.reason p {
+    color: var(--text-secondary);
+    font-size: 0.875rem;
+    line-height: 1.5;
 }
 
-.comparison-table {
+.comparison-wrap {
     overflow-x: auto;
-}
-
-.comparison-table table {
-    width: 100%;
-    border-collapse: collapse;
-    background: var(--bg-secondary);
+    border: 1px solid var(--border);
     border-radius: var(--radius-lg);
-    overflow: hidden;
-    border: 1px solid var(--border-color);
+    margin-bottom: var(--space-2xl);
 }
 
-.comparison-table th,
-.comparison-table td {
-    padding: var(--space-md);
-    text-align: left;
-    border-bottom: 1px solid var(--border-color);
-}
-
-.comparison-table th {
-    background: var(--bg-tertiary);
+.comparison-wrap th:first-child,
+.comparison-wrap td:first-child {
     color: var(--text-primary);
-    font-weight: 600;
-}
-
-.comparison-table td {
-    color: var(--text-secondary);
-}
-
-.comparison-table tr:last-child td {
-    border-bottom: none;
-}
-
-.badge {
-    display: inline-block;
-    padding: 4px 8px;
-    border-radius: var(--radius-sm);
-    font-size: 0.75rem;
     font-weight: 500;
 }
 
-.badge.rust { background: #ce422b; color: white; }
-.badge.c { background: #555; color: white; }
-.badge.cython { background: #3776ab; color: white; }
-.badge.success { background: var(--success); color: white; }
-.badge.warning { background: var(--warning); color: white; }
-.badge.danger { background: var(--danger); color: white; }
-
-.stars {
-    font-size: 1.25rem;
+.check {
+    color: var(--success);
 }
 
-.performance-benchmark {
-    text-align: center;
+.cross {
+    color: var(--danger);
 }
 
-.performance-benchmark h3 {
-    margin-bottom: var(--space-xl);
-    color: var(--text-primary);
+.dash {
+    color: var(--text-muted);
 }
 
-.benchmark-code {
-    max-width: 800px;
-    margin: 0 auto;
+.pill {
+    display: inline-flex;
+    align-items: center;
+    gap: 4px;
+    font-size: 0.8125rem;
 }
 
-/* Footer */
+/* ==========================================================================
+   Footer
+   ========================================================================== */
+
 .footer {
-    background: var(--bg-tertiary);
-    border-top: 1px solid var(--border-color);
-    padding: var(--space-xxl) 0 var(--space-lg);
+    padding: var(--space-2xl) 0 var(--space-xl);
 }
 
-.footer-content {
+.footer-grid {
     display: grid;
     grid-template-columns: 2fr 1fr 1fr 1fr;
     gap: var(--space-xl);
     margin-bottom: var(--space-xl);
 }
 
-.footer-title {
-    color: var(--text-primary);
-    margin-bottom: var(--space-md);
-}
-
-.footer-description {
-    color: var(--text-muted);
-    margin-bottom: var(--space-lg);
-    line-height: 1.6;
-}
-
-.footer-links {
+.footer-brand {
     display: flex;
-    gap: var(--space-md);
+    align-items: center;
+    gap: var(--space-xs);
+    margin-bottom: var(--space-sm);
 }
 
-.footer-link {
-    display: flex;
-    align-items: center;
-    gap: var(--space-sm);
-    padding: var(--space-sm) var(--space-md);
-    background: var(--bg-secondary);
-    border-radius: var(--radius-md);
-    color: var(--text-secondary);
-    transition: var(--transition);
-    text-decoration: none;
+.footer-brand svg {
+    color: var(--accent);
 }
 
-.footer-link:hover {
-    background: var(--bg-card);
-    color: var(--primary);
+.footer-description {
+    color: var(--text-secondary);
+    font-size: 0.875rem;
+    line-height: 1.6;
+    max-width: 32ch;
 }
 
-.footer-section-title {
-    color: var(--text-primary);
+.footer-heading {
+    font-size: 0.75rem;
+    text-transform: uppercase;
+    letter-spacing: 0.04em;
+    color: var(--text-muted);
     margin-bottom: var(--space-md);
-    font-size: 1rem;
 }
 
 .footer-menu {
     list-style: none;
-}
-
-.footer-menu li {
-    margin-bottom: var(--space-sm);
+    display: grid;
+    gap: var(--space-sm);
 }
 
 .footer-menu-link {
-    color: var(--text-muted);
+    font-size: 0.875rem;
+    color: var(--text-secondary);
     transition: var(--transition);
 }
 
 .footer-menu-link:hover {
-    color: var(--primary);
-}
-
-.footer-tech {
-    color: var(--text-secondary);
+    color: var(--text-primary);
 }
 
 .footer-bottom {
-    border-top: 1px solid var(--border-color);
-    padding-top: var(--space-lg);
     display: flex;
     justify-content: space-between;
     align-items: center;
     flex-wrap: wrap;
     gap: var(--space-md);
-}
-
-.footer-copyright,
-.footer-version {
+    padding-top: var(--space-xl);
+    border-top: 1px solid var(--border);
     color: var(--text-muted);
-    font-size: 0.875rem;
+    font-size: 0.8125rem;
 }
 
-/* Responsive Design */
-@media (max-width: 1024px) {
-    .hero .container {
-        grid-template-columns: 1fr;
-        gap: var(--space-xl);
-        text-align: center;
-    }
-    
-    .hero-stats {
-        grid-template-columns: repeat(2, 1fr);
-    }
-    
-    .api-nav {
-        grid-template-columns: 1fr;
-    }
-    
-    .api-sidebar {
-        position: static;
-    }
-    
-    .footer-content {
-        grid-template-columns: 1fr 1fr;
-        gap: var(--space-lg);
-    }
+.search-hit {
+    background: var(--accent-dim);
+    border-radius: 4px;
+    box-shadow: 0 0 0 1px rgba(255, 106, 61, 0.25);
 }
 
-@media (max-width: 768px) {
-    .nav-menu {
-        display: none;
-    }
-    
-    .hamburger {
-        display: flex;
-    }
-    
-    .hero-title {
-        font-size: 3rem;
-    }
-    
-    .hero-actions {
-        flex-direction: column;
-        align-items: center;
-    }
-    
-    .hero-stats {
-        grid-template-columns: repeat(2, 1fr);
-    }
-    
-    .features-grid {
-        grid-template-columns: 1fr;
-    }
-    
-    .installation-content {
-        grid-template-columns: 1fr;
-    }
-    
-    .tab-buttons {
-        gap: var(--space-xs);
-    }
-    
-    .tab-btn {
-        padding: var(--space-sm) var(--space-md);
-        font-size: 0.875rem;
-    }
-    
-    .examples-grid {
-        grid-template-columns: 1fr;
-    }
-    
-    .performance-reasons {
-        grid-template-columns: 1fr;
-    }
-    
-    .footer-content {
-        grid-template-columns: 1fr;
-        text-align: center;
-    }
-    
-    .footer-bottom {
-        flex-direction: column;
-        text-align: center;
-    }
+/* ==========================================================================
+   Scroll to top
+   ========================================================================== */
+
+.scroll-top {
+    position: fixed;
+    bottom: var(--space-lg);
+    right: var(--space-lg);
+    width: 40px;
+    height: 40px;
+    border-radius: 50%;
+    background: var(--bg-card);
+    border: 1px solid var(--border);
+    color: var(--text-secondary);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    opacity: 0;
+    visibility: hidden;
+    transform: translateY(8px);
+    transition: var(--transition);
+    z-index: 90;
 }
 
-@media (max-width: 480px) {
-    .container {
-        padding: 0 var(--space-md);
-    }
-    
-    .hero {
-        padding: var(--space-xl) 0;
-    }
-    
-    .hero-title {
-        font-size: 2.5rem;
-    }
-    
-    .hero-subtitle {
-        font-size: 1.25rem;
-    }
-    
-    .hero-stats {
-        grid-template-columns: 1fr;
-    }
-    
-    .section-title {
-        font-size: 2rem;
-    }
-    
-    .nav-container {
-        padding: var(--space-md);
-    }
+.scroll-top.visible {
+    opacity: 1;
+    visibility: visible;
+    transform: translateY(0);
 }
 
-/* Smooth Scrolling */
-html {
-    scroll-behavior: smooth;
+.scroll-top:hover {
+    border-color: var(--border-hover);
+    color: var(--text-primary);
 }
 
-/* Code Syntax Highlighting Overrides */
+/* ==========================================================================
+   Syntax highlighting (Prism overrides)
+   ========================================================================== */
+
 .token.comment,
 .token.prolog,
 .token.doctype,
 .token.cdata {
-    color: #636f88;
+    color: var(--text-muted);
 }
 
 .token.punctuation {
-    color: #81a2be;
+    color: var(--text-secondary);
 }
 
 .token.property,
@@ -1191,7 +1117,7 @@ html {
 .token.constant,
 .token.symbol,
 .token.deleted {
-    color: #de935f;
+    color: #e39a6b;
 }
 
 .token.selector,
@@ -1200,7 +1126,7 @@ html {
 .token.char,
 .token.builtin,
 .token.inserted {
-    color: #b5bd68;
+    color: #9ecb8f;
 }
 
 .token.operator,
@@ -1208,63 +1134,163 @@ html {
 .token.url,
 .language-css .token.string,
 .style .token.string {
-    color: #8abeb7;
+    color: #7ec7c2;
 }
 
 .token.atrule,
 .token.attr-value,
 .token.keyword {
-    color: #b294bb;
+    color: #c298e0;
 }
 
 .token.function,
 .token.class-name {
-    color: #f0c674;
+    color: #e5c07b;
 }
 
 .token.regex,
 .token.important,
 .token.variable {
-    color: #cc6666;
+    color: var(--accent-text);
+}
+
+/* ==========================================================================
+   Accessibility
+   ========================================================================== */
+
+a:focus-visible,
+button:focus-visible,
+input:focus-visible {
+    outline: 2px solid var(--accent);
+    outline-offset: 2px;
+}
+
+/* ==========================================================================
+   Responsive
+   ========================================================================== */
+
+@media (max-width: 1024px) {
+    .nav-search {
+        display: none;
+    }
+
+    .hero .container {
+        grid-template-columns: minmax(0, 1fr);
+    }
+
+    .features-grid {
+        grid-template-columns: repeat(2, minmax(0, 1fr));
+    }
+
+    .api-layout {
+        grid-template-columns: minmax(0, 1fr);
+    }
+
+    .api-sidebar {
+        position: static;
+    }
+
+    .api-menu {
+        display: flex;
+        overflow-x: auto;
+        border-left: none;
+        border-bottom: 1px solid var(--border);
+        padding-bottom: var(--space-sm);
+        gap: var(--space-xs);
+    }
+
+    .api-link {
+        border-left: none;
+        border-bottom: 2px solid transparent;
+        white-space: nowrap;
+        margin-left: 0;
+    }
+
+    .api-link.active {
+        border-bottom-color: var(--accent);
+    }
+
+    .performance-reasons {
+        grid-template-columns: repeat(2, minmax(0, 1fr));
+    }
+
+    .footer-grid {
+        grid-template-columns: repeat(2, minmax(0, 1fr));
+    }
 }
 
-/* Loading Animation */
-@keyframes fadeInUp {
-    from {
+@media (max-width: 768px) {
+    .nav-menu {
+        position: fixed;
+        top: var(--nav-height);
+        left: 0;
+        right: 0;
+        flex-direction: column;
+        align-items: stretch;
+        background: var(--bg);
+        border-bottom: 1px solid var(--border);
+        padding: var(--space-md) var(--space-lg);
+        gap: var(--space-sm);
+        transform: translateY(-8px);
         opacity: 0;
-        transform: translateY(30px);
+        visibility: hidden;
+        transition: var(--transition);
     }
-    to {
-        opacity: 1;
+
+    .nav-menu.active {
         transform: translateY(0);
+        opacity: 1;
+        visibility: visible;
+    }
+
+    .nav-link {
+        padding: var(--space-sm) 0;
+        border-bottom: 1px solid var(--border);
+    }
+
+    .nav-link.active {
+        border-bottom: 1px solid var(--border);
+    }
+
+    .hamburger {
+        display: flex;
+    }
+
+    .hero {
+        padding: var(--space-2xl) 0 var(--space-xl);
+    }
+
+    .hero-actions {
+        flex-direction: column;
+        align-items: stretch;
+    }
+
+    .features-grid,
+    .install-grid,
+    .examples-grid,
+    .performance-reasons,
+    .footer-grid {
+        grid-template-columns: minmax(0, 1fr);
     }
-}
 
-.fade-in-up {
-    animation: fadeInUp 0.6s ease-out;
+    section {
+        padding: var(--space-2xl) 0;
+    }
 }
 
-/* Focus Styles for Accessibility */
-a:focus,
-button:focus,
-.btn:focus {
-    outline: 2px solid var(--primary);
-    outline-offset: 2px;
+@media (max-width: 480px) {
+    .container,
+    .nav-container {
+        padding: 0 var(--space-md);
+    }
+
+    .code-content {
+        padding: var(--space-md);
+    }
 }
 
-/* Print Styles */
 @media print {
-    .navbar,
-    .footer {
+    .navbar, .footer, .scroll-top {
         display: none;
     }
-    
-    .main-content {
-        margin-top: 0;
-    }
-    
-    * {
-        background: white !important;
-        color: black !important;
-    }
-}
\ No newline at end of file
+}