summaryrefslogtreecommitdiff
path: root/docs/handbook/tomlplusplus
diff options
context:
space:
mode:
Diffstat (limited to 'docs/handbook/tomlplusplus')
-rw-r--r--docs/handbook/tomlplusplus/architecture.md920
-rw-r--r--docs/handbook/tomlplusplus/arrays.md625
-rw-r--r--docs/handbook/tomlplusplus/basic-usage.md705
-rw-r--r--docs/handbook/tomlplusplus/building.md474
-rw-r--r--docs/handbook/tomlplusplus/code-style.md277
-rw-r--r--docs/handbook/tomlplusplus/formatting.md546
-rw-r--r--docs/handbook/tomlplusplus/node-system.md625
-rw-r--r--docs/handbook/tomlplusplus/overview.md474
-rw-r--r--docs/handbook/tomlplusplus/parsing.md494
-rw-r--r--docs/handbook/tomlplusplus/path-system.md412
-rw-r--r--docs/handbook/tomlplusplus/tables.md551
-rw-r--r--docs/handbook/tomlplusplus/testing.md226
-rw-r--r--docs/handbook/tomlplusplus/unicode-handling.md335
-rw-r--r--docs/handbook/tomlplusplus/values.md547
14 files changed, 7211 insertions, 0 deletions
diff --git a/docs/handbook/tomlplusplus/architecture.md b/docs/handbook/tomlplusplus/architecture.md
new file mode 100644
index 0000000000..8ba979d200
--- /dev/null
+++ b/docs/handbook/tomlplusplus/architecture.md
@@ -0,0 +1,920 @@
+# toml++ — Architecture
+
+## Overview
+
+toml++ implements a tree-based data model for TOML documents. A parsed TOML document becomes a tree of `toml::node` objects, with `toml::table` as the root. The architecture centers on:
+
+1. A polymorphic node hierarchy (`node` → `table`, `array`, `value<T>`)
+2. A recursive-descent parser that builds trees
+3. Formatter classes that serialize trees back to text
+4. A path system for structured navigation
+
+All public types live in the `toml` namespace. Internal implementation details live in `toml::impl` (an ABI-namespaced detail namespace).
+
+---
+
+## Class Hierarchy
+
+```
+toml::node (abstract base)
+├── toml::table — ordered map of key → node*
+├── toml::array — vector of node*
+└── toml::value<T> — leaf node holding a value
+ ├── value<std::string>
+ ├── value<int64_t>
+ ├── value<double>
+ ├── value<bool>
+ ├── value<toml::date>
+ ├── value<toml::time>
+ └── value<toml::date_time>
+```
+
+Supporting types:
+```
+toml::node_view<T> — non-owning optional reference to a node
+toml::key — string + source_region metadata
+toml::path — vector of path_component
+toml::path_component — key string or array index
+toml::source_position — line + column
+toml::source_region — begin + end positions + path
+toml::parse_error — error description + source_region
+toml::parse_result — table | parse_error (no-exceptions mode)
+```
+
+Formatter hierarchy:
+```
+impl::formatter (base, protected)
+├── toml::toml_formatter — TOML output
+├── toml::json_formatter — JSON output
+└── toml::yaml_formatter — YAML output
+```
+
+---
+
+## `toml::node` — The Abstract Base Class
+
+Defined in `include/toml++/impl/node.hpp`, `toml::node` is the polymorphic base of all TOML tree nodes. It is declared as `TOML_ABSTRACT_INTERFACE`, meaning it has pure virtual methods and cannot be instantiated directly.
+
+### Private Members
+
+```cpp
+class node
+{
+ private:
+ source_region source_{};
+
+ template <typename T>
+ decltype(auto) get_value_exact() const noexcept(...);
+
+ // ref_type_ and ref_type — template aliases for ref() return types
+ // do_ref() — static helper for ref() implementation
+```
+
+The `source_` member records where this node was defined in the original TOML document (line, column, file path).
+
+### Protected Members
+
+```cpp
+ protected:
+ node() noexcept;
+ node(const node&) noexcept;
+ node(node&&) noexcept;
+ node& operator=(const node&) noexcept;
+ node& operator=(node&&) noexcept;
+
+ // ref_cast<T>() — unsafe downcast helpers (all four ref-qualifications)
+ template <typename T> ref_cast_type<T, node&> ref_cast() & noexcept;
+ template <typename T> ref_cast_type<T, node&&> ref_cast() && noexcept;
+ template <typename T> ref_cast_type<T, const node&> ref_cast() const& noexcept;
+ template <typename T> ref_cast_type<T, const node&&> ref_cast() const&& noexcept;
+```
+
+Constructors and assignment operators are `protected` to prevent direct instantiation. `ref_cast<T>()` performs `reinterpret_cast`-based downcasts, used internally by `ref<T>()`.
+
+### Public Interface — Type Checks
+
+Every `node` provides a complete set of virtual type-checking methods:
+
+```cpp
+ public:
+ virtual ~node() noexcept;
+
+ // Homogeneity checks
+ virtual bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept = 0;
+ virtual bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept = 0;
+ virtual bool is_homogeneous(node_type ntype) const noexcept = 0;
+ template <typename ElemType = void>
+ bool is_homogeneous() const noexcept;
+
+ // Type identity
+ virtual node_type type() const noexcept = 0;
+ virtual bool is_table() const noexcept = 0;
+ virtual bool is_array() const noexcept = 0;
+ virtual bool is_array_of_tables() const noexcept;
+ virtual bool is_value() const noexcept = 0;
+ virtual bool is_string() const noexcept = 0;
+ virtual bool is_integer() const noexcept = 0;
+ virtual bool is_floating_point() const noexcept = 0;
+ virtual bool is_number() const noexcept = 0;
+ virtual bool is_boolean() const noexcept = 0;
+ virtual bool is_date() const noexcept = 0;
+ virtual bool is_time() const noexcept = 0;
+ virtual bool is_date_time() const noexcept = 0;
+
+ // Template type check
+ template <typename T>
+ bool is() const noexcept;
+```
+
+The `is<T>()` template dispatches to the appropriate virtual method using `if constexpr`:
+
+```cpp
+template <typename T>
+bool is() const noexcept
+{
+ using type = impl::remove_cvref<impl::unwrap_node<T>>;
+ if constexpr (std::is_same_v<type, table>)
+ return is_table();
+ else if constexpr (std::is_same_v<type, array>)
+ return is_array();
+ else if constexpr (std::is_same_v<type, std::string>)
+ return is_string();
+ // ... etc for int64_t, double, bool, date, time, date_time
+}
+```
+
+### Public Interface — Type Casts
+
+```cpp
+ // Downcasts — return nullptr if type doesn't match
+ virtual table* as_table() noexcept = 0;
+ virtual array* as_array() noexcept = 0;
+ virtual toml::value<std::string>* as_string() noexcept = 0;
+ virtual toml::value<int64_t>* as_integer() noexcept = 0;
+ virtual toml::value<double>* as_floating_point() noexcept = 0;
+ virtual toml::value<bool>* as_boolean() noexcept = 0;
+ virtual toml::value<date>* as_date() noexcept = 0;
+ virtual toml::value<time>* as_time() noexcept = 0;
+ virtual toml::value<date_time>* as_date_time() noexcept = 0;
+ // + const overloads for all of the above
+
+ // Template downcast
+ template <typename T>
+ impl::wrap_node<T>* as() noexcept;
+ template <typename T>
+ const impl::wrap_node<T>* as() const noexcept;
+```
+
+`as<T>()` is the unified template that dispatches to `as_table()`, `as_string()`, etc.
+
+### Public Interface — Value Retrieval
+
+```cpp
+ // Exact-match value retrieval
+ template <typename T>
+ optional<T> value_exact() const noexcept(...);
+
+ // Permissive value retrieval (allows conversions)
+ template <typename T>
+ optional<T> value() const noexcept(...);
+
+ // Value with default
+ template <typename T>
+ auto value_or(T&& default_value) const noexcept(...);
+```
+
+`value_exact<T>()` only succeeds if the node contains exactly type `T`. `value<T>()` is more lenient, allowing integer-to-float conversions and the like. `value_or()` returns the value if present, otherwise the given default.
+
+### Public Interface — Reference Access
+
+```cpp
+ template <typename T>
+ decltype(auto) ref() & noexcept;
+ template <typename T>
+ decltype(auto) ref() && noexcept;
+ template <typename T>
+ decltype(auto) ref() const& noexcept;
+ template <typename T>
+ decltype(auto) ref() const&& noexcept;
+```
+
+`ref<T>()` provides direct reference access to the underlying value. It asserts the type matches and is UB if it doesn't.
+
+### Public Interface — Visitation
+
+```cpp
+ template <typename Func>
+ decltype(auto) visit(Func&& visitor) & noexcept(...);
+ // + &&, const&, const&& overloads
+```
+
+Calls the visitor with the concrete node type. The visitor receives the actual `table&`, `array&`, or `value<T>&`.
+
+### Source Region
+
+```cpp
+ const source_region& source() const noexcept;
+```
+
+Returns where this node was defined in the source document.
+
+---
+
+## `toml::table` — TOML Tables
+
+Declared in `include/toml++/impl/table.hpp`, `toml::table` extends `node` and models an ordered map of keys to nodes.
+
+### Internal Storage
+
+```cpp
+class table : public node
+{
+ private:
+ using map_type = std::map<toml::key, impl::node_ptr, std::less<>>;
+ map_type map_;
+ bool inline_ = false;
+```
+
+- The backing container is `std::map<toml::key, std::unique_ptr<node>, std::less<>>`.
+- `std::less<>` enables heterogeneous lookup (search by `std::string_view` without constructing a `key`).
+- `inline_` tracks whether the table should be serialized as an inline table `{ ... }`.
+- Insertion order is maintained because `toml::key` provides comparison operators that match `std::map`'s ordered semantics.
+
+### Iterators
+
+The table uses custom `impl::table_iterator<IsConst>` which wraps the map iterator and produces `table_proxy_pair<IsConst>` references:
+
+```cpp
+template <bool IsConst>
+struct table_proxy_pair
+{
+ using value_type = std::conditional_t<IsConst, const node, node>;
+ const toml::key& first;
+ value_type& second;
+};
+```
+
+This means iterating a table yields `(const key&, node&)` pairs, not `(const key&, unique_ptr<node>&)`. The `unique_ptr` layer is hidden.
+
+Public type aliases:
+```cpp
+using table_iterator = impl::table_iterator<false>;
+using const_table_iterator = impl::table_iterator<true>;
+```
+
+### Construction
+
+```cpp
+table() noexcept; // default
+table(const table&); // deep copy
+table(table&& other) noexcept; // move
+explicit table(std::initializer_list<impl::table_init_pair> kvps);
+```
+
+The initializer-list constructor accepts `table_init_pair` objects, each containing a key and a value:
+
+```cpp
+struct table_init_pair
+{
+ mutable toml::key key;
+ mutable node_ptr value; // std::unique_ptr<node>
+
+ template <typename K, typename V>
+ table_init_pair(K&& k, V&& v, value_flags flags = preserve_source_value_flags);
+};
+```
+
+This enables the idiomatic construction:
+```cpp
+auto tbl = toml::table{
+ { "name", "toml++" },
+ { "version", 3 },
+ { "nested", toml::table{ { "key", true } } }
+};
+```
+
+### Key Operations
+
+| Method | Description |
+|--------|-------------|
+| `size()` | Number of key-value pairs |
+| `empty()` | Whether the table is empty |
+| `get(key)` | Get node pointer by key, or nullptr |
+| `get_as<T>(key)` | Get typed node pointer, or nullptr |
+| `contains(key)` | Check if key exists |
+| `operator[](key)` | Returns `node_view` (safe, never null-deref) |
+| `at(key)` | Returns node reference, throws if missing |
+| `insert(key, val)` | Insert if not present |
+| `insert_or_assign(key, val)` | Insert or replace |
+| `emplace<T>(key, args...)` | Construct in place if not present |
+| `erase(key)` | Remove by key |
+| `erase(iterator)` | Remove by iterator |
+| `clear()` | Remove all entries |
+
+### Metadata
+
+```cpp
+bool is_inline() const noexcept;
+void is_inline(bool val) noexcept;
+```
+
+Controls inline table formatting: `{ a = 1, b = 2 }` vs. multi-line.
+
+---
+
+## `toml::array` — TOML Arrays
+
+Declared in `include/toml++/impl/array.hpp`, `toml::array` extends `node` and models a heterogeneous sequence.
+
+### Internal Storage
+
+```cpp
+class array : public node
+{
+ private:
+ std::vector<impl::node_ptr> elems_;
+```
+
+Each element is a `std::unique_ptr<node>`. The array can contain any mix of value types, tables, and nested arrays.
+
+### Iterators
+
+`impl::array_iterator<IsConst>` wraps the vector iterator and dereferences the `unique_ptr`, yielding `node&` references:
+
+```cpp
+using array_iterator = impl::array_iterator<false>;
+using const_array_iterator = impl::array_iterator<true>;
+```
+
+It satisfies `RandomAccessIterator` requirements (unlike `table_iterator` which is `BidirectionalIterator`).
+
+### Key Operations
+
+| Method | Description |
+|--------|-------------|
+| `size()` | Number of elements |
+| `empty()` | Whether the array is empty |
+| `capacity()` | Reserved capacity |
+| `reserve(n)` | Reserve capacity |
+| `shrink_to_fit()` | Release excess capacity |
+| `operator[](index)` | Returns `node&` (no bounds check) |
+| `at(index)` | Returns `node&` (bounds-checked, throws) |
+| `front()` / `back()` | First / last element |
+| `get(index)` | Returns `node*` or nullptr |
+| `get_as<T>(index)` | Returns typed pointer or nullptr |
+| `push_back(val)` | Append element |
+| `emplace_back<T>(args...)` | Construct at end |
+| `insert(pos, val)` | Insert at position |
+| `emplace(pos, args...)` | Construct at position |
+| `erase(pos)` | Remove at position |
+| `erase(first, last)` | Remove range |
+| `pop_back()` | Remove last |
+| `clear()` | Remove all |
+| `resize(n)` | Resize (default-constructed elements) |
+| `truncate(n)` | Remove elements beyond index n |
+| `flatten()` | Flatten nested arrays |
+| `prune()` | Remove empty tables and arrays recursively |
+
+### Homogeneity
+
+```cpp
+bool is_homogeneous(node_type ntype) const noexcept;
+bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept;
+template <typename ElemType = void>
+bool is_homogeneous() const noexcept;
+```
+
+Returns `true` if all elements are the same type. Returns `false` for empty arrays.
+
+### for_each
+
+```cpp
+template <typename Func>
+array& for_each(Func&& visitor) & noexcept(...);
+```
+
+Iterates elements, calling the visitor with each concrete element type. Supports early exit by returning `bool` from the visitor (on compilers without the GCC 7 bug).
+
+---
+
+## `toml::value<T>` — Leaf Values
+
+Declared in `include/toml++/impl/value.hpp`, `toml::value<T>` is a class template holding a single TOML value.
+
+### Template Parameter Constraints
+
+`T` must be one of the native TOML value types:
+- `std::string`
+- `int64_t`
+- `double`
+- `bool`
+- `toml::date`
+- `toml::time`
+- `toml::date_time`
+
+```cpp
+template <typename ValueType>
+class value : public node
+{
+ static_assert(impl::is_native<ValueType> && !impl::is_cvref<ValueType>);
+
+ private:
+ ValueType val_;
+ value_flags flags_ = value_flags::none;
+```
+
+### Type Aliases
+
+```cpp
+using value_type = ValueType;
+using value_arg = /* conditional type */;
+```
+
+`value_arg` differs by value type:
+- `int64_t`, `double`, `bool` → passed by value
+- `std::string` → passed as `std::string_view`
+- `date`, `time`, `date_time` → passed as `const value_type&`
+
+### Key Operations
+
+```cpp
+// Access the underlying value
+ValueType& get() & noexcept;
+ValueType&& get() && noexcept;
+const ValueType& get() const& noexcept;
+const ValueType&& get() const&& noexcept;
+
+// Implicit conversion operator
+operator ValueType&() noexcept;
+operator const ValueType&() const noexcept;
+
+// Value flags (integer format: binary, octal, hex)
+value_flags flags() const noexcept;
+value<ValueType>& flags(value_flags new_flags) noexcept;
+```
+
+### Construction
+
+`value<T>` supports variadic construction via `impl::native_value_maker`:
+
+```cpp
+template <typename... Args>
+explicit value(Args&&... args);
+
+value(const value& other) noexcept;
+value(const value& other, value_flags flags) noexcept;
+value(value&& other) noexcept;
+value(value&& other, value_flags flags) noexcept;
+```
+
+Special handling exists for:
+- `char8_t` strings (C++20): converted via `reinterpret_cast`
+- Wide strings (Windows): narrowed via `impl::narrow()`
+
+---
+
+## Date/Time Types
+
+Defined in `include/toml++/impl/date_time.hpp`:
+
+### `toml::date`
+
+```cpp
+struct date
+{
+ uint16_t year;
+ uint8_t month; // 1-12
+ uint8_t day; // 1-31
+
+ constexpr date(Y y, M m, D d) noexcept;
+ // Comparison operators: ==, !=, <, <=, >, >=
+ // Stream output: YYYY-MM-DD
+};
+```
+
+### `toml::time`
+
+```cpp
+struct time
+{
+ uint8_t hour; // 0-23
+ uint8_t minute; // 0-59
+ uint8_t second; // 0-59
+ uint32_t nanosecond; // 0-999999999
+
+ constexpr time(H h, M m, S s = 0, NS ns = 0) noexcept;
+ // Comparison operators, stream output: HH:MM:SS.nnnnnnnnn
+};
+```
+
+### `toml::time_offset`
+
+```cpp
+struct time_offset
+{
+ int16_t minutes; // -1440 to +1440
+
+ constexpr time_offset(H h, M m) noexcept;
+ // Comparison operators, stream output: +HH:MM or -HH:MM
+};
+```
+
+### `toml::date_time`
+
+```cpp
+struct date_time
+{
+ toml::date date;
+ toml::time time;
+ optional<toml::time_offset> offset;
+
+ // If offset is present, it's an offset date-time
+ // If offset is absent, it's a local date-time
+ bool is_local() const noexcept;
+};
+```
+
+---
+
+## `toml::key` — Table Keys
+
+Defined in `include/toml++/impl/key.hpp`:
+
+```cpp
+class key
+{
+ private:
+ std::string key_;
+ source_region source_;
+
+ public:
+ explicit key(std::string_view k, source_region&& src = {});
+ explicit key(std::string&& k, source_region&& src = {}) noexcept;
+ explicit key(const char* k, source_region&& src = {});
+
+ // String access
+ std::string_view str() const noexcept;
+ operator std::string_view() const noexcept;
+ bool empty() const noexcept;
+
+ // Source tracking
+ const source_region& source() const noexcept;
+
+ // Comparison — compares the string content, not source position
+ friend bool operator==(const key& lhs, const key& rhs) noexcept;
+ friend bool operator<(const key& lhs, const key& rhs) noexcept;
+ // + heterogeneous comparisons with std::string_view
+};
+```
+
+Keys carry source position metadata from parsing, but comparisons only consider the string content.
+
+---
+
+## Parser Design
+
+The parser is a recursive-descent UTF-8 parser implemented in `impl::parser` (defined in `parser.inl`). It operates on a stream of UTF-8 codepoints.
+
+### Parse Entry Points
+
+```cpp
+// Defined in parser.hpp
+parse_result parse(std::string_view doc, std::string_view source_path = {});
+parse_result parse(std::string_view doc, std::string&& source_path);
+parse_result parse_file(std::string_view file_path);
+
+// Stream overloads
+parse_result parse(std::istream& doc, std::string_view source_path = {});
+```
+
+### Return Type: `parse_result`
+
+When exceptions are enabled (`TOML_EXCEPTIONS=1`):
+```cpp
+using parse_result = table; // parse() throws on error
+```
+
+When exceptions are disabled (`TOML_EXCEPTIONS=0`):
+```cpp
+class parse_result // discriminated union: table or parse_error
+{
+ bool err_;
+ union { toml::table; parse_error; } storage_;
+
+ public:
+ explicit operator bool() const noexcept; // true = success
+ table& table() &;
+ parse_error& error() &;
+ // + iterator accessors for safe ranged-for on failure
+};
+```
+
+### Error Type
+
+```cpp
+class parse_error /* : public std::runtime_error (with exceptions) */
+{
+ std::string_view description() const noexcept;
+ const source_region& source() const noexcept;
+};
+```
+
+### Parser Internals
+
+The `impl::parser` class stores:
+- A UTF-8 byte stream reader
+- A source position tracker
+- The root table being built
+- The current implicit table stack (for dotted keys and `[section.headers]`)
+
+Parsing proceeds top-down:
+1. Skip BOM if present
+2. Parse key-value pairs, table headers (`[table]`), and array-of-tables headers (`[[array]]`)
+3. For each key-value pair, parse the key (bare or quoted), then the value
+4. Values are parsed based on the leading character(s): strings, numbers, booleans, dates/times, arrays, inline tables
+
+Node allocation uses `impl::make_node()` which dispatches through `impl::make_node_impl_specialized()`:
+```cpp
+template <typename T>
+auto* make_node_impl_specialized(T&& val, value_flags flags)
+{
+ using unwrapped_type = unwrap_node<remove_cvref<T>>;
+ if constexpr (is_one_of<unwrapped_type, array, table>)
+ return new unwrapped_type(static_cast<T&&>(val));
+ else
+ {
+ using native_type = native_type_of<unwrapped_type>;
+ using value_type = value<native_type>;
+ return new value_type{ static_cast<T&&>(val) };
+ }
+}
+```
+
+---
+
+## Formatter Design
+
+### Base Class: `impl::formatter`
+
+Defined in `include/toml++/impl/formatter.hpp`:
+
+```cpp
+class formatter
+{
+ private:
+ const node* source_;
+ const parse_result* result_; // for no-exceptions mode
+ const formatter_constants* constants_;
+ formatter_config config_;
+ size_t indent_columns_;
+ format_flags int_format_mask_;
+ std::ostream* stream_;
+ int indent_;
+ bool naked_newline_;
+
+ protected:
+ // Stream management
+ void attach(std::ostream& stream) noexcept;
+ void detach() noexcept;
+
+ // Output primitives
+ void print_newline(bool force = false);
+ void print_indent();
+ void print_unformatted(char);
+ void print_unformatted(std::string_view);
+
+ // Typed printing
+ void print_string(std::string_view str, bool allow_multi_line, bool allow_bare, bool allow_literal_whitespace);
+ void print(const value<std::string>&);
+ void print(const value<int64_t>&);
+ void print(const value<double>&);
+ void print(const value<bool>&);
+ void print(const value<date>&);
+ void print(const value<time>&);
+ void print(const value<date_time>&);
+ void print_value(const node&, node_type);
+
+ // Configuration queries
+ bool indent_array_elements() const noexcept;
+ bool indent_sub_tables() const noexcept;
+ bool literal_strings_allowed() const noexcept;
+ bool multi_line_strings_allowed() const noexcept;
+ bool unicode_strings_allowed() const noexcept;
+ bool terse_kvps() const noexcept;
+ bool force_multiline_arrays() const noexcept;
+};
+```
+
+### Formatter Constants
+
+Each concrete formatter defines its behavior via `formatter_constants`:
+
+```cpp
+struct formatter_constants
+{
+ format_flags mandatory_flags; // always-on flags
+ format_flags ignored_flags; // always-off flags
+ std::string_view float_pos_inf; // e.g., "inf", "Infinity", ".inf"
+ std::string_view float_neg_inf;
+ std::string_view float_nan;
+ std::string_view bool_true;
+ std::string_view bool_false;
+};
+```
+
+| Formatter | pos_inf | neg_inf | nan | bool_true | bool_false |
+|-----------|---------|---------|-----|-----------|------------|
+| `toml_formatter` | `"inf"` | `"-inf"` | `"nan"` | `"true"` | `"false"` |
+| `json_formatter` | `"Infinity"` | `"-Infinity"` | `"NaN"` | `"true"` | `"false"` |
+| `yaml_formatter` | `".inf"` | `"-.inf"` | `".NAN"` | `"true"` | `"false"` |
+
+### `toml::toml_formatter`
+
+Inherits `impl::formatter`. Serializes to valid TOML format.
+
+Key behaviors:
+- Tracks a `key_path_` vector for producing `[table.paths]`
+- Manages `pending_table_separator_` for blank lines between sections
+- Uses 4-space indentation by default (`" "sv`)
+- Respects `is_inline()` on tables
+- Default flags include all `allow_*` flags and `indentation`
+
+### `toml::json_formatter`
+
+Outputs valid JSON. Key differences from TOML formatter:
+- Mandatory `quote_dates_and_times` (dates become quoted strings)
+- Ignores `allow_literal_strings` and `allow_multi_line_strings`
+- Default includes `quote_infinities_and_nans`
+- Uses 4-space indentation
+
+### `toml::yaml_formatter`
+
+Outputs YAML. Key differences:
+- Uses 2-space indentation (`" "sv`)
+- Mandatory `quote_dates_and_times` and `indentation`
+- Ignores `allow_multi_line_strings`
+- Custom string printing via `print_yaml_string()`
+- YAML-style inf/nan representation (`.inf`, `-.inf`, `.NAN`)
+
+### Streaming Pattern
+
+All formatters use the same attach/print/detach pattern:
+
+```cpp
+friend std::ostream& operator<<(std::ostream& lhs, toml_formatter& rhs)
+{
+ rhs.attach(lhs);
+ rhs.key_path_.clear(); // (toml_formatter specific)
+ rhs.print();
+ rhs.detach();
+ return lhs;
+}
+```
+
+---
+
+## `toml::node_view<T>` — Safe Node References
+
+Defined in `include/toml++/impl/node_view.hpp`:
+
+```cpp
+template <typename ViewedType>
+class node_view
+{
+ static_assert(impl::is_one_of<ViewedType, toml::node, const toml::node>);
+
+ private:
+ mutable viewed_type* node_ = nullptr;
+
+ public:
+ using viewed_type = ViewedType;
+
+ node_view() noexcept = default;
+ explicit node_view(viewed_type* node) noexcept;
+ explicit node_view(viewed_type& node) noexcept;
+
+ explicit operator bool() const noexcept;
+ viewed_type* node() const noexcept;
+```
+
+`node_view` wraps a pointer to a `node` (or `const node`) and provides the same interface as `node` — type checks, casts, value retrieval — but safely handles null (returns empty optionals, false booleans, empty views on subscript).
+
+The key design feature is chainable subscript operators:
+
+```cpp
+node_view operator[](std::string_view key) const noexcept;
+node_view operator[](size_t index) const noexcept;
+node_view operator[](const path& p) const noexcept;
+```
+
+These return empty views when the key/index doesn't exist, so you can chain deeply without null checks:
+
+```cpp
+auto val = tbl["section"]["subsection"]["key"].value_or(42);
+// Safe even if any intermediate node is missing
+```
+
+---
+
+## Source Tracking
+
+### `toml::source_position`
+
+```cpp
+struct source_position
+{
+ source_index line; // 1-based
+ source_index column; // 1-based
+
+ explicit constexpr operator bool() const noexcept;
+ // Comparison operators
+};
+```
+
+### `toml::source_region`
+
+```cpp
+struct source_region
+{
+ source_position begin;
+ source_position end;
+ source_path_ptr path; // std::shared_ptr<const std::string>
+};
+```
+
+Every node carries a `source_region` accessible via `node::source()`. For programmatically-constructed nodes, the source region is default (zeroed). For parsed nodes, it tracks the exact file location.
+
+---
+
+## Ownership Model
+
+The ownership model is straightforward:
+- `toml::table` owns its child nodes via `std::map<key, std::unique_ptr<node>>`
+- `toml::array` owns its child nodes via `std::vector<std::unique_ptr<node>>`
+- `toml::value<T>` owns its stored value directly (by value)
+- `toml::node_view<T>` is non-owning (raw pointer)
+- `toml::parse_result` owns either a `table` or a `parse_error` (union storage)
+
+Copying a `table` or `array` performs a deep copy of the entire subtree. Moving transfers ownership with no allocation.
+
+---
+
+## Thread Safety
+
+toml++ does not provide internal synchronization. A parsed `table` tree can be read concurrently from multiple threads, but modification requires external synchronization. The parser itself is not re-entrant — each call to `parse()` creates a new internal parser instance.
+
+---
+
+## Memory Allocation
+
+All heap allocation uses the global `operator new` (no custom allocators). Nodes are individually heap-allocated even in arrays. The `make_node.hpp` factory always calls `new`:
+
+```cpp
+return new unwrapped_type(static_cast<T&&>(val));
+// or
+return new value_type{ static_cast<T&&>(val) };
+```
+
+This makes the library simple but means that large TOML documents with many small values will create many small allocations.
+
+---
+
+## ABI Namespaces
+
+toml++ uses conditional inline namespaces to prevent ODR violations when mixing translation units compiled with different configuration macros:
+
+```cpp
+TOML_ABI_NAMESPACE_BOOL(TOML_EXCEPTIONS, ex, noex);
+// Creates either:
+// namespace ex { ... } // when TOML_EXCEPTIONS == 1
+// namespace noex { ... } // when TOML_EXCEPTIONS == 0
+
+TOML_ABI_NAMESPACE_BOOL(TOML_HAS_CUSTOM_OPTIONAL_TYPE, custopt, stdopt);
+```
+
+This means `toml::ex::parse_result` (exceptions enabled) and `toml::noex::parse_result` (exceptions disabled) are different types, preventing linker errors if you accidentally mix them.
+
+---
+
+## Conditional Compilation
+
+Large portions of the library can be compiled out:
+
+| Macro | What it removes |
+|-------|----------------|
+| `TOML_ENABLE_PARSER=0` | All parsing functions, `parse_error`, `parse_result`, parser implementation |
+| `TOML_ENABLE_FORMATTERS=0` | All formatter classes, `format_flags` |
+| `TOML_HEADER_ONLY=0` | Switches to compiled mode (link against `toml.cpp`) |
+
+This enables minimal builds for projects that only need, say, programmatic TOML construction and serialization (no parsing).
+
+---
+
+## Related Documentation
+
+- [node-system.md](node-system.md) — Detailed node API reference
+- [tables.md](tables.md) — Table operations in depth
+- [arrays.md](arrays.md) — Array operations in depth
+- [values.md](values.md) — Value template details
+- [parsing.md](parsing.md) — Parser behavior and error handling
+- [formatting.md](formatting.md) — Formatter usage and customization
diff --git a/docs/handbook/tomlplusplus/arrays.md b/docs/handbook/tomlplusplus/arrays.md
new file mode 100644
index 0000000000..3e916392fc
--- /dev/null
+++ b/docs/handbook/tomlplusplus/arrays.md
@@ -0,0 +1,625 @@
+# toml++ — Arrays
+
+## Overview
+
+`toml::array` extends `toml::node` and models a heterogeneous, ordered sequence of TOML nodes. Unlike C++ standard containers that store elements of a single type, a TOML array can contain any mix of value types, sub-tables, and nested arrays.
+
+Declared in `include/toml++/impl/array.hpp` with implementation in `array.inl`.
+
+---
+
+## Internal Storage
+
+```cpp
+class array : public node
+{
+ private:
+ std::vector<impl::node_ptr> elems_;
+ // impl::node_ptr = std::unique_ptr<node>
+};
+```
+
+- Each element is owned via `std::unique_ptr<node>`
+- The array owns all child nodes — destruction cascades
+- Elements can be any `node` subclass: `value<T>`, `table`, or nested `array`
+
+---
+
+## Construction
+
+### Default Construction
+
+```cpp
+toml::array arr; // empty array
+```
+
+### Initializer List Construction
+
+```cpp
+auto arr = toml::array{ 1, 2, 3 }; // array of integers
+auto mixed = toml::array{ 1, "hello", 3.14, true }; // mixed types
+auto nested = toml::array{
+ toml::array{ 1, 2 },
+ toml::array{ 3, 4 }
+};
+
+// Array of tables (array-of-tables syntax in TOML)
+auto aot = toml::array{
+ toml::table{ { "name", "Alice" }, { "age", 30 } },
+ toml::table{ { "name", "Bob" }, { "age", 25 } }
+};
+```
+
+Values are converted to nodes via `impl::make_node()`:
+- `int`, `int64_t` → `value<int64_t>`
+- `double`, `float` → `value<double>`
+- `const char*`, `std::string` → `value<std::string>`
+- `bool` → `value<bool>`
+- `toml::date` → `value<date>`
+- `toml::time` → `value<time>`
+- `toml::date_time` → `value<date_time>`
+- `toml::table` → `table` (moved)
+- `toml::array` → `array` (moved)
+
+### Copy and Move
+
+```cpp
+toml::array copy(original); // deep copy — all elements cloned
+toml::array moved(std::move(arr)); // move — no allocation
+```
+
+Copy is recursive: nested tables and arrays are deep-copied.
+
+---
+
+## Iterators
+
+### Types
+
+```cpp
+using array_iterator = impl::array_iterator<false>;
+using const_array_iterator = impl::array_iterator<true>;
+```
+
+`array_iterator` is a **RandomAccessIterator** (unlike `table_iterator` which is Bidirectional). This means it supports arithmetic, comparison, and random access:
+
+```cpp
+auto it = arr.begin();
+it += 3; // jump forward 3
+ptrdiff_t diff = arr.end() - it; // distance
+bool less = it < arr.end(); // comparison
+```
+
+Dereferencing yields `node&` (the `unique_ptr` is hidden):
+
+```cpp
+for (auto it = arr.begin(); it != arr.end(); ++it)
+{
+ toml::node& elem = *it;
+ std::cout << elem << "\n";
+}
+```
+
+### Iterator Methods
+
+```cpp
+iterator begin() noexcept;
+iterator end() noexcept;
+const_iterator begin() const noexcept;
+const_iterator end() const noexcept;
+const_iterator cbegin() const noexcept;
+const_iterator cend() const noexcept;
+```
+
+### Range-Based For
+
+```cpp
+for (auto& elem : arr)
+{
+ std::cout << elem.type() << ": " << elem << "\n";
+}
+```
+
+---
+
+## Capacity
+
+```cpp
+size_t size() const noexcept; // number of elements
+bool empty() const noexcept; // true if size() == 0
+size_t capacity() const noexcept; // reserved capacity
+size_t max_size() const noexcept; // maximum possible size
+
+void reserve(size_t new_cap); // reserve capacity
+void shrink_to_fit(); // release excess capacity
+```
+
+---
+
+## Element Access
+
+### `operator[]` — Unchecked Index Access
+
+```cpp
+node& operator[](size_t index) noexcept;
+const node& operator[](size_t index) const noexcept;
+```
+
+No bounds checking. UB if `index >= size()`.
+
+```cpp
+auto arr = toml::array{ 10, 20, 30 };
+std::cout << arr[1].value_or(0) << "\n"; // 20
+```
+
+### `at()` — Bounds-Checked Access
+
+```cpp
+node& at(size_t index);
+const node& at(size_t index) const;
+```
+
+Throws `std::out_of_range` if `index >= size()`.
+
+### `front()` / `back()`
+
+```cpp
+node& front() noexcept;
+node& back() noexcept;
+const node& front() const noexcept;
+const node& back() const noexcept;
+```
+
+### `get()` — Pointer Access
+
+```cpp
+node* get(size_t index) noexcept;
+const node* get(size_t index) const noexcept;
+```
+
+Returns `nullptr` if out of bounds (safe alternative to `operator[]`).
+
+### `get_as<T>()` — Typed Pointer Access
+
+```cpp
+template <typename T>
+impl::wrap_node<T>* get_as(size_t index) noexcept;
+```
+
+Returns a typed pointer if the element at `index` exists and matches type `T`:
+
+```cpp
+auto arr = toml::array{ "hello", 42, true };
+
+if (auto* s = arr.get_as<std::string>(0))
+ std::cout << "String: " << s->get() << "\n";
+
+if (auto* i = arr.get_as<int64_t>(1))
+ std::cout << "Integer: " << i->get() << "\n";
+```
+
+---
+
+## Insertion
+
+### `push_back()` — Append
+
+```cpp
+template <typename T>
+decltype(auto) push_back(T&& val, value_flags flags = preserve_source_value_flags);
+```
+
+Appends a new element to the end:
+
+```cpp
+arr.push_back(42);
+arr.push_back("hello");
+arr.push_back(toml::table{ { "key", "value" } });
+arr.push_back(toml::array{ 1, 2, 3 });
+```
+
+Returns a reference to the inserted node.
+
+### `emplace_back<T>()` — Construct at End
+
+```cpp
+template <typename T, typename... Args>
+decltype(auto) emplace_back(Args&&... args);
+```
+
+```cpp
+arr.emplace_back<std::string>("constructed in place");
+arr.emplace_back<int64_t>(42);
+arr.emplace_back<toml::table>(); // empty table
+```
+
+### `insert()` — Insert at Position
+
+```cpp
+template <typename T>
+iterator insert(const_iterator pos, T&& val, value_flags flags = preserve_source_value_flags);
+```
+
+```cpp
+arr.insert(arr.begin(), "first"); // insert at front
+arr.insert(arr.begin() + 2, 42); // insert at index 2
+arr.insert(arr.end(), toml::array{1,2}); // same as push_back
+```
+
+### `emplace()` — Construct at Position
+
+```cpp
+template <typename T, typename... Args>
+iterator emplace(const_iterator pos, Args&&... args);
+```
+
+```cpp
+arr.emplace<std::string>(arr.begin(), "inserted string");
+```
+
+---
+
+## Removal
+
+### `pop_back()`
+
+```cpp
+void pop_back() noexcept;
+```
+
+Removes the last element.
+
+### `erase()` — By Iterator
+
+```cpp
+iterator erase(const_iterator pos) noexcept;
+iterator erase(const_iterator first, const_iterator last) noexcept;
+```
+
+```cpp
+arr.erase(arr.begin()); // remove first
+arr.erase(arr.begin(), arr.begin() + 3); // remove first 3
+```
+
+### `clear()`
+
+```cpp
+void clear() noexcept;
+```
+
+Removes all elements.
+
+### `resize()`
+
+```cpp
+void resize(size_t new_size, T&& default_value, value_flags flags = preserve_source_value_flags);
+```
+
+If `new_size > size()`, appends copies of `default_value`. If `new_size < size()`, truncates.
+
+### `truncate()`
+
+```cpp
+void truncate(size_t new_size);
+```
+
+Removes elements beyond `new_size`. If `new_size >= size()`, does nothing.
+
+---
+
+## Array Transformation
+
+### `flatten()` — Flatten Nested Arrays
+
+```cpp
+array& flatten() &;
+array&& flatten() &&;
+```
+
+Recursively flattens nested arrays into a single-level array:
+
+```cpp
+auto arr = toml::array{
+ 1,
+ toml::array{ 2, 3 },
+ toml::array{ toml::array{ 4, 5 }, 6 }
+};
+
+arr.flatten();
+// arr is now: [1, 2, 3, 4, 5, 6]
+```
+
+Tables within nested arrays are **not** flattened — only arrays are unwrapped.
+
+### `prune()` — Remove Empty Containers
+
+```cpp
+array& prune(bool recursive = true) &;
+array&& prune(bool recursive = true) &&;
+```
+
+Removes empty tables and empty arrays. If `recursive` is true, prunes nested containers first, then removes them if they became empty:
+
+```cpp
+auto arr = toml::array{
+ 1,
+ toml::table{}, // empty table
+ toml::array{}, // empty array
+ toml::array{ toml::table{} } // array containing empty table
+};
+
+arr.prune();
+// arr is now: [1]
+```
+
+---
+
+## Homogeneity
+
+### Checking Type Uniformity
+
+```cpp
+bool is_homogeneous(node_type ntype) const noexcept;
+bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept;
+bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept;
+
+template <typename ElemType = void>
+bool is_homogeneous() const noexcept;
+```
+
+```cpp
+auto ints = toml::array{ 1, 2, 3 };
+auto mixed = toml::array{ 1, "two", 3.0 };
+
+ints.is_homogeneous<int64_t>(); // true
+ints.is_homogeneous<double>(); // false
+ints.is_homogeneous(); // true (all same type)
+
+mixed.is_homogeneous(); // false
+mixed.is_homogeneous(toml::node_type::none); // false
+
+// Find first mismatch:
+toml::node* bad = nullptr;
+mixed.is_homogeneous(toml::node_type::integer, bad);
+// bad points to the "two" string value
+```
+
+**Important:** Empty arrays return `false` for all homogeneity checks — they don't contain "any" type.
+
+### `is_array_of_tables()`
+
+```cpp
+bool is_array_of_tables() const noexcept;
+```
+
+Returns `true` only if the array is non-empty and every element is a `table`:
+
+```cpp
+auto aot = toml::array{
+ toml::table{ { "name", "Alice" } },
+ toml::table{ { "name", "Bob" } }
+};
+std::cout << aot.is_array_of_tables() << "\n"; // true
+
+auto mixed = toml::array{ toml::table{}, 42 };
+std::cout << mixed.is_array_of_tables() << "\n"; // false
+```
+
+---
+
+## `for_each()` — Type-Safe Iteration
+
+```cpp
+template <typename Func>
+array& for_each(Func&& visitor) &;
+template <typename Func>
+array&& for_each(Func&& visitor) &&;
+template <typename Func>
+const array& for_each(Func&& visitor) const&;
+```
+
+Iterates elements, calling the visitor with each element in its concrete type. The visitor can accept:
+- `(auto& element)` — element only
+- `(size_t index, auto& element)` — index + element
+
+```cpp
+auto arr = toml::array{ 1, "two", 3.0, true };
+
+arr.for_each([](size_t idx, auto& elem)
+{
+ using T = std::remove_cvref_t<decltype(elem)>;
+
+ std::cout << "[" << idx << "] ";
+
+ if constexpr (toml::is_integer<T>)
+ std::cout << "int: " << elem.get() << "\n";
+ else if constexpr (toml::is_string<T>)
+ std::cout << "string: " << elem.get() << "\n";
+ else if constexpr (toml::is_floating_point<T>)
+ std::cout << "float: " << elem.get() << "\n";
+ else if constexpr (toml::is_boolean<T>)
+ std::cout << "bool: " << elem.get() << "\n";
+});
+```
+
+Output:
+```
+[0] int: 1
+[1] string: two
+[2] float: 3
+[3] bool: true
+```
+
+### Early Exit (Non-GCC-7)
+
+On supported compilers, returning `bool` from the visitor enables early termination:
+
+```cpp
+arr.for_each([](auto& elem) -> bool
+{
+ if constexpr (toml::is_string<decltype(elem)>)
+ {
+ std::cout << "Found string: " << elem.get() << "\n";
+ return false; // stop
+ }
+ return true; // continue
+});
+```
+
+---
+
+## Array of Tables (TOML `[[syntax]]`)
+
+In TOML, `[[array_name]]` defines an array of tables:
+
+```toml
+[[servers]]
+name = "alpha"
+ip = "10.0.0.1"
+
+[[servers]]
+name = "beta"
+ip = "10.0.0.2"
+```
+
+This parses to a `table` containing key `"servers"` → `array` → `[table, table]`.
+
+Accessing:
+```cpp
+auto tbl = toml::parse(/* above TOML */);
+
+if (auto* servers = tbl["servers"].as_array())
+{
+ for (auto& server_node : *servers)
+ {
+ auto* server = server_node.as_table();
+ if (server)
+ {
+ auto name = (*server)["name"].value_or(""sv);
+ auto ip = (*server)["ip"].value_or(""sv);
+ std::cout << name << " @ " << ip << "\n";
+ }
+ }
+}
+```
+
+Creating programmatically:
+```cpp
+auto tbl = toml::table{
+ { "servers", toml::array{
+ toml::table{ { "name", "alpha" }, { "ip", "10.0.0.1" } },
+ toml::table{ { "name", "beta" }, { "ip", "10.0.0.2" } }
+ }}
+};
+```
+
+---
+
+## Comparison
+
+### Equality
+
+```cpp
+friend bool operator==(const array& lhs, const array& rhs) noexcept;
+friend bool operator!=(const array& lhs, const array& rhs) noexcept;
+```
+
+Deep structural equality: same size, same element types, same values in order.
+
+---
+
+## Printing
+
+Arrays are streamable:
+
+```cpp
+auto arr = toml::array{ 1, 2, 3, "four" };
+std::cout << arr << "\n";
+// Output: [1, 2, 3, "four"]
+```
+
+The output format depends on the formatter. The default `toml_formatter` uses inline array syntax for simple arrays and multiline for arrays of tables.
+
+---
+
+## Type Identity
+
+```cpp
+node_type type() const noexcept final; // returns node_type::array
+bool is_table() const noexcept final; // returns false
+bool is_array() const noexcept final; // returns true
+bool is_value() const noexcept final; // returns false
+// ... all other is_*() return false
+
+array* as_array() noexcept final; // returns this
+const array* as_array() const noexcept final; // returns this
+// ... all other as_*() return nullptr
+```
+
+---
+
+## Complete Example
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+
+int main()
+{
+ // Build an array
+ toml::array fruits;
+ fruits.push_back("apple");
+ fruits.push_back("banana");
+ fruits.push_back("cherry");
+
+ // Insert at position
+ fruits.insert(fruits.begin() + 1, "blueberry");
+
+ // Iterate
+ for (size_t i = 0; i < fruits.size(); i++)
+ {
+ std::cout << i << ": " << fruits[i].value_or(""sv) << "\n";
+ }
+
+ // Check homogeneity
+ std::cout << "All strings? " << fruits.is_homogeneous<std::string>() << "\n";
+
+ // Flatten nested arrays
+ auto nested = toml::array{
+ toml::array{ 1, 2 },
+ toml::array{ 3, toml::array{ 4, 5 } }
+ };
+ nested.flatten();
+ std::cout << "Flattened: " << nested << "\n";
+
+ // Array of tables
+ auto servers = toml::array{
+ toml::table{ { "host", "alpha" }, { "port", 8080 } },
+ toml::table{ { "host", "beta" }, { "port", 8081 } }
+ };
+ std::cout << "Is array of tables? " << servers.is_array_of_tables() << "\n";
+
+ // for_each with type dispatch
+ auto mixed = toml::array{ 42, "hello", 3.14 };
+ mixed.for_each([](auto& elem)
+ {
+ if constexpr (toml::is_integer<decltype(elem)>)
+ std::cout << "int: " << elem.get() << "\n";
+ else if constexpr (toml::is_string<decltype(elem)>)
+ std::cout << "str: " << elem.get() << "\n";
+ else if constexpr (toml::is_floating_point<decltype(elem)>)
+ std::cout << "flt: " << elem.get() << "\n";
+ });
+
+ return 0;
+}
+```
+
+---
+
+## Related Documentation
+
+- [node-system.md](node-system.md) — Base node interface
+- [tables.md](tables.md) — Table container details
+- [values.md](values.md) — Leaf value details
+- [formatting.md](formatting.md) — Array formatting options
diff --git a/docs/handbook/tomlplusplus/basic-usage.md b/docs/handbook/tomlplusplus/basic-usage.md
new file mode 100644
index 0000000000..e11d47c42c
--- /dev/null
+++ b/docs/handbook/tomlplusplus/basic-usage.md
@@ -0,0 +1,705 @@
+# toml++ — Basic Usage
+
+## Including the Library
+
+The simplest way to start using toml++ is with the default header-only mode:
+
+```cpp
+#include <toml++/toml.hpp>
+```
+
+Or with the single-header drop-in:
+
+```cpp
+#include "toml.hpp"
+```
+
+The library places everything in the `toml` namespace. Most examples use the string literal namespace:
+
+```cpp
+using namespace std::string_view_literals; // for "..."sv
+```
+
+---
+
+## Parsing TOML
+
+### Parsing a String
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+
+int main()
+{
+ auto tbl = toml::parse(R"(
+ title = "My Config"
+
+ [database]
+ server = "192.168.1.1"
+ ports = [ 8001, 8001, 8002 ]
+ enabled = true
+ )");
+
+ std::cout << tbl << "\n";
+ return 0;
+}
+```
+
+`toml::parse()` accepts a `std::string_view` and returns a `toml::table` (when exceptions are enabled) or a `toml::parse_result` (when exceptions are disabled).
+
+### Parsing a File
+
+```cpp
+auto tbl = toml::parse_file("config.toml");
+```
+
+`toml::parse_file()` takes a file path as `std::string_view`, opens the file, and parses its contents.
+
+### Parsing from a Stream
+
+```cpp
+#include <fstream>
+
+std::ifstream file("config.toml");
+auto tbl = toml::parse(file, "config.toml");
+```
+
+The second argument is the source path used for error messages and `source()` metadata.
+
+### Parsing with Source Path
+
+You can provide a source path for diagnostic purposes:
+
+```cpp
+auto tbl = toml::parse(toml_string, "my_config.toml");
+```
+
+This path is stored in each node's `source().path` and appears in error messages.
+
+---
+
+## Error Handling
+
+### With Exceptions (Default)
+
+When `TOML_EXCEPTIONS` is enabled (the default if you don't disable them), `toml::parse()` and `toml::parse_file()` throw `toml::parse_error` on failure:
+
+```cpp
+try
+{
+ auto tbl = toml::parse_file("config.toml");
+}
+catch (const toml::parse_error& err)
+{
+ std::cerr << "Parse error: " << err.description() << "\n";
+ std::cerr << " at " << err.source() << "\n";
+ // err.source().begin.line, err.source().begin.column
+}
+```
+
+`toml::parse_error` inherits from `std::runtime_error` in this mode.
+
+### Without Exceptions
+
+When compiled with `TOML_EXCEPTIONS=0` (or with `-fno-exceptions`), `parse()` returns a `toml::parse_result`:
+
+```cpp
+toml::parse_result result = toml::parse_file("config.toml");
+
+if (result)
+{
+ // Success — result implicitly converts to table&
+ toml::table& tbl = result;
+ std::cout << tbl << "\n";
+}
+else
+{
+ // Failure
+ std::cerr << "Parse error: " << result.error().description() << "\n";
+ std::cerr << " at " << result.error().source() << "\n";
+}
+```
+
+`parse_result` is a discriminated union that holds either a `toml::table` or a `toml::parse_error`. It converts to `bool` for success checking.
+
+---
+
+## Accessing Values
+
+### Using `operator[]` — The Easy Way
+
+`operator[]` on a `toml::table` returns a `toml::node_view`, which is a safe optional-like wrapper:
+
+```cpp
+auto tbl = toml::parse(R"(
+ [server]
+ host = "localhost"
+ port = 8080
+ debug = false
+ tags = ["web", "api"]
+)");
+
+// Chained access — never throws, returns empty view if path doesn't exist
+auto host_view = tbl["server"]["host"];
+auto port_view = tbl["server"]["port"];
+
+// Check if the view refers to a node
+if (host_view)
+ std::cout << "Host exists\n";
+```
+
+### Getting Values with `value<T>()`
+
+```cpp
+// Returns std::optional<T>
+std::optional<std::string_view> host = tbl["server"]["host"].value<std::string_view>();
+std::optional<int64_t> port = tbl["server"]["port"].value<int64_t>();
+std::optional<bool> debug = tbl["server"]["debug"].value<bool>();
+
+if (host)
+ std::cout << "Host: " << *host << "\n";
+```
+
+`value<T>()` is permissive — it allows some type conversions (e.g., reading an integer as a double).
+
+### Getting Values with `value_exact<T>()`
+
+```cpp
+// Strict — only returns a value if the types match exactly
+std::optional<int64_t> port = tbl["server"]["port"].value_exact<int64_t>();
+```
+
+`value_exact<T>()` only succeeds if the underlying node is exactly that type. No conversions.
+
+### Getting Values with `value_or()`
+
+The most convenient accessor — returns the value or a default:
+
+```cpp
+std::string_view host = tbl["server"]["host"].value_or("0.0.0.0"sv);
+int64_t port = tbl["server"]["port"].value_or(80);
+bool debug = tbl["server"]["debug"].value_or(true);
+
+// Safe even if the key doesn't exist:
+std::string_view missing = tbl["nonexistent"]["key"].value_or("default"sv);
+```
+
+### Using `as<T>()` for Pointer Access
+
+`as<T>()` returns a pointer to the node if it matches the type, or `nullptr`:
+
+```cpp
+if (auto* str_val = tbl["server"]["host"].as_string())
+ std::cout << "Host: " << str_val->get() << "\n";
+
+if (auto* port_val = tbl["server"]["port"].as_integer())
+ std::cout << "Port: " << port_val->get() << "\n";
+
+// Generic template version:
+if (auto* arr = tbl["server"]["tags"].as<toml::array>())
+ std::cout << "Tags count: " << arr->size() << "\n";
+```
+
+Specific convenience methods exist:
+- `as_table()` → `toml::table*`
+- `as_array()` → `toml::array*`
+- `as_string()` → `toml::value<std::string>*`
+- `as_integer()` → `toml::value<int64_t>*`
+- `as_floating_point()` → `toml::value<double>*`
+- `as_boolean()` → `toml::value<bool>*`
+- `as_date()` → `toml::value<toml::date>*`
+- `as_time()` → `toml::value<toml::time>*`
+- `as_date_time()` → `toml::value<toml::date_time>*`
+
+### Direct Node Access with `get()`
+
+On `toml::table`:
+```cpp
+toml::node* node = tbl.get("server");
+if (node && node->is_table())
+{
+ toml::table& server = *node->as_table();
+ // ...
+}
+```
+
+On `toml::array`:
+```cpp
+auto* arr = tbl["server"]["tags"].as_array();
+if (arr && arr->size() > 0)
+{
+ toml::node& first = (*arr)[0];
+ std::cout << first.value_or(""sv) << "\n";
+}
+```
+
+### Typed get with `get_as<T>()`
+
+```cpp
+// On table — returns pointer if key exists AND matches type
+if (auto* val = tbl.get_as<std::string>("title"))
+ std::cout << "Title: " << val->get() << "\n";
+
+// On array — returns pointer if index is valid AND matches type
+auto* arr = tbl["tags"].as_array();
+if (arr)
+{
+ if (auto* s = arr->get_as<std::string>(0))
+ std::cout << "First tag: " << s->get() << "\n";
+}
+```
+
+---
+
+## Iterating Tables
+
+### Range-based For Loop
+
+```cpp
+auto tbl = toml::parse(R"(
+ a = 1
+ b = "hello"
+ c = true
+)");
+
+for (auto&& [key, value] : tbl)
+{
+ std::cout << key << " = " << value << " (type: " << value.type() << ")\n";
+}
+```
+
+Output:
+```
+a = 1 (type: integer)
+b = "hello" (type: string)
+c = true (type: boolean)
+```
+
+### Using `for_each()`
+
+`for_each()` calls a visitor with each key-value pair. The value is passed as its concrete type:
+
+```cpp
+tbl.for_each([](auto& key, auto& value)
+{
+ std::cout << key << ": ";
+ if constexpr (toml::is_string<decltype(value)>)
+ std::cout << "string = " << value.get() << "\n";
+ else if constexpr (toml::is_integer<decltype(value)>)
+ std::cout << "integer = " << value.get() << "\n";
+ else if constexpr (toml::is_boolean<decltype(value)>)
+ std::cout << "boolean = " << value.get() << "\n";
+ else
+ std::cout << "(other)\n";
+});
+```
+
+---
+
+## Iterating Arrays
+
+### Range-based For Loop
+
+```cpp
+auto tbl = toml::parse(R"(
+ numbers = [1, 2, 3, 4, 5]
+)");
+
+auto& arr = *tbl["numbers"].as_array();
+for (auto& elem : arr)
+{
+ std::cout << elem.value_or(0) << " ";
+}
+// Output: 1 2 3 4 5
+```
+
+### Index-based Access
+
+```cpp
+for (size_t i = 0; i < arr.size(); i++)
+{
+ std::cout << arr[i].value_or(0) << " ";
+}
+```
+
+### Using `for_each()`
+
+```cpp
+arr.for_each([](size_t index, auto& elem)
+{
+ if constexpr (toml::is_integer<decltype(elem)>)
+ std::cout << "[" << index << "] = " << elem.get() << "\n";
+});
+```
+
+---
+
+## Creating TOML Programmatically
+
+### Constructing a Table
+
+```cpp
+auto tbl = toml::table{
+ { "title", "My Application" },
+ { "version", 2 },
+ { "debug", false },
+ { "database", toml::table{
+ { "host", "localhost" },
+ { "port", 5432 }
+ }},
+ { "tags", toml::array{ "web", "api", "rest" } }
+};
+
+std::cout << tbl << "\n";
+```
+
+Output:
+```toml
+title = "My Application"
+version = 2
+debug = false
+tags = ["web", "api", "rest"]
+
+[database]
+host = "localhost"
+port = 5432
+```
+
+### Inserting Values
+
+```cpp
+toml::table config;
+
+// insert() — only inserts if key doesn't exist
+config.insert("name", "MyApp");
+config.insert("name", "Overwritten"); // no-op, key already exists
+
+// insert_or_assign() — inserts or replaces
+config.insert_or_assign("name", "ReplacedApp");
+
+// emplace() — construct in place if key doesn't exist
+config.emplace<std::string>("greeting", "Hello, World!");
+```
+
+### Building Arrays
+
+```cpp
+toml::array arr;
+arr.push_back(1);
+arr.push_back(2);
+arr.push_back(3);
+arr.push_back("mixed types are fine");
+
+// Or construct directly:
+auto arr2 = toml::array{ 10, 20, 30 };
+
+// Emplace:
+arr2.emplace_back<std::string>("hello");
+```
+
+### Creating Date/Time Values
+
+```cpp
+auto tbl = toml::table{
+ { "birthday", toml::date{ 1990, 6, 15 } },
+ { "alarm", toml::time{ 7, 30 } },
+ { "event", toml::date_time{
+ toml::date{ 2024, 12, 25 },
+ toml::time{ 9, 0 },
+ toml::time_offset{ -5, 0 } // EST
+ }}
+};
+
+std::cout << tbl << "\n";
+```
+
+Output:
+```toml
+birthday = 1990-06-15
+alarm = 07:30:00
+event = 2024-12-25T09:00:00-05:00
+```
+
+---
+
+## Modifying Parsed Data
+
+```cpp
+auto tbl = toml::parse(R"(
+ [server]
+ host = "localhost"
+ port = 8080
+)");
+
+// Change a value
+tbl.insert_or_assign("server", toml::table{
+ { "host", "0.0.0.0" },
+ { "port", 443 },
+ { "ssl", true }
+});
+
+// Add a new section
+tbl.insert("logging", toml::table{
+ { "level", "info" },
+ { "file", "/var/log/app.log" }
+});
+
+// Remove a key
+if (auto* server = tbl["server"].as_table())
+ server->erase("ssl");
+
+// Modify array
+tbl.insert("features", toml::array{ "auth", "cache" });
+if (auto* features = tbl["features"].as_array())
+{
+ features->push_back("logging");
+ features->insert(features->begin(), "core");
+}
+
+std::cout << tbl << "\n";
+```
+
+---
+
+## Serialization
+
+### To TOML (Default)
+
+Simply stream a table or use `toml_formatter`:
+
+```cpp
+// These are equivalent:
+std::cout << tbl << "\n";
+std::cout << toml::toml_formatter{ tbl } << "\n";
+```
+
+### To JSON
+
+```cpp
+std::cout << toml::json_formatter{ tbl } << "\n";
+```
+
+### To YAML
+
+```cpp
+std::cout << toml::yaml_formatter{ tbl } << "\n";
+```
+
+### To a String
+
+```cpp
+#include <sstream>
+
+std::ostringstream ss;
+ss << tbl;
+std::string toml_string = ss.str();
+
+// Or as JSON:
+ss.str("");
+ss << toml::json_formatter{ tbl };
+std::string json_string = ss.str();
+```
+
+### To a File
+
+```cpp
+#include <fstream>
+
+std::ofstream file("output.toml");
+file << tbl;
+```
+
+---
+
+## Path-Based Access
+
+### Using `at_path()`
+
+```cpp
+auto tbl = toml::parse(R"(
+ [database]
+ servers = [
+ { host = "alpha", port = 5432 },
+ { host = "beta", port = 5433 }
+ ]
+)");
+
+// Dot-separated path with array indices
+auto host = toml::at_path(tbl, "database.servers[0].host");
+std::cout << host.value_or("unknown"sv) << "\n"; // "alpha"
+
+auto port = toml::at_path(tbl, "database.servers[1].port");
+std::cout << port.value_or(0) << "\n"; // 5433
+```
+
+### Using `toml::path`
+
+```cpp
+toml::path p("database.servers[0].host");
+auto view = tbl[p];
+std::cout << view.value_or("unknown"sv) << "\n";
+
+// Path manipulation
+toml::path parent = p.parent_path(); // "database.servers[0]"
+std::cout << tbl[parent] << "\n"; // { host = "alpha", port = 5432 }
+```
+
+---
+
+## The Visitor Pattern
+
+### Using `visit()`
+
+```cpp
+toml::node& some_node = *tbl.get("title");
+
+some_node.visit([](auto& val)
+{
+ // val is the concrete type: table&, array&, or value<T>&
+ using T = std::remove_cvref_t<decltype(val)>;
+
+ if constexpr (std::is_same_v<T, toml::table>)
+ std::cout << "It's a table\n";
+ else if constexpr (std::is_same_v<T, toml::array>)
+ std::cout << "It's an array\n";
+ else
+ std::cout << "It's a value: " << val.get() << "\n";
+});
+```
+
+### Using `for_each()` on Tables and Arrays
+
+`for_each()` iterates and visits each element with its concrete type:
+
+```cpp
+tbl.for_each([](const toml::key& key, auto& value)
+{
+ std::cout << key << " -> " << value << "\n";
+});
+```
+
+---
+
+## Source Information
+
+Every parsed node tracks where it was defined:
+
+```cpp
+auto tbl = toml::parse_file("config.toml");
+
+if (auto* name = tbl.get("name"))
+{
+ auto& src = name->source();
+ std::cout << "Defined at line " << src.begin.line
+ << ", column " << src.begin.column << "\n";
+
+ if (src.path)
+ std::cout << "In file: " << *src.path << "\n";
+}
+```
+
+---
+
+## Type Checking
+
+```cpp
+toml::node& node = /* some node */;
+
+// Virtual method checks
+if (node.is_string()) { /* ... */ }
+if (node.is_integer()) { /* ... */ }
+if (node.is_table()) { /* ... */ }
+
+// Template check
+if (node.is<double>()) { /* ... */ }
+if (node.is<toml::array>()) { /* ... */ }
+
+// Get the type enum
+switch (node.type())
+{
+ case toml::node_type::string: break;
+ case toml::node_type::integer: break;
+ case toml::node_type::floating_point: break;
+ case toml::node_type::boolean: break;
+ case toml::node_type::date: break;
+ case toml::node_type::time: break;
+ case toml::node_type::date_time: break;
+ case toml::node_type::table: break;
+ case toml::node_type::array: break;
+ default: break;
+}
+```
+
+---
+
+## Complete Example: Config File Reader
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+#include <string_view>
+
+using namespace std::string_view_literals;
+
+int main()
+{
+ toml::table config;
+ try
+ {
+ config = toml::parse_file("app.toml");
+ }
+ catch (const toml::parse_error& err)
+ {
+ std::cerr << "Failed to parse config:\n" << err << "\n";
+ return 1;
+ }
+
+ // Read application settings
+ auto app_name = config["app"]["name"].value_or("Unknown"sv);
+ auto app_version = config["app"]["version"].value_or(1);
+ auto log_level = config["logging"]["level"].value_or("info"sv);
+ auto log_file = config["logging"]["file"].value_or("/tmp/app.log"sv);
+
+ std::cout << "Application: " << app_name << " v" << app_version << "\n";
+ std::cout << "Log level: " << log_level << "\n";
+ std::cout << "Log file: " << log_file << "\n";
+
+ // Read database connections
+ if (auto* dbs = config["databases"].as_array())
+ {
+ for (auto& db_node : *dbs)
+ {
+ if (auto* db = db_node.as_table())
+ {
+ auto host = (*db)["host"].value_or("localhost"sv);
+ auto port = (*db)["port"].value_or(5432);
+ auto name = (*db)["name"].value_or("mydb"sv);
+ std::cout << "DB: " << name << " @ " << host << ":" << port << "\n";
+ }
+ }
+ }
+
+ // Modify and write back
+ config.insert_or_assign("last_run", toml::date_time{
+ toml::date{ 2024, 1, 15 },
+ toml::time{ 14, 30, 0 }
+ });
+
+ std::ofstream out("app.toml");
+ out << config;
+
+ return 0;
+}
+```
+
+---
+
+## Related Documentation
+
+- [node-system.md](node-system.md) — Deep dive into node types and value retrieval
+- [tables.md](tables.md) — Table manipulation details
+- [arrays.md](arrays.md) — Array manipulation details
+- [parsing.md](parsing.md) — Parser internals and error handling
+- [formatting.md](formatting.md) — Serialization customization
+- [path-system.md](path-system.md) — Path-based navigation
diff --git a/docs/handbook/tomlplusplus/building.md b/docs/handbook/tomlplusplus/building.md
new file mode 100644
index 0000000000..c70c76c297
--- /dev/null
+++ b/docs/handbook/tomlplusplus/building.md
@@ -0,0 +1,474 @@
+# toml++ — Building
+
+## Overview
+
+toml++ supports multiple build modes and build systems. It can be consumed as a header-only library, a single-header drop-in, or a compiled static/shared library. The primary build system is Meson, with CMake as a first-class alternative and Visual Studio project files also provided.
+
+---
+
+## Build Modes
+
+### 1. Header-Only Mode (Default)
+
+The simplest way to use toml++. No compilation of the library itself is needed.
+
+**Setup:**
+1. Add `tomlplusplus/include` to your include paths
+2. `#include <toml++/toml.hpp>` in your source files
+3. Compile your project with C++17 or later
+
+This is the default mode. The macro `TOML_HEADER_ONLY` defaults to `1`.
+
+**Advantages:**
+- Zero build configuration
+- No separate library to link
+- Works with any build system
+
+**Disadvantages:**
+- Every translation unit that includes toml++ compiles the full implementation
+- Can increase compile times in large projects
+
+**Example CMake integration:**
+```cmake
+# Add tomlplusplus as a subdirectory or fetch it
+add_subdirectory(external/tomlplusplus)
+target_link_libraries(my_target PRIVATE tomlplusplus::tomlplusplus)
+```
+
+### 2. Single-Header Mode
+
+toml++ ships with a pre-amalgamated single-header file at the repository root: `toml.hpp`.
+
+**Setup:**
+1. Copy `toml.hpp` into your project
+2. `#include "toml.hpp"` in your source files
+3. Done
+
+This file contains the entire library — all headers, all `.inl` implementation files — concatenated into one file. The API is identical to the multi-header version.
+
+### 3. Compiled Library Mode
+
+For projects where compile time matters, toml++ can be built as a compiled library.
+
+**Setup:**
+
+In exactly **one** translation unit, compile `src/toml.cpp`:
+
+```cpp
+// src/toml.cpp — this is the entire compiled-library source
+#ifndef TOML_IMPLEMENTATION
+#define TOML_IMPLEMENTATION
+#endif
+#ifndef TOML_HEADER_ONLY
+#define TOML_HEADER_ONLY 0
+#endif
+
+#include <toml++/toml.hpp>
+```
+
+In all other translation units, define `TOML_HEADER_ONLY=0` before including the header:
+
+```cpp
+#define TOML_HEADER_ONLY 0
+#include <toml++/toml.hpp>
+```
+
+Or set it project-wide via compiler flags:
+```bash
+g++ -DTOML_HEADER_ONLY=0 -I/path/to/tomlplusplus/include ...
+```
+
+**Advantages:**
+- The parser and formatter implementations are compiled once
+- Faster incremental builds
+- Smaller binary size (fewer inlined copies)
+
+**Disadvantages:**
+- Requires linking the compiled translation unit
+- Need to ensure `TOML_HEADER_ONLY=0` is consistent across all TUs
+
+### 4. C++20 Modules Mode
+
+When using CMake 3.28+ and a C++20 compiler:
+
+```cmake
+cmake -DTOMLPLUSPLUS_BUILD_MODULES=ON ..
+```
+
+Then in your source:
+```cpp
+import tomlplusplus;
+```
+
+Module support is experimental and requires:
+- CMake ≥ 3.28
+- A compiler with C++20 module support (recent GCC, Clang, or MSVC)
+
+The module source files are in `src/modules/`.
+
+---
+
+## Meson Build System
+
+Meson is the primary build system for toml++. The project file is `meson.build` at the repository root.
+
+### Project Definition
+
+```meson
+project(
+ 'tomlplusplus',
+ 'cpp',
+ license: 'MIT',
+ version: '3.4.0',
+ meson_version: '>=0.61.0',
+ default_options: [
+ 'buildtype=release',
+ 'default_library=shared',
+ 'b_lto=false',
+ 'b_ndebug=if-release',
+ 'cpp_std=c++17'
+ ]
+)
+```
+
+### Meson Options
+
+Options are defined in `meson_options.txt`:
+
+| Option | Type | Default | Description |
+|--------|------|---------|-------------|
+| `devel` | bool | `false` | Development build (implies `build_tests`, `build_examples`, `pedantic`) |
+| `build_lib` | bool | `false` | Compile as a library (implied by `devel`) |
+| `build_examples` | bool | `false` | Build example programs (implied by `devel`) |
+| `build_tests` | bool | `false` | Build test suite (implied by `devel`) |
+| `build_tt` | bool | `false` | Build toml-test encoder/decoder (implied by `devel`, disabled by `unreleased_features`) |
+| `pedantic` | bool | `false` | Enable maximum compiler warnings (implied by `devel`) |
+| `permissive` | bool | `false` | MSVC `/permissive` mode (default is `/permissive-`) |
+| `time_trace` | bool | `false` | Enable `-ftime-trace` (Clang only) |
+| `unreleased_features` | bool | `false` | Enable `TOML_UNRELEASED_FEATURES=1` |
+| `generate_cmake_config` | bool | `true` | Generate a CMake package config file |
+| `use_vendored_libs` | bool | `true` | Use vendored Catch2 for tests |
+
+### Building with Meson
+
+```bash
+# Configure
+meson setup build
+# Or with options:
+meson setup build -Dbuild_tests=true -Dbuild_examples=true
+
+# Compile
+meson compile -C build
+
+# Run tests
+meson test -C build
+
+# Development build (builds everything, enables warnings)
+meson setup build -Ddevel=true
+```
+
+### Using as a Meson Subproject
+
+Create `subprojects/tomlplusplus.wrap`:
+```ini
+[wrap-git]
+url = https://github.com/marzer/tomlplusplus.git
+revision = v3.4.0
+
+[provide]
+tomlplusplus = tomlplusplus_dep
+```
+
+Then in your `meson.build`:
+```meson
+tomlplusplus_dep = dependency('tomlplusplus', version: '>=3.4.0')
+executable('my_app', 'main.cpp', dependencies: [tomlplusplus_dep])
+```
+
+### Meson Library Target
+
+When `build_lib` is true (or implied), the library is compiled:
+
+```meson
+# In the meson.build, the library creates a tomlplusplus_dep dependency
+# that other targets consume
+```
+
+The compiled library defines:
+- `TOML_HEADER_ONLY=0`
+- `TOML_IMPLEMENTATION`
+
+### Compiler Flag Management
+
+The Meson build applies comprehensive compiler flags based on the detected compiler:
+
+**Common flags:**
+```
+-ferror-limit=5 # Clang: max errors
+-fmax-errors=5 # GCC: max errors
+-fchar8_t # Enable char8_t
+```
+
+**MSVC-specific:**
+```
+/bigobj # Large object file support
+/utf-8 # UTF-8 source encoding
+/Zc:__cplusplus # Correct __cplusplus value
+/Zc:inline # Remove unreferenced COMDAT
+/Zc:externConstexpr # External constexpr linkage
+/Zc:preprocessor # Standards-conforming preprocessor
+```
+
+**Pedantic mode** enables extensive warning flags for both GCC and Clang (`-Weverything`, `-Wcast-align`, `-Wshadow`, etc.) with targeted suppressions for unavoidable warnings (`-Wno-c++98-compat`, `-Wno-padded`, etc.).
+
+---
+
+## CMake Build System
+
+### CMake Project
+
+The `CMakeLists.txt` defines an interface (header-only) library:
+
+```cmake
+cmake_minimum_required(VERSION 3.14)
+
+project(
+ tomlplusplus
+ VERSION 3.4.0
+ DESCRIPTION "Header-only TOML config file parser and serializer for C++17"
+ HOMEPAGE_URL "https://marzer.github.io/tomlplusplus/"
+ LANGUAGES CXX
+)
+
+add_library(tomlplusplus_tomlplusplus INTERFACE)
+add_library(tomlplusplus::tomlplusplus ALIAS tomlplusplus_tomlplusplus)
+
+target_include_directories(
+ tomlplusplus_tomlplusplus
+ INTERFACE
+ "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
+)
+
+target_compile_features(tomlplusplus_tomlplusplus INTERFACE cxx_std_17)
+```
+
+### Using with CMake — Subdirectory
+
+```cmake
+add_subdirectory(path/to/tomlplusplus)
+target_link_libraries(my_target PRIVATE tomlplusplus::tomlplusplus)
+```
+
+### Using with CMake — FetchContent
+
+```cmake
+include(FetchContent)
+FetchContent_Declare(
+ tomlplusplus
+ GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
+ GIT_TAG v3.4.0
+)
+FetchContent_MakeAvailable(tomlplusplus)
+
+target_link_libraries(my_target PRIVATE tomlplusplus::tomlplusplus)
+```
+
+### Using with CMake — find_package
+
+If toml++ is installed system-wide or the CMake config was generated:
+
+```cmake
+find_package(tomlplusplus REQUIRED)
+target_link_libraries(my_target PRIVATE tomlplusplus::tomlplusplus)
+```
+
+### CMake Options
+
+```cmake
+option(BUILD_EXAMPLES "Build examples tree." OFF)
+option(BUILD_FUZZER "Build fuzzer." OFF)
+option(TOMLPLUSPLUS_BUILD_MODULES "Build C++ modules support" OFF)
+```
+
+### CMake Install
+
+When `tomlplusplus_INSTALL` is true, install rules are included from `cmake/install-rules.cmake`.
+
+---
+
+## Visual Studio
+
+The repository includes Visual Studio project files:
+- `toml++.sln` — Solution file
+- `toml++.vcxproj` — Main project file
+- `toml++.vcxproj.filters` — Filter definition
+- `toml++.props` — Property sheet
+- `toml++.natvis` — Natvis debugger visualizer for TOML node types
+
+Individual examples also have `.vcxproj` files:
+- `examples/simple_parser.vcxproj`
+- `examples/toml_to_json_transcoder.vcxproj`
+- `examples/toml_generator.vcxproj`
+- `examples/error_printer.vcxproj`
+- `examples/parse_benchmark.vcxproj`
+
+---
+
+## Package Managers
+
+### Vcpkg
+
+```bash
+vcpkg install tomlplusplus
+```
+
+### Conan
+
+In your `conanfile.txt`:
+```
+[requires]
+tomlplusplus/3.4.0
+```
+
+### DDS
+
+In your `package.json5`:
+```json5
+depends: [
+ 'tomlpp^3.4.0',
+]
+```
+
+### tipi.build
+
+In `.tipi/deps`:
+```json
+{
+ "marzer/tomlplusplus": {}
+}
+```
+
+---
+
+## Compiler Requirements
+
+### Minimum Versions
+
+| Compiler | Minimum Version |
+|----------|----------------|
+| GCC | 8+ |
+| Clang | 8+ |
+| Apple Clang | Xcode 10+ |
+| MSVC | VS2019 (19.20+) |
+| Intel C++ | ICC 19+, ICL 19+ |
+
+### Required Standard
+
+C++17 is required. The preprocessor enforces this:
+
+```cpp
+#if TOML_CPP < 17
+#error toml++ requires C++17 or higher.
+#endif
+```
+
+### Compiler Feature Detection
+
+The library detects compiler capabilities:
+
+```cpp
+// TOML_CPP — detected C++ standard version (11, 14, 17, 20, 23, 26, 29)
+// TOML_GCC — GCC major version (0 if not GCC)
+// TOML_CLANG — Clang major version (0 if not Clang)
+// TOML_MSVC — MSVC version (0 if not MSVC)
+// TOML_ICC — Intel compiler detection
+// TOML_NVCC — NVIDIA CUDA compiler detection
+// TOML_HAS_CHAR8 — char8_t available
+// TOML_HAS_EXCEPTIONS — exceptions enabled
+```
+
+---
+
+## Configuration Macros Reference
+
+Define these **before** including `<toml++/toml.hpp>`:
+
+### Core Configuration
+
+```cpp
+// Library mode
+#define TOML_HEADER_ONLY 1 // 1 = header-only (default), 0 = compiled
+
+// Feature toggles
+#define TOML_ENABLE_PARSER 1 // 1 = include parser (default), 0 = no parser
+#define TOML_ENABLE_FORMATTERS 1 // 1 = include formatters (default), 0 = no formatters
+
+// Exception handling
+// Auto-detected from compiler settings. Override with:
+#define TOML_EXCEPTIONS 1 // or 0
+
+// Unreleased TOML features
+#define TOML_UNRELEASED_FEATURES 0 // 1 = enable upcoming TOML spec features
+```
+
+### Platform Configuration
+
+```cpp
+// Windows wide-string support (auto-detected on Windows)
+#define TOML_ENABLE_WINDOWS_COMPAT 1
+
+// Custom optional type
+#define TOML_OPTIONAL_TYPE std::optional // or your custom type
+
+// Disable environment checks
+#define TOML_DISABLE_ENVIRONMENT_CHECKS
+```
+
+### Example: Minimal Parse-Only Build
+
+```cpp
+#define TOML_ENABLE_FORMATTERS 0 // Don't need serialization
+#include <toml++/toml.hpp>
+```
+
+### Example: Serialize-Only Build
+
+```cpp
+#define TOML_ENABLE_PARSER 0 // Don't need parsing
+#include <toml++/toml.hpp>
+```
+
+---
+
+## Build Troubleshooting
+
+### Common Issues
+
+**"toml++ requires C++17 or higher"**
+Ensure your compiler is invoked with `-std=c++17` (or later) or the equivalent flag.
+
+**Large object files on MSVC**
+Use `/bigobj` flag (the Meson build adds this automatically).
+
+**Long compile times**
+Switch to compiled library mode (`TOML_HEADER_ONLY=0` + compile `src/toml.cpp`).
+
+**ODR violations when mixing settings**
+Ensure all translation units use the same values for `TOML_EXCEPTIONS`, `TOML_ENABLE_PARSER`, etc. The ABI namespace system catches some mismatches at link time, but not all.
+
+**`char8_t` errors on older compilers**
+Add `-fchar8_t` flag if your compiler supports it, or compile with C++20 mode.
+
+**RTTI disabled**
+toml++ does not require RTTI. It uses virtual dispatch, not `dynamic_cast` or `typeid`.
+
+**Exceptions disabled**
+Set `TOML_EXCEPTIONS=0` or use `-fno-exceptions`. The API adapts: `parse()` returns `parse_result` instead of throwing.
+
+---
+
+## Related Documentation
+
+- [overview.md](overview.md) — Library feature list
+- [basic-usage.md](basic-usage.md) — Getting started with parsing and serialization
+- [testing.md](testing.md) — Running the test suite
diff --git a/docs/handbook/tomlplusplus/code-style.md b/docs/handbook/tomlplusplus/code-style.md
new file mode 100644
index 0000000000..43c16d3ce4
--- /dev/null
+++ b/docs/handbook/tomlplusplus/code-style.md
@@ -0,0 +1,277 @@
+# toml++ — Code Style
+
+## Overview
+
+This document describes the code conventions and formatting rules used in the toml++ project, derived from the `.clang-format` configuration and source code patterns.
+
+---
+
+## Formatting Rules (`.clang-format`)
+
+The project uses clang-format with these key settings:
+
+### Indentation
+
+- **IndentWidth**: 4 (tabs are used, tab width 4)
+- **UseTab**: `ForContinuationAndIndentation`
+- **TabWidth**: 4
+- **ContinuationIndentWidth**: 4
+- **ConstructorInitializerIndentWidth**: 4
+- **AccessModifierOffset**: -4 (access specifiers at class indent level)
+- **IndentCaseLabels**: true
+- **NamespaceIndentation**: All
+
+### Braces
+
+- **BreakBeforeBraces**: Allman style
+ - Functions, classes, structs, enums, namespaces, control statements — all open brace on new line:
+
+```cpp
+namespace toml
+{
+ class node
+ {
+ public:
+ void method()
+ {
+ if (condition)
+ {
+ // ...
+ }
+ }
+ };
+}
+```
+
+### Alignment
+
+- **AlignConsecutiveAssignments**: true
+- **AlignConsecutiveDeclarations**: true
+- **AlignTrailingComments**: true
+- **AlignOperands**: true
+- **AlignAfterOpenBracket**: Align
+
+### Line Length
+
+- **ColumnLimit**: 120
+
+### Other Settings
+
+- **AllowShortFunctionsOnASingleLine**: Empty (empty functions on one line)
+- **AllowShortIfStatementsOnASingleLine**: Never
+- **AllowShortLoopsOnASingleLine**: false
+- **AlwaysBreakTemplateDeclarations**: Yes
+- **BinPackArguments**: false
+- **BinPackParameters**: false
+- **PointerAlignment**: Left (`int* ptr`, not `int *ptr`)
+- **SpaceAfterTemplateKeyword**: true
+- **SortIncludes**: false (manual include ordering)
+
+---
+
+## Naming Conventions
+
+### Macros
+
+All macros use the `TOML_` prefix with `UPPER_SNAKE_CASE`:
+
+```cpp
+TOML_HEADER_ONLY
+TOML_EXCEPTIONS
+TOML_ENABLE_PARSER
+TOML_ENABLE_FORMATTERS
+TOML_ENABLE_WINDOWS_COMPAT
+TOML_UNRELEASED_FEATURES
+TOML_LIB_MAJOR
+TOML_NAMESPACE_START
+TOML_NAMESPACE_END
+TOML_EXPORTED_CLASS
+TOML_EXPORTED_MEMBER_FUNCTION
+TOML_EXPORTED_STATIC_FUNCTION
+TOML_EXPORTED_FREE_FUNCTION
+```
+
+### Namespaces
+
+- Public API: `toml` namespace (aliased from a versioned namespace `toml::vN`)
+- Internal implementation: `toml::impl` (aka `toml::vN::impl`)
+- Macro-managed namespace boundaries:
+
+```cpp
+TOML_NAMESPACE_START // opens toml::v3
+{
+ // public API
+}
+TOML_NAMESPACE_END // closes
+
+TOML_IMPL_NAMESPACE_START // opens toml::v3::impl
+{
+ // internal details
+}
+TOML_IMPL_NAMESPACE_END
+```
+
+### Types and Classes
+
+- `snake_case` for all types: `node`, `table`, `array`, `value`, `path`, `path_component`, `parse_result`, `parse_error`, `source_region`, `source_position`, `date_time`, `time_offset`, `node_view`, `key`
+- Template parameters: `PascalCase` (`ValueType`, `IsConst`, `ViewedType`, `ElemType`)
+
+### Member Variables
+
+- Private members use trailing underscore: `val_`, `flags_`, `elems_`, `map_`, `inline_`, `source_`, `components_`
+- No prefix for public struct fields: `year`, `month`, `day`, `line`, `column`, `begin`, `end`, `path`
+
+### Methods
+
+- `snake_case`: `is_table()`, `as_array()`, `value_or()`, `push_back()`, `emplace_back()`, `is_homogeneous()`, `for_each()`, `parse_file()`, `at_path()`
+
+### Enums
+
+- `snake_case` enum type names: `node_type`, `value_flags`, `format_flags`, `path_component_type`
+- `snake_case` enum values: `node_type::string`, `value_flags::format_as_hexadecimal`, `format_flags::indent_sub_tables`
+
+---
+
+## Header Organization
+
+### File Pairs
+
+Most features have a `.hpp` declaration header and a `.inl` implementation file:
+
+```
+node.hpp / node.inl
+table.hpp / table.inl
+array.hpp / array.inl
+parser.hpp / parser.inl
+formatter.hpp / formatter.inl
+```
+
+### Include Guards
+
+Headers use `#pragma once` (no traditional include guards).
+
+### Header Structure
+
+Typical header layout:
+
+```cpp
+// license header comment
+#pragma once
+
+#include "preprocessor.hpp" // macros and config
+#include "forward_declarations.hpp" // forward declarations
+// ... other includes
+
+// Header-only mode guard
+#if defined(TOML_IMPLEMENTATION) || !TOML_HEADER_ONLY
+
+TOML_NAMESPACE_START
+{
+ // declarations / implementations
+}
+TOML_NAMESPACE_END
+
+#endif // TOML_IMPLEMENTATION
+```
+
+### Export Annotations
+
+Exported symbols use macros for DLL visibility:
+
+```cpp
+TOML_EXPORTED_CLASS table : public node
+{
+ TOML_EXPORTED_MEMBER_FUNCTION void clear() noexcept;
+ TOML_EXPORTED_STATIC_FUNCTION static table parse(...);
+};
+
+TOML_EXPORTED_FREE_FUNCTION parse_result parse(std::string_view);
+```
+
+---
+
+## Preprocessor Conventions
+
+### Compiler Detection
+
+```cpp
+TOML_GCC // GCC
+TOML_CLANG // Clang
+TOML_MSVC // MSVC
+TOML_ICC // Intel C++
+TOML_ICC_CL // Intel C++ (MSVC frontend)
+```
+
+### Feature Detection
+
+```cpp
+TOML_HAS_CHAR8 // char8_t available
+TOML_HAS_CUSTOM_OPTIONAL_TYPE // user-provided optional
+TOML_INT_CHARCONV // charconv for integers
+TOML_FLOAT_CHARCONV // charconv for floats
+```
+
+### Warning Management
+
+Extensive `#pragma` blocks suppress known-benign warnings per compiler:
+
+```cpp
+TOML_PUSH_WARNINGS
+TOML_DISABLE_WARNINGS
+// ... code ...
+TOML_POP_WARNINGS
+
+TOML_DISABLE_ARITHMETIC_WARNINGS
+TOML_DISABLE_SPAM_WARNINGS
+```
+
+---
+
+## Conditional Compilation Patterns
+
+Major features are conditionally compiled:
+
+```cpp
+#if TOML_ENABLE_PARSER
+ // parser code
+#endif
+
+#if TOML_ENABLE_FORMATTERS
+ // formatter code
+#endif
+
+#if TOML_ENABLE_WINDOWS_COMPAT
+ // wchar_t / wstring overloads
+#endif
+
+#if TOML_EXCEPTIONS
+ // exception-based error handling
+#else
+ // return-code error handling
+#endif
+```
+
+---
+
+## Documentation Conventions
+
+- Source comments use `//` style (not `/* */`)
+- Doxygen is used for API documentation (the public `toml.hpp` single-header has `///` comments)
+- Internal implementation headers have minimal comments — the code is expected to be self-documenting
+
+---
+
+## Build System Conventions
+
+- Primary build: **Meson** (`meson.build`, `meson_options.txt`)
+- Secondary: **CMake** (`CMakeLists.txt`)
+- All configuration macros can be set via build system options or via `#define` before including the header
+- Meson option names mirror the macro names: `is_header_only` → `TOML_HEADER_ONLY`
+
+---
+
+## Related Documentation
+
+- [architecture.md](architecture.md) — Project structure and design
+- [building.md](building.md) — Build system details
+- [testing.md](testing.md) — Testing conventions
diff --git a/docs/handbook/tomlplusplus/formatting.md b/docs/handbook/tomlplusplus/formatting.md
new file mode 100644
index 0000000000..d46a433a86
--- /dev/null
+++ b/docs/handbook/tomlplusplus/formatting.md
@@ -0,0 +1,546 @@
+# toml++ — Formatting
+
+## Overview
+
+toml++ includes three formatters for serializing a TOML node tree to text:
+
+| Formatter | Output Format | Header |
+|-----------|--------------|--------|
+| `toml::toml_formatter` | Standard TOML | `toml_formatter.hpp` |
+| `toml::json_formatter` | JSON | `json_formatter.hpp` |
+| `toml::yaml_formatter` | YAML | `yaml_formatter.hpp` |
+
+All three inherit from the internal `impl::formatter` base class and share common indentation and streaming infrastructure.
+
+Formatters can be disabled entirely via `TOML_ENABLE_FORMATTERS=0`.
+
+---
+
+## Base Formatter (`impl::formatter`)
+
+Declared in `include/toml++/impl/formatter.hpp`. Not directly instantiable — used through the concrete subclasses.
+
+### `formatter_constants`
+
+Each formatter defines a set of string constants:
+
+```cpp
+struct formatter_constants
+{
+ format_flags mandatory_flags; // flags always applied
+ format_flags ignored_flags; // flags explicitly not applied
+
+ std::string_view float_pos_inf; // "+inf", "Infinity", ".inf"
+ std::string_view float_neg_inf; // "-inf", "-Infinity", "-.inf"
+ std::string_view float_nan; // "nan", "NaN", ".nan"
+
+ std::string_view bool_true; // "true"
+ std::string_view bool_false; // "false"
+};
+```
+
+### `formatter_config`
+
+```cpp
+struct formatter_config
+{
+ format_flags flags; // active formatting flags
+};
+```
+
+### Internal State
+
+The base class manages:
+- `const node* source_` — the node being formatted
+- `formatter_constants constants_` — string representations
+- `formatter_config config_` — user-supplied configuration
+- `int indent_` — current indentation level
+- `bool naked_newline_` — tracks newline state
+
+Helper methods:
+- `increase_indent()` / `decrease_indent()` — adjust indentation
+- `print_indent()` — emit current indentation
+- `print_newline()` — emit newline with proper tracking
+- `print_string()` — format a TOML string with proper escaping
+- `print_value()` — format a leaf value (delegates to constants for inf/nan/bool)
+
+---
+
+## `format_flags`
+
+Bitmask enum controlling formatting behavior:
+
+```cpp
+enum class format_flags : uint64_t
+{
+ none = 0,
+ quote_dates_and_times = (1ull << 0),
+ quote_infinities_and_nans = (1ull << 1),
+ allow_literal_strings = (1ull << 2),
+ allow_multi_line_strings = (1ull << 3),
+ allow_real_tabs_in_values = (1ull << 4),
+ allow_unicode_strings = (1ull << 5),
+ allow_binary_integers = (1ull << 6),
+ allow_octal_integers = (1ull << 7),
+ allow_hexadecimal_integers = (1ull << 8),
+ indent_sub_tables = (1ull << 9),
+ indent_array_elements = (1ull << 10),
+ indentation = indent_sub_tables | indent_array_elements,
+ relaxed_float_precision = (1ull << 11),
+ terse_key_value_pairs = (1ull << 12),
+};
+```
+
+### Flag Details
+
+| Flag | Effect |
+|------|--------|
+| `quote_dates_and_times` | Emit dates/times as `"2024-01-15"` instead of `2024-01-15` |
+| `quote_infinities_and_nans` | Emit `"inf"` / `"nan"` as quoted strings |
+| `allow_literal_strings` | Use `'single quotes'` where possible |
+| `allow_multi_line_strings` | Use `"""multi-line"""` where appropriate |
+| `allow_real_tabs_in_values` | Emit `\t` as literal tab instead of escape |
+| `allow_unicode_strings` | Keep Unicode characters instead of escaping to `\uXXXX` |
+| `allow_binary_integers` | Emit `0b1010` for binary-flagged integers |
+| `allow_octal_integers` | Emit `0o755` for octal-flagged integers |
+| `allow_hexadecimal_integers` | Emit `0xFF` for hex-flagged integers |
+| `indent_sub_tables` | Indent sub-table content |
+| `indent_array_elements` | Indent array elements on separate lines |
+| `relaxed_float_precision` | Use less precision for floats |
+| `terse_key_value_pairs` | Emit `key=value` instead of `key = value` |
+
+---
+
+## TOML Formatter
+
+### Constants
+
+```cpp
+static constexpr formatter_constants toml_formatter_constants = {
+ // mandatory_flags:
+ format_flags::allow_literal_strings
+ | format_flags::allow_multi_line_strings
+ | format_flags::allow_unicode_strings
+ | format_flags::allow_binary_integers
+ | format_flags::allow_octal_integers
+ | format_flags::allow_hexadecimal_integers,
+
+ // ignored_flags:
+ format_flags::quote_dates_and_times
+ | format_flags::quote_infinities_and_nans,
+
+ // float_pos_inf, float_neg_inf, float_nan:
+ "inf"sv, "-inf"sv, "nan"sv,
+
+ // bool_true, bool_false:
+ "true"sv, "false"sv
+};
+```
+
+### Default Flags
+
+```cpp
+static constexpr format_flags default_flags =
+ format_flags::allow_literal_strings
+ | format_flags::allow_multi_line_strings
+ | format_flags::allow_unicode_strings
+ | format_flags::allow_binary_integers
+ | format_flags::allow_octal_integers
+ | format_flags::allow_hexadecimal_integers
+ | format_flags::indentation;
+```
+
+### Construction
+
+```cpp
+// Format a table (most common)
+toml::toml_formatter fmt{ my_table };
+std::cout << fmt;
+
+// With custom flags
+toml::toml_formatter fmt2{ my_table, format_flags::indent_sub_tables };
+std::cout << fmt2;
+
+// Format any node (array, value, etc.)
+toml::toml_formatter fmt3{ my_array };
+std::cout << fmt3;
+```
+
+### Key Path Tracking
+
+The TOML formatter maintains a `key_path_` to correctly generate fully-qualified section headers:
+
+```cpp
+auto tbl = toml::parse(R"(
+ [server.database]
+ host = "localhost"
+ port = 5432
+)");
+
+std::cout << toml::toml_formatter{ tbl };
+```
+
+Output:
+```toml
+[server.database]
+host = "localhost"
+port = 5432
+```
+
+### Inline Tables
+
+Tables marked as inline are output as `{ key = val, ... }`:
+
+```cpp
+auto tbl = toml::table{
+ { "point", toml::table{ { "x", 1 }, { "y", 2 } } }
+};
+tbl["point"].as_table()->is_inline(true);
+
+std::cout << toml::toml_formatter{ tbl };
+// Output: point = { x = 1, y = 2 }
+```
+
+### Streaming
+
+The default `operator<<` for nodes uses `toml_formatter`:
+
+```cpp
+auto tbl = toml::parse("key = 42");
+std::cout << tbl << "\n";
+// Equivalent to: std::cout << toml::toml_formatter{ tbl } << "\n";
+```
+
+---
+
+## JSON Formatter
+
+### Constants
+
+```cpp
+static constexpr formatter_constants json_formatter_constants = {
+ // mandatory_flags:
+ format_flags::quote_dates_and_times
+ | format_flags::quote_infinities_and_nans,
+
+ // ignored_flags:
+ format_flags::allow_literal_strings
+ | format_flags::allow_multi_line_strings
+ | format_flags::allow_binary_integers
+ | format_flags::allow_octal_integers
+ | format_flags::allow_hexadecimal_integers,
+
+ // float_pos_inf, float_neg_inf, float_nan:
+ "Infinity"sv, "-Infinity"sv, "NaN"sv,
+
+ // bool_true, bool_false:
+ "true"sv, "false"sv
+};
+```
+
+### Key Differences from TOML Formatter
+
+- **Dates and times** are always quoted: `"2024-01-15"` instead of `2024-01-15`
+- **Infinity and NaN** are quoted: `"Infinity"`, `"-Infinity"`, `"NaN"`
+- **Integers** always in decimal (no `0xFF`, `0o777`, `0b1010`)
+- **Object keys** always quoted
+- **No section headers** — uses nested `{ }` structure
+- **Commas** separate elements
+
+### Default Flags
+
+```cpp
+static constexpr format_flags default_flags =
+ format_flags::quote_dates_and_times
+ | format_flags::quote_infinities_and_nans
+ | format_flags::indentation;
+```
+
+### Usage
+
+```cpp
+auto tbl = toml::parse(R"(
+ [server]
+ host = "localhost"
+ port = 8080
+ tags = ["web", "api"]
+)");
+
+std::cout << toml::json_formatter{ tbl } << "\n";
+```
+
+Output:
+```json
+{
+ "server": {
+ "host": "localhost",
+ "port": 8080,
+ "tags": [
+ "web",
+ "api"
+ ]
+ }
+}
+```
+
+### Transcoding Example
+
+From the `examples/toml_to_json_transcoder.cpp`:
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+
+int main(int argc, char** argv)
+{
+ toml::table tbl;
+ try
+ {
+ tbl = toml::parse(std::cin, "stdin"sv);
+ }
+ catch (const toml::parse_error& err)
+ {
+ std::cerr << err << "\n";
+ return 1;
+ }
+
+ std::cout << toml::json_formatter{ tbl } << "\n";
+ return 0;
+}
+```
+
+---
+
+## YAML Formatter
+
+### Constants
+
+```cpp
+static constexpr formatter_constants yaml_formatter_constants = {
+ // mandatory_flags:
+ format_flags::quote_dates_and_times,
+
+ // ignored_flags:
+ format_flags::allow_literal_strings
+ | format_flags::allow_multi_line_strings
+ | format_flags::allow_binary_integers
+ | format_flags::allow_octal_integers
+ | format_flags::allow_hexadecimal_integers,
+
+ // float_pos_inf, float_neg_inf, float_nan:
+ ".inf"sv, "-.inf"sv, ".nan"sv,
+
+ // bool_true, bool_false:
+ "true"sv, "false"sv
+};
+```
+
+### Key Differences
+
+- **Indentation** uses 2 spaces (indent level 1 = 2 spaces)
+- **No braces or brackets** — uses YAML's indentation-based structure
+- **Dates quoted**: `"2024-01-15"`
+- **Inf/NaN**: `.inf`, `-.inf`, `.nan` (YAML style)
+- **Array elements** prefixed with `- `
+- **No commas**
+
+### Default Flags
+
+```cpp
+static constexpr format_flags default_flags =
+ format_flags::quote_dates_and_times
+ | format_flags::allow_unicode_strings
+ | format_flags::indentation;
+```
+
+### Usage
+
+```cpp
+auto tbl = toml::parse(R"(
+ [server]
+ host = "localhost"
+ port = 8080
+ tags = ["web", "api"]
+)");
+
+std::cout << toml::yaml_formatter{ tbl } << "\n";
+```
+
+Output:
+```yaml
+server:
+ host: "localhost"
+ port: 8080
+ tags:
+ - "web"
+ - "api"
+```
+
+---
+
+## Printing Individual Nodes
+
+Any node can be formatted, not just tables:
+
+```cpp
+auto arr = toml::array{ 1, 2, 3, "four" };
+std::cout << toml::toml_formatter{ arr } << "\n";
+// [1, 2, 3, "four"]
+
+std::cout << toml::json_formatter{ arr } << "\n";
+// [1, 2, 3, "four"]
+
+auto val = toml::value<std::string>{ "hello" };
+std::cout << toml::toml_formatter{ val } << "\n";
+// "hello"
+```
+
+---
+
+## Writing to Files
+
+```cpp
+#include <fstream>
+
+auto tbl = toml::parse_file("input.toml");
+
+// Write as TOML
+{
+ std::ofstream out("output.toml");
+ out << toml::toml_formatter{ tbl };
+}
+
+// Write as JSON
+{
+ std::ofstream out("output.json");
+ out << toml::json_formatter{ tbl };
+}
+
+// Write as YAML
+{
+ std::ofstream out("output.yaml");
+ out << toml::yaml_formatter{ tbl };
+}
+```
+
+---
+
+## Customizing Output
+
+### Disabling Indentation
+
+```cpp
+auto fmt = toml::toml_formatter{ tbl, format_flags::none };
+std::cout << fmt;
+```
+
+### Terse Key-Value Pairs
+
+```cpp
+auto fmt = toml::toml_formatter{
+ tbl,
+ format_flags::terse_key_value_pairs | format_flags::indentation
+};
+// Output: key=value instead of key = value
+```
+
+### Preserving Source Format
+
+By default, integer format flags from parsing are preserved. A value parsed from `0xFF` will serialize back as `0xFF`:
+
+```cpp
+auto tbl = toml::parse("mask = 0xFF");
+std::cout << tbl << "\n";
+// Output: mask = 0xFF
+```
+
+This works because the parser sets `value_flags::format_as_hexadecimal` on the value, and the TOML formatter has `allow_hexadecimal_integers` in its mandatory flags.
+
+---
+
+## Formatter Comparison
+
+| Feature | `toml_formatter` | `json_formatter` | `yaml_formatter` |
+|---------|-----------------|-----------------|-----------------|
+| Format | TOML v1.0 | JSON | YAML |
+| Indentation | Tab (default) | 4 spaces | 2 spaces |
+| Infinity | `inf` | `"Infinity"` | `.inf` |
+| NaN | `nan` | `"NaN"` | `.nan` |
+| Dates | Unquoted | Quoted | Quoted |
+| Integer formats | Hex/Oct/Bin | Decimal only | Decimal only |
+| Literal strings | Yes | No | No |
+| Multi-line strings | Yes | No | No |
+| Section headers | `[table]` | N/A | N/A |
+| Inline tables | `{ k = v }` | N/A | N/A |
+
+---
+
+## Complete Example
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+#include <sstream>
+
+int main()
+{
+ auto config = toml::parse(R"(
+ title = "My Config"
+ debug = true
+ max_connections = 0xFF
+
+ [server]
+ host = "localhost"
+ port = 8080
+ started = 2024-01-15T10:30:00Z
+
+ [server.ssl]
+ enabled = true
+ cert = "/etc/ssl/cert.pem"
+
+ [[server.routes]]
+ path = "/"
+ handler = "index"
+
+ [[server.routes]]
+ path = "/api"
+ handler = "api"
+ )");
+
+ // TOML output (the default)
+ std::cout << "=== TOML ===\n";
+ std::cout << config << "\n\n";
+
+ // JSON output
+ std::cout << "=== JSON ===\n";
+ std::cout << toml::json_formatter{ config } << "\n\n";
+
+ // YAML output
+ std::cout << "=== YAML ===\n";
+ std::cout << toml::yaml_formatter{ config } << "\n\n";
+
+ // Terse TOML
+ std::cout << "=== Terse TOML ===\n";
+ std::cout << toml::toml_formatter{
+ config,
+ toml::format_flags::terse_key_value_pairs
+ | toml::format_flags::indentation
+ } << "\n";
+
+ // Format to string
+ std::ostringstream ss;
+ ss << toml::json_formatter{ config };
+ std::string json_string = ss.str();
+
+ return 0;
+}
+```
+
+---
+
+## Related Documentation
+
+- [parsing.md](parsing.md) — Parsing TOML into node trees
+- [values.md](values.md) — Value flags affecting output format
+- [tables.md](tables.md) — Inline table formatting
+- [basic-usage.md](basic-usage.md) — Quick formatting examples
diff --git a/docs/handbook/tomlplusplus/node-system.md b/docs/handbook/tomlplusplus/node-system.md
new file mode 100644
index 0000000000..c34b531385
--- /dev/null
+++ b/docs/handbook/tomlplusplus/node-system.md
@@ -0,0 +1,625 @@
+# toml++ — Node System
+
+## Overview
+
+The node system is the core of toml++'s data model. Every element in a TOML document — tables, arrays, and leaf values — is represented as a `toml::node`. This document covers the base class interface, `node_view` for safe access, type checking mechanisms, value retrieval strategies, and visitation patterns.
+
+---
+
+## `toml::node` — The Base Class
+
+`toml::node` is an abstract base class (`TOML_ABSTRACT_INTERFACE`) declared in `include/toml++/impl/node.hpp`. It cannot be instantiated directly; only its derived classes (`table`, `array`, `value<T>`) can.
+
+### Source Tracking
+
+Every node stores a `source_region` tracking its origin in the parsed document:
+
+```cpp
+class node
+{
+ private:
+ source_region source_{};
+
+ public:
+ const source_region& source() const noexcept;
+};
+```
+
+For programmatically-constructed nodes, `source()` returns a default-constructed region (all zeros). For parsed nodes, it contains the file path and begin/end line/column.
+
+### Lifetime
+
+```cpp
+ protected:
+ node() noexcept;
+ node(const node&) noexcept; // copies source_region
+ node(node&&) noexcept; // moves source_region
+ node& operator=(const node&) noexcept;
+ node& operator=(node&&) noexcept;
+
+ public:
+ virtual ~node() noexcept;
+```
+
+Constructors are protected — you create nodes by constructing `table`, `array`, or `value<T>` objects.
+
+---
+
+## Type Checking
+
+### Virtual Type Checks
+
+Every `node` provides a full set of virtual type-checking methods:
+
+```cpp
+virtual node_type type() const noexcept = 0;
+
+virtual bool is_table() const noexcept = 0;
+virtual bool is_array() const noexcept = 0;
+virtual bool is_array_of_tables() const noexcept;
+virtual bool is_value() const noexcept = 0;
+virtual bool is_string() const noexcept = 0;
+virtual bool is_integer() const noexcept = 0;
+virtual bool is_floating_point() const noexcept = 0;
+virtual bool is_number() const noexcept = 0;
+virtual bool is_boolean() const noexcept = 0;
+virtual bool is_date() const noexcept = 0;
+virtual bool is_time() const noexcept = 0;
+virtual bool is_date_time() const noexcept = 0;
+```
+
+`is_number()` returns `true` for both integers and floating-point values.
+
+`is_array_of_tables()` returns `true` only for arrays where every element is a table.
+
+### Template Type Check: `is<T>()`
+
+```cpp
+template <typename T>
+bool is() const noexcept;
+```
+
+Accepts any TOML node or value type. Uses `if constexpr` internally to dispatch:
+
+```cpp
+node.is<toml::table>() // equivalent to node.is_table()
+node.is<toml::array>() // equivalent to node.is_array()
+node.is<std::string>() // equivalent to node.is_string()
+node.is<int64_t>() // equivalent to node.is_integer()
+node.is<double>() // equivalent to node.is_floating_point()
+node.is<bool>() // equivalent to node.is_boolean()
+node.is<toml::date>() // equivalent to node.is_date()
+node.is<toml::time>() // equivalent to node.is_time()
+node.is<toml::date_time>() // equivalent to node.is_date_time()
+```
+
+You can also use the wrapped `value<T>` type:
+```cpp
+node.is<toml::value<int64_t>>() // same as node.is<int64_t>()
+```
+
+The `impl::unwrap_node<T>` trait unwraps `value<T>` → `T` and `node_view<T>` → `T`.
+
+### Compile-Time Type Traits
+
+The `toml` namespace provides type traits usable with `if constexpr`:
+
+```cpp
+// Type traits for use in generic/template code
+toml::is_table<decltype(val)> // true if val is table or node_view of table
+toml::is_array<decltype(val)> // true if val is array or node_view of array
+toml::is_string<decltype(val)> // true if val is value<std::string>
+toml::is_integer<decltype(val)> // true if val is value<int64_t>
+toml::is_floating_point<decltype(val)> // true if val is value<double>
+toml::is_number<decltype(val)> // integer or floating-point
+toml::is_boolean<decltype(val)> // true if val is value<bool>
+toml::is_date<decltype(val)> // true if val is value<date>
+toml::is_time<decltype(val)> // true if val is value<time>
+toml::is_date_time<decltype(val)> // true if val is value<date_time>
+toml::is_value<T> // true for any native value type
+toml::is_container<T> // true for table or array
+```
+
+These are critical for `for_each()` visitors:
+
+```cpp
+tbl.for_each([](auto& key, auto& value)
+{
+ if constexpr (toml::is_string<decltype(value)>)
+ std::cout << key << " is a string: " << value.get() << "\n";
+ else if constexpr (toml::is_integer<decltype(value)>)
+ std::cout << key << " is an integer: " << value.get() << "\n";
+ else if constexpr (toml::is_table<decltype(value)>)
+ std::cout << key << " is a table with " << value.size() << " entries\n";
+});
+```
+
+### `node_type` Enum
+
+Runtime type identification uses the `node_type` enum:
+
+```cpp
+enum class node_type : uint8_t
+{
+ none, // sentinel / empty
+ table,
+ array,
+ string,
+ integer,
+ floating_point,
+ boolean,
+ date,
+ time,
+ date_time
+};
+```
+
+Usage:
+```cpp
+switch (node.type())
+{
+ case toml::node_type::string: /* ... */ break;
+ case toml::node_type::table: /* ... */ break;
+ // ...
+}
+```
+
+`node_type` is streamable:
+```cpp
+std::cout << node.type() << "\n"; // prints "string", "integer", etc.
+```
+
+---
+
+## Type Casts: `as<T>()` and Friends
+
+### Virtual Cast Methods
+
+```cpp
+// Return pointer if this node IS that type, nullptr otherwise
+virtual table* as_table() noexcept = 0;
+virtual array* as_array() noexcept = 0;
+virtual toml::value<std::string>* as_string() noexcept = 0;
+virtual toml::value<int64_t>* as_integer() noexcept = 0;
+virtual toml::value<double>* as_floating_point() noexcept = 0;
+virtual toml::value<bool>* as_boolean() noexcept = 0;
+virtual toml::value<date>* as_date() noexcept = 0;
+virtual toml::value<time>* as_time() noexcept = 0;
+virtual toml::value<date_time>* as_date_time() noexcept = 0;
+// + const overloads
+```
+
+Each derived class implements these: `table::as_table()` returns `this`, all others return `nullptr`; `value<int64_t>::as_integer()` returns `this`, all others return `nullptr`.
+
+### Template Cast: `as<T>()`
+
+```cpp
+template <typename T>
+impl::wrap_node<T>* as() noexcept;
+
+template <typename T>
+const impl::wrap_node<T>* as() const noexcept;
+```
+
+Dispatches to the appropriate `as_*()` method. `impl::wrap_node<T>` wraps native types in `value<T>`:
+- `as<int64_t>()` → `value<int64_t>*` (via `as_integer()`)
+- `as<toml::table>()` → `table*` (via `as_table()`)
+- `as<toml::value<int64_t>>()` → `value<int64_t>*` (same as above)
+
+Usage:
+```cpp
+if (auto* tbl = node.as<toml::table>())
+ std::cout << "Table with " << tbl->size() << " entries\n";
+
+if (auto* val = node.as<int64_t>())
+ std::cout << "Integer: " << val->get() << "\n";
+```
+
+### Reference Access: `ref<T>()`
+
+```cpp
+template <typename T>
+decltype(auto) ref() & noexcept;
+template <typename T>
+decltype(auto) ref() && noexcept;
+template <typename T>
+decltype(auto) ref() const& noexcept;
+template <typename T>
+decltype(auto) ref() const&& noexcept;
+```
+
+Returns a **direct reference** to the underlying value. Unlike `as<T>()`, this does not return a pointer and does not check the type at runtime — it is **undefined behavior** to call `ref<T>()` with the wrong type. It asserts in debug builds.
+
+```cpp
+// Only safe if you KNOW the type
+int64_t& val = node.ref<int64_t>();
+```
+
+---
+
+## Value Retrieval
+
+### `value<T>()` — Permissive Retrieval
+
+```cpp
+template <typename T>
+optional<T> value() const noexcept(...);
+```
+
+Returns the node's value, allowing type conversions:
+
+| Source Type | Target Type | Behavior |
+|-------------|-------------|----------|
+| `int64_t` | `int64_t` | Exact |
+| `int64_t` | `double` | Converts |
+| `int64_t` | `int32_t` | Converts if lossless (range check) |
+| `int64_t` | `uint32_t` | Converts if lossless (range check) |
+| `double` | `double` | Exact |
+| `double` | `int64_t` | No (returns empty) |
+| `bool` | `bool` | Exact |
+| `bool` | `int64_t` | Converts (0 or 1) |
+| `std::string` | `std::string_view` | Returns view |
+| `std::string` | `std::string` | Returns copy |
+| `date` | `date` | Exact |
+| `time` | `time` | Exact |
+| `date_time` | `date_time` | Exact |
+
+```cpp
+auto tbl = toml::parse("val = 42");
+
+// These all work:
+auto as_i64 = tbl["val"].value<int64_t>(); // 42
+auto as_dbl = tbl["val"].value<double>(); // 42.0
+auto as_i32 = tbl["val"].value<int32_t>(); // 42
+auto as_u16 = tbl["val"].value<uint16_t>(); // 42
+
+// Returns empty:
+auto as_str = tbl["val"].value<std::string>(); // nullopt (int != string)
+```
+
+### `value_exact<T>()` — Strict Retrieval
+
+```cpp
+template <typename T>
+optional<T> value_exact() const noexcept(...);
+```
+
+Only returns a value if the node's native type matches exactly:
+
+```cpp
+auto tbl = toml::parse("val = 42");
+
+auto exact = tbl["val"].value_exact<int64_t>(); // 42
+auto wrong = tbl["val"].value_exact<double>(); // nullopt (42 is integer, not float)
+auto wrong2 = tbl["val"].value_exact<int32_t>(); // nullopt (native type is int64_t)
+```
+
+Allowed target types for `value_exact<T>()`:
+- Native TOML types: `std::string`, `int64_t`, `double`, `bool`, `date`, `time`, `date_time`
+- Lossless representations: `std::string_view`, `const char*` (for strings), `std::wstring` (Windows)
+
+### `value_or()` — Retrieval with Default
+
+```cpp
+template <typename T>
+auto value_or(T&& default_value) const noexcept(...);
+```
+
+Returns the value if the node contains a compatible type, otherwise returns the default. The return type matches the default value's type.
+
+```cpp
+int64_t port = tbl["port"].value_or(8080); // 8080 if missing
+std::string_view host = tbl["host"].value_or("localhost"sv);
+
+// Works safely on missing paths:
+bool flag = tbl["section"]["missing"]["deep"].value_or(false);
+```
+
+---
+
+## `toml::node_view<T>` — Safe Optional Node Access
+
+### Purpose
+
+`node_view` wraps a `node*` (possibly null) and provides the full node interface with null safety. It's what `table::operator[]` returns.
+
+### Template Parameter
+
+```cpp
+template <typename ViewedType>
+class node_view
+{
+ static_assert(impl::is_one_of<ViewedType, toml::node, const toml::node>);
+ // ...
+ mutable viewed_type* node_ = nullptr;
+};
+```
+
+- `node_view<node>` — mutable view
+- `node_view<const node>` — const view
+
+### Construction
+
+```cpp
+node_view() noexcept = default; // empty view
+explicit node_view(viewed_type* node) noexcept; // from pointer
+explicit node_view(viewed_type& node) noexcept; // from reference
+```
+
+### Boolean Conversion
+
+```cpp
+explicit operator bool() const noexcept; // true if node_ != nullptr
+```
+
+### Chained Subscript
+
+The key design feature — subscript returns another `node_view`, enabling safe deep access:
+
+```cpp
+// operator[] on node_view
+node_view operator[](std::string_view key) const noexcept;
+node_view operator[](size_t index) const noexcept;
+node_view operator[](const toml::path& p) const noexcept;
+```
+
+If `node_` is null or isn't the right container type, returns an empty `node_view`. This makes arbitrarily deep access safe:
+
+```cpp
+// All of these are safe even if intermediate keys don't exist:
+auto v1 = tbl["a"]["b"]["c"].value_or(0);
+auto v2 = tbl["missing"]["doesn't"]["exist"].value_or("default"sv);
+```
+
+### Full Interface Mirror
+
+`node_view` provides all the same methods as `node`:
+- Type checks: `is_table()`, `is_string()`, `is<T>()`, etc.
+- Type casts: `as_table()`, `as_string()`, `as<T>()`, etc.
+- Value retrieval: `value<T>()`, `value_exact<T>()`, `value_or()`
+- Source access: `source()`
+- Visitation: `visit()`
+
+All safely return defaults/nullptr/empty-optionals when the view is empty.
+
+### `node()` Accessor
+
+```cpp
+viewed_type* node() const noexcept;
+```
+
+Returns the raw pointer, which may be `nullptr`.
+
+### Printing
+
+```cpp
+friend std::ostream& operator<<(std::ostream& os, const node_view& nv);
+```
+
+Prints the referenced node's value, or nothing if empty.
+
+---
+
+## Homogeneity Checking
+
+### `is_homogeneous()` — Check Element Type Uniformity
+
+Available on `node`, `table`, and `array`:
+
+```cpp
+// With node_type parameter:
+virtual bool is_homogeneous(node_type ntype) const noexcept = 0;
+
+// With out-parameter for first mismatch:
+virtual bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept = 0;
+
+// Template version:
+template <typename ElemType = void>
+bool is_homogeneous() const noexcept;
+```
+
+**Behavior:**
+- `node_type::none` → "are all elements the same type?" (any type, as long as consistent)
+- Any specific type → "are all elements this type?"
+- Returns `false` for empty containers
+
+```cpp
+auto arr = toml::array{ 1, 2, 3 };
+
+arr.is_homogeneous(toml::node_type::integer); // true
+arr.is_homogeneous(toml::node_type::string); // false
+arr.is_homogeneous(toml::node_type::none); // true (all same type)
+arr.is_homogeneous<int64_t>(); // true
+arr.is_homogeneous<double>(); // false
+arr.is_homogeneous(); // true (void = any consistent type)
+
+// Find the first mismatch:
+auto mixed = toml::array{ 1, 2, "oops" };
+toml::node* mismatch = nullptr;
+if (!mixed.is_homogeneous(toml::node_type::integer, mismatch))
+{
+ std::cout << "Mismatch at " << mismatch->source() << "\n";
+ std::cout << "Type: " << mismatch->type() << "\n"; // "string"
+}
+```
+
+For `value<T>` nodes, `is_homogeneous()` trivially returns `true` (a single value is always homogeneous with itself).
+
+---
+
+## Visitation with `visit()`
+
+```cpp
+template <typename Func>
+decltype(auto) visit(Func&& visitor) & noexcept(...);
+template <typename Func>
+decltype(auto) visit(Func&& visitor) && noexcept(...);
+template <typename Func>
+decltype(auto) visit(Func&& visitor) const& noexcept(...);
+template <typename Func>
+decltype(auto) visit(Func&& visitor) const&& noexcept(...);
+```
+
+Calls the visitor with the concrete derived type. The visitor must accept all possible types (use a generic lambda or overload set):
+
+```cpp
+node.visit([](auto& concrete)
+{
+ using T = std::remove_cvref_t<decltype(concrete)>;
+
+ if constexpr (std::is_same_v<T, toml::table>)
+ std::cout << "table with " << concrete.size() << " keys\n";
+ else if constexpr (std::is_same_v<T, toml::array>)
+ std::cout << "array with " << concrete.size() << " elements\n";
+ else
+ std::cout << "value: " << concrete.get() << "\n";
+});
+```
+
+The visitor receives one of:
+- `table&` / `const table&`
+- `array&` / `const array&`
+- `value<std::string>&` / `const value<std::string>&`
+- `value<int64_t>&` / `const value<int64_t>&`
+- `value<double>&` / `const value<double>&`
+- `value<bool>&` / `const value<bool>&`
+- `value<date>&` / `const value<date>&`
+- `value<time>&` / `const value<time>&`
+- `value<date_time>&` / `const value<date_time>&`
+
+### Return Values
+
+If your visitor returns a value, `visit()` returns it. All branches must return the same type:
+
+```cpp
+std::string desc = node.visit([](auto& val) -> std::string
+{
+ using T = std::remove_cvref_t<decltype(val)>;
+ if constexpr (std::is_same_v<T, toml::table>)
+ return "table";
+ else if constexpr (std::is_same_v<T, toml::array>)
+ return "array";
+ else
+ return "value";
+});
+```
+
+---
+
+## `for_each()` Iteration
+
+Available on `table` and `array`:
+
+### On Tables
+
+```cpp
+template <typename Func>
+table& for_each(Func&& visitor) &;
+```
+
+The visitor receives `(const toml::key& key, auto& value)` where `value` is the concrete type:
+
+```cpp
+tbl.for_each([](const toml::key& key, auto& value)
+{
+ std::cout << key.str() << " (" << key.source().begin.line << "): ";
+
+ if constexpr (toml::is_string<decltype(value)>)
+ std::cout << '"' << value.get() << "\"\n";
+ else if constexpr (toml::is_integer<decltype(value)>)
+ std::cout << value.get() << "\n";
+ else if constexpr (toml::is_table<decltype(value)>)
+ std::cout << "{...}\n";
+ else
+ std::cout << value << "\n";
+});
+```
+
+### On Arrays
+
+```cpp
+template <typename Func>
+array& for_each(Func&& visitor) &;
+```
+
+The visitor receives `(size_t index, auto& element)` or just `(auto& element)`:
+
+```cpp
+arr.for_each([](size_t idx, auto& elem)
+{
+ std::cout << "[" << idx << "] " << elem << "\n";
+});
+```
+
+### Early Exit
+
+On compilers without the GCC 7 bug (`TOML_RETURN_BOOL_FROM_FOR_EACH_BROKEN == 0`), your visitor can return `bool` to stop iteration early:
+
+```cpp
+tbl.for_each([](const toml::key& key, auto& value) -> bool
+{
+ if (key.str() == "stop_here")
+ return false; // stop iteration
+ std::cout << key << "\n";
+ return true; // continue
+});
+```
+
+---
+
+## `impl::unwrap_node<T>` and `impl::wrap_node<T>`
+
+These internal type traits handle the mapping between user-facing types and internal node types:
+
+- `unwrap_node<value<int64_t>>` → `int64_t`
+- `unwrap_node<int64_t>` → `int64_t` (no change)
+- `unwrap_node<node_view<node>>` → `node` (extracted viewed type)
+- `wrap_node<int64_t>` → `value<int64_t>`
+- `wrap_node<table>` → `table` (no change)
+- `wrap_node<array>` → `array` (no change)
+
+These ensure that `as<int64_t>()` returns `value<int64_t>*` and `as<table>()` returns `table*`.
+
+---
+
+## Node Comparison
+
+Nodes support equality comparison:
+
+```cpp
+// Same type and value → equal
+toml::value<int64_t> a(42);
+toml::value<int64_t> b(42);
+bool eq = (a == b); // true
+
+// Cross-type comparison is always false
+toml::value<int64_t> i(42);
+toml::value<double> d(42.0);
+bool eq2 = (i == d); // false (different node types)
+
+// Tables and arrays compare structurally (deep equality)
+```
+
+---
+
+## Summary: Choosing the Right Accessor
+
+| Need | Method | Returns | Null Safe |
+|------|--------|---------|-----------|
+| Check if key exists | `tbl["key"]` then `operator bool()` | `node_view` | Yes |
+| Get value or default | `value_or(default)` | Value type | Yes |
+| Get value, might be absent | `value<T>()` | `optional<T>` | Yes |
+| Get exact-type value | `value_exact<T>()` | `optional<T>` | Yes |
+| Get typed pointer | `as<T>()` | `T*` or `nullptr` | Yes |
+| Direct reference (unsafe) | `ref<T>()` | `T&` | No (UB if wrong type) |
+| Raw node pointer | `get(key)` | `node*` | Yes (returns null) |
+| Typed node pointer | `get_as<T>(key)` | `wrap_node<T>*` | Yes (returns null) |
+
+---
+
+## Related Documentation
+
+- [architecture.md](architecture.md) — Overall class hierarchy
+- [tables.md](tables.md) — Table-specific operations
+- [arrays.md](arrays.md) — Array-specific operations
+- [values.md](values.md) — Value template details
diff --git a/docs/handbook/tomlplusplus/overview.md b/docs/handbook/tomlplusplus/overview.md
new file mode 100644
index 0000000000..d8ba7df5aa
--- /dev/null
+++ b/docs/handbook/tomlplusplus/overview.md
@@ -0,0 +1,474 @@
+# toml++ (tomlplusplus) — Overview
+
+## What Is toml++?
+
+toml++ is a header-only TOML v1.0 parser, serializer, and data model library for C++17 and later. It is authored by Mark Gillard and published under the MIT license. The library version as of this documentation is **3.4.0**, implementing TOML language specification version **1.0.0**.
+
+The library lives in the `toml` namespace and provides a complete object model for TOML documents: tables, arrays, and typed values. It can parse TOML from strings, streams, and files; manipulate the resulting tree programmatically; and serialize back to TOML, JSON, or YAML.
+
+Repository: [https://github.com/marzer/tomlplusplus](https://github.com/marzer/tomlplusplus)
+
+---
+
+## What Is TOML?
+
+TOML stands for **Tom's Obvious Minimal Language**. It is a configuration file format designed to be easy to read, unambiguous, and map cleanly to a hash table (dictionary). A TOML document is fundamentally a collection of key-value pairs organized into tables.
+
+### TOML Data Types
+
+TOML defines the following native data types:
+
+| TOML Type | C++ Representation in toml++ | `node_type` Enum |
+|-----------------|------------------------------|-------------------------------|
+| String | `std::string` | `node_type::string` |
+| Integer | `int64_t` | `node_type::integer` |
+| Float | `double` | `node_type::floating_point` |
+| Boolean | `bool` | `node_type::boolean` |
+| Local Date | `toml::date` | `node_type::date` |
+| Local Time | `toml::time` | `node_type::time` |
+| Offset Date-Time| `toml::date_time` | `node_type::date_time` |
+| Array | `toml::array` | `node_type::array` |
+| Table | `toml::table` | `node_type::table` |
+
+### Example TOML Document
+
+```toml
+# This is a TOML document
+
+title = "TOML Example"
+
+[owner]
+name = "Tom Preston-Werner"
+dob = 1979-05-27T07:32:00-08:00
+
+[database]
+enabled = true
+ports = [ 8000, 8001, 8002 ]
+data = [ ["delta", "phi"], [3.14] ]
+temp_targets = { cpu = 79.5, case = 72.0 }
+
+[servers]
+
+[servers.alpha]
+ip = "10.0.0.1"
+role = "frontend"
+
+[servers.beta]
+ip = "10.0.0.2"
+role = "backend"
+```
+
+---
+
+## C++17 Features Used
+
+toml++ requires C++17 as its minimum standard. The version detection logic in `preprocessor.hpp` checks `__cplusplus` and `_MSVC_LANG`, rejecting anything below C++17:
+
+```cpp
+#if TOML_CPP < 17
+#error toml++ requires C++17 or higher.
+#endif
+```
+
+Key C++17 features utilized throughout the library:
+
+### `std::string_view`
+Used pervasively for zero-copy string references in parsing, key lookups, path parsing, and formatting. The parser accepts `std::string_view` for document content and source paths.
+
+### `std::optional<T>`
+Returned by value retrieval functions like `node::value<T>()` and `node::value_exact<T>()`. The library also supports a custom optional type via `TOML_HAS_CUSTOM_OPTIONAL_TYPE`.
+
+### `if constexpr`
+Used extensively in template code for compile-time type dispatch. For example, `node::is<T>()` and `node::as<T>()` use `if constexpr` chains to dispatch to the correct type check or cast:
+
+```cpp
+template <typename T>
+bool is() const noexcept
+{
+ using type = impl::remove_cvref<impl::unwrap_node<T>>;
+ if constexpr (std::is_same_v<type, table>)
+ return is_table();
+ else if constexpr (std::is_same_v<type, array>)
+ return is_array();
+ else if constexpr (std::is_same_v<type, std::string>)
+ return is_string();
+ // ...
+}
+```
+
+### Structured Bindings
+Table iteration supports structured bindings:
+
+```cpp
+for (auto&& [key, value] : my_table)
+{
+ std::cout << key << " = " << value << "\n";
+}
+```
+
+### Fold Expressions
+Used in template parameter packs throughout the implementation, such as in `impl::all_integral<>` constraints for date/time constructors.
+
+### `std::variant` / `std::any` Awareness
+While not directly depending on `std::variant`, the library includes `std_variant.hpp` for platforms that need it. The node hierarchy itself is polymorphic (virtual dispatch), not variant-based.
+
+### Inline Variables
+Used for constants like `impl::node_type_friendly_names[]` and `impl::control_char_escapes[]`, which are declared `inline constexpr` in header files.
+
+### Class Template Argument Deduction (CTAD)
+`toml::value` supports CTAD for constructing values from native types without explicitly specifying the template parameter.
+
+---
+
+## C++20 Feature Support
+
+When compiled under C++20 or later, toml++ optionally supports:
+
+- **`char8_t` strings**: When `TOML_HAS_CHAR8` is true, the parser accepts `std::u8string_view` and `std::u8string` inputs. File paths can also be `char8_t`-based.
+- **C++20 Modules**: The library ships with experimental module support via `import tomlplusplus;`. Enabled by setting `TOMLPLUSPLUS_BUILD_MODULES=ON` in CMake (requires CMake 3.28+).
+
+---
+
+## Complete Feature List
+
+### Parsing
+- Parse TOML from `std::string_view`, `std::istream`, or files (`toml::parse()`, `toml::parse_file()`)
+- Full TOML v1.0.0 conformance — passes all tests in the [toml-test](https://github.com/toml-lang/toml-test) suite
+- Optional support for unreleased TOML features (e.g., unicode bare keys from toml/pull/891)
+- Proper UTF-8 handling including BOM detection and skipping
+- Detailed error reporting with source positions (`toml::parse_error`, `toml::source_region`)
+- Works with or without exceptions (`TOML_EXCEPTIONS` macro)
+- Support for `char8_t` strings (C++20)
+- Windows wide string compatibility (`TOML_ENABLE_WINDOWS_COMPAT`)
+
+### Data Model
+- Complete node type hierarchy: `toml::node` (abstract base) → `toml::table`, `toml::array`, `toml::value<T>`
+- `toml::node_view<T>` for safe, optional-like node access with chained subscript operators
+- `toml::key` class preserving source location information for parsed keys
+- Type-safe value access: `node::value<T>()`, `node::value_exact<T>()`, `node::value_or(default)`
+- Template `as<T>()` for casting nodes to concrete types
+- `is<T>()` family for type checking
+- Visitor pattern via `node::visit()` and `for_each()`
+- Homogeneity checking with `is_homogeneous()`
+
+### Manipulation
+- Construct tables and arrays programmatically using initializer lists
+- `table::insert()`, `table::insert_or_assign()`, `table::emplace()`
+- `array::push_back()`, `array::emplace_back()`, `array::insert()`
+- `table::erase()`, `array::erase()`
+- Deep copy via copy constructors
+- All containers are movable
+
+### Navigation
+- `operator[]` subscript chaining: `tbl["section"]["key"]`
+- `toml::at_path()` free function for dot-separated path lookup: `at_path(tbl, "section.key[0]")`
+- `toml::path` class for programmatic path construction and manipulation
+- `path::parent_path()`, `path::leaf()`, `path::subpath()`
+- Path components are either keys (`std::string`) or array indices (`size_t`)
+
+### Serialization
+- `toml::toml_formatter` — serialize as valid TOML (default when streaming nodes)
+- `toml::json_formatter` — serialize as JSON
+- `toml::yaml_formatter` — serialize as YAML
+- All formatters support `format_flags` for fine-grained output control
+- Format flags include: `indent_array_elements`, `indent_sub_tables`, `allow_literal_strings`, `allow_multi_line_strings`, `allow_unicode_strings`, `allow_real_tabs_in_strings`, `allow_binary_integers`, `allow_octal_integers`, `allow_hexadecimal_integers`, `quote_dates_and_times`, `quote_infinities_and_nans`, `terse_key_value_pairs`, `force_multiline_arrays`
+- Inline table detection (`table::is_inline()`)
+- Value flags for controlling integer format representation (`value_flags::format_as_binary`, `format_as_octal`, `format_as_hexadecimal`)
+
+### Build Modes
+- **Header-only** (default): just `#include <toml++/toml.hpp>`
+- **Single-header**: drop `toml.hpp` (root-level amalgamated file) into your project
+- **Compiled library**: define `TOML_HEADER_ONLY=0` and compile `src/toml.cpp`
+- **C++20 Modules**: `import tomlplusplus;`
+
+### Build Systems
+- Meson (primary, with full option support)
+- CMake (interface library target `tomlplusplus::tomlplusplus`)
+- Visual Studio solution files (`.sln`, `.vcxproj`)
+- Package managers: Conan, Vcpkg, DDS, tipi.build
+
+### Compiler Support
+- GCC 8+
+- Clang 8+ (including Apple Clang)
+- MSVC (VS2019+)
+- Intel C++ Compiler (ICC/ICL)
+- NVIDIA CUDA Compiler (NVCC) with workarounds
+
+### Platform Support
+- x86, x64, ARM architectures
+- Windows, Linux, macOS
+- Does not require RTTI
+- Works with or without exceptions
+
+---
+
+## Version Information
+
+Version constants are defined in `include/toml++/impl/version.hpp`:
+
+```cpp
+#define TOML_LIB_MAJOR 3
+#define TOML_LIB_MINOR 4
+#define TOML_LIB_PATCH 0
+
+#define TOML_LANG_MAJOR 1
+#define TOML_LANG_MINOR 0
+#define TOML_LANG_PATCH 0
+```
+
+- `TOML_LIB_MAJOR/MINOR/PATCH` — the library version (3.4.0)
+- `TOML_LANG_MAJOR/MINOR/PATCH` — the TOML specification version implemented (1.0.0)
+
+---
+
+## Configuration Macros
+
+toml++ is heavily configurable via preprocessor macros. Key ones include:
+
+| Macro | Default | Description |
+|-------|---------|-------------|
+| `TOML_HEADER_ONLY` | `1` | When `1`, the library is header-only. Set to `0` for compiled mode. |
+| `TOML_EXCEPTIONS` | auto-detected | Whether to use exceptions. Auto-detected from compiler settings. |
+| `TOML_ENABLE_PARSER` | `1` | Set to `0` to disable the parser entirely (serialization only). |
+| `TOML_ENABLE_FORMATTERS` | `1` | Set to `0` to disable all formatters. |
+| `TOML_ENABLE_WINDOWS_COMPAT` | `1` on Windows | Enables `std::wstring` overloads for Windows. |
+| `TOML_UNRELEASED_FEATURES` | `0` | Enable support for unreleased TOML spec features. |
+| `TOML_HAS_CUSTOM_OPTIONAL_TYPE` | `0` | Define with a custom optional type to use instead of `std::optional`. |
+| `TOML_DISABLE_ENVIRONMENT_CHECKS` | undefined | Define to skip compile-time environment validation. |
+
+### Environment Ground-Truths
+
+The library validates its environment at compile time (unless `TOML_DISABLE_ENVIRONMENT_CHECKS` is defined):
+
+```cpp
+static_assert(CHAR_BIT == 8, TOML_ENV_MESSAGE);
+static_assert('A' == 65, TOML_ENV_MESSAGE); // ASCII
+static_assert(sizeof(double) == 8, TOML_ENV_MESSAGE);
+static_assert(std::numeric_limits<double>::is_iec559, TOML_ENV_MESSAGE); // IEEE 754
+```
+
+These ensure the library operates on platforms with 8-bit bytes, ASCII character encoding, and IEEE 754 double-precision floats.
+
+---
+
+## Namespace Organization
+
+The library uses a layered namespace structure:
+
+- **`toml`** — The root namespace containing all public API types: `table`, `array`, `value<T>`, `node`, `node_view`, `key`, `path`, `date`, `time`, `date_time`, `source_position`, `source_region`, `parse_error`, `parse_result`, etc.
+- **`toml::impl`** (internal) — Implementation details not part of the public API. Contains the parser, formatter base class, iterator implementations, and type trait utilities.
+- **ABI namespaces** — Conditional inline namespaces (e.g., `ex`/`noex` for exception mode, `custopt`/`stdopt` for optional type) ensure ABI compatibility when linking translation units compiled with different settings.
+
+---
+
+## Type Traits and Concepts
+
+The `toml` namespace provides several type trait utilities:
+
+```cpp
+toml::is_value<T> // true for native value types (std::string, int64_t, double, bool, date, time, date_time)
+toml::is_container<T> // true for table and array
+toml::is_string<T> // true if T is a toml::value<std::string> or node_view thereof
+toml::is_integer<T> // true if T is a toml::value<int64_t> or node_view thereof
+toml::is_floating_point<T>
+toml::is_number<T>
+toml::is_boolean<T>
+toml::is_date<T>
+toml::is_time<T>
+toml::is_date_time<T>
+toml::is_table<T>
+toml::is_array<T>
+```
+
+These are usable in `if constexpr` and `static_assert` contexts, making generic TOML-processing code straightforward.
+
+---
+
+## The `node_type` Enumeration
+
+Defined in the forward declarations, `node_type` identifies the kind of a TOML node:
+
+```cpp
+enum class node_type : uint8_t
+{
+ none, // Not a valid node type (used as nil sentinel)
+ table, // toml::table
+ array, // toml::array
+ string, // toml::value<std::string>
+ integer, // toml::value<int64_t>
+ floating_point, // toml::value<double>
+ boolean, // toml::value<bool>
+ date, // toml::value<toml::date>
+ time, // toml::value<toml::time>
+ date_time // toml::value<toml::date_time>
+};
+```
+
+Friendly display names are available via `impl::node_type_friendly_names[]`:
+`"none"`, `"table"`, `"array"`, `"string"`, `"integer"`, `"floating-point"`, `"boolean"`, `"date"`, `"time"`, `"date-time"`.
+
+---
+
+## The `value_flags` Enumeration
+
+Controls how integer values are formatted during serialization:
+
+```cpp
+enum class value_flags : uint16_t
+{
+ none = 0,
+ format_as_binary = 1, // 0b...
+ format_as_octal = 2, // 0o...
+ format_as_hexadecimal = 3 // 0x...
+};
+```
+
+The sentinel value `preserve_source_value_flags` tells the library to keep whatever format the parser originally detected.
+
+---
+
+## The `format_flags` Enumeration
+
+Controls formatter output behavior. It is a bitmask enum:
+
+```cpp
+enum class format_flags : uint64_t
+{
+ none = 0,
+ quote_dates_and_times = 1,
+ quote_infinities_and_nans = 2,
+ allow_literal_strings = 4,
+ allow_multi_line_strings = 8,
+ allow_real_tabs_in_strings = 16,
+ allow_unicode_strings = 32,
+ allow_binary_integers = 64,
+ allow_octal_integers = 128,
+ allow_hexadecimal_integers = 256,
+ indent_sub_tables = 512,
+ indent_array_elements = 1024,
+ indentation = indent_sub_tables | indent_array_elements,
+ terse_key_value_pairs = 2048,
+ force_multiline_arrays = 4096
+};
+```
+
+Each formatter has its own `default_flags` static constant and a set of mandatory/ignored flags defined in `formatter_constants`.
+
+---
+
+## Minimal Usage Example
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+
+int main()
+{
+ // Parse a TOML string
+ auto config = toml::parse(R"(
+ [server]
+ host = "localhost"
+ port = 8080
+ debug = false
+ )");
+
+ // Read values
+ std::string_view host = config["server"]["host"].value_or("0.0.0.0"sv);
+ int64_t port = config["server"]["port"].value_or(80);
+ bool debug = config["server"]["debug"].value_or(true);
+
+ std::cout << "Host: " << host << "\n";
+ std::cout << "Port: " << port << "\n";
+ std::cout << "Debug: " << std::boolalpha << debug << "\n";
+
+ // Serialize back to TOML
+ std::cout << "\n" << config << "\n";
+
+ // Serialize as JSON
+ std::cout << toml::json_formatter{ config } << "\n";
+
+ return 0;
+}
+```
+
+---
+
+## File Organization
+
+```
+tomlplusplus/
+├── toml.hpp # Single-header amalgamation (drop-in)
+├── include/
+│ └── toml++/
+│ ├── toml.hpp # Main include (includes all impl headers)
+│ ├── toml.h # C-compatible header alias
+│ └── impl/
+│ ├── forward_declarations.hpp # All forward decls, type aliases
+│ ├── preprocessor.hpp # Compiler/platform detection, macros
+│ ├── version.hpp # Version constants
+│ ├── node.hpp # toml::node base class
+│ ├── node.inl # node method implementations
+│ ├── node_view.hpp # toml::node_view<T>
+│ ├── table.hpp # toml::table
+│ ├── table.inl # table method implementations
+│ ├── array.hpp # toml::array
+│ ├── array.inl # array method implementations
+│ ├── value.hpp # toml::value<T>
+│ ├── key.hpp # toml::key
+│ ├── date_time.hpp # toml::date, toml::time, toml::date_time
+│ ├── source_region.hpp # source_position, source_region
+│ ├── parser.hpp # toml::parse(), toml::parse_file()
+│ ├── parser.inl # Parser implementation
+│ ├── parse_error.hpp # toml::parse_error
+│ ├── parse_result.hpp # toml::parse_result (no-exceptions mode)
+│ ├── path.hpp # toml::path, toml::path_component
+│ ├── path.inl # Path implementation
+│ ├── at_path.hpp # toml::at_path() free function
+│ ├── at_path.inl # at_path implementation
+│ ├── formatter.hpp # impl::formatter base class
+│ ├── formatter.inl # formatter base implementation
+│ ├── toml_formatter.hpp # toml::toml_formatter
+│ ├── toml_formatter.inl # TOML formatter implementation
+│ ├── json_formatter.hpp # toml::json_formatter
+│ ├── json_formatter.inl # JSON formatter implementation
+│ ├── yaml_formatter.hpp # toml::yaml_formatter
+│ ├── yaml_formatter.inl # YAML formatter implementation
+│ ├── make_node.hpp # impl::make_node() factory
+│ ├── print_to_stream.hpp/.inl # Stream output helpers
+│ ├── unicode.hpp # Unicode utilities
+│ ├── unicode.inl # UTF-8 decoder
+│ ├── unicode_autogenerated.hpp # Auto-generated Unicode tables
+│ └── std_*.hpp # Standard library includes
+├── src/
+│ └── toml.cpp # Compiled-library translation unit
+├── tests/ # Catch2-based test suite
+├── examples/ # Example programs
+├── tools/ # Build/generation tools
+├── meson.build # Primary build system
+├── CMakeLists.txt # CMake build system
+└── toml-test/ # toml-test conformance suite integration
+```
+
+---
+
+## License
+
+toml++ is licensed under the **MIT License**. See `LICENSE` in the repository root for the full text.
+
+---
+
+## Related Documentation
+
+- [architecture.md](architecture.md) — Class hierarchy and internal design
+- [building.md](building.md) — Build instructions and integration
+- [basic-usage.md](basic-usage.md) — Common usage patterns
+- [node-system.md](node-system.md) — The node type system in depth
+- [tables.md](tables.md) — Working with toml::table
+- [arrays.md](arrays.md) — Working with toml::array
+- [values.md](values.md) — Working with toml::value<T>
+- [parsing.md](parsing.md) — Parser internals and error handling
+- [formatting.md](formatting.md) — Output formatters
+- [path-system.md](path-system.md) — Path-based navigation
+- [unicode-handling.md](unicode-handling.md) — UTF-8 and Unicode support
+- [code-style.md](code-style.md) — Code conventions
+- [testing.md](testing.md) — Test framework and conformance
diff --git a/docs/handbook/tomlplusplus/parsing.md b/docs/handbook/tomlplusplus/parsing.md
new file mode 100644
index 0000000000..ac97ba89e8
--- /dev/null
+++ b/docs/handbook/tomlplusplus/parsing.md
@@ -0,0 +1,494 @@
+# toml++ — Parsing
+
+## Overview
+
+toml++ provides a recursive-descent parser that converts TOML text into a `toml::table` tree of nodes. The parser handles the full TOML v1.0.0 specification, including all string types, numeric formats, date/time values, inline tables, and arrays of tables.
+
+Key entry points are `toml::parse()` and `toml::parse_file()`, declared in `include/toml++/impl/parser.hpp`.
+
+The parser can be disabled entirely via `TOML_ENABLE_PARSER=0`.
+
+---
+
+## Entry Points
+
+### `parse()` — Parse from String
+
+```cpp
+// From std::string_view (most common)
+parse_result parse(std::string_view doc, std::string_view source_path = {});
+parse_result parse(std::string_view doc, std::string&& source_path);
+
+// From std::istream
+parse_result parse(std::istream& doc, std::string_view source_path = {});
+parse_result parse(std::istream& doc, std::string&& source_path);
+
+// From char8_t (C++20 u8 strings)
+parse_result parse(std::u8string_view doc, std::string_view source_path = {});
+```
+
+The `source_path` parameter is stored in `source_region` data and appears in error messages. It does not affect parsing behavior.
+
+```cpp
+auto result = toml::parse(R"(
+ [server]
+ host = "localhost"
+ port = 8080
+)");
+
+// With source path for error messages:
+auto result2 = toml::parse(toml_string, "config.toml");
+```
+
+### `parse_file()` — Parse from File Path
+
+```cpp
+parse_result parse_file(std::string_view file_path);
+
+// Windows wstring overload (when TOML_ENABLE_WINDOWS_COMPAT=1)
+parse_result parse_file(std::wstring_view file_path);
+```
+
+Reads the entire file and parses it:
+
+```cpp
+auto config = toml::parse_file("settings.toml");
+auto config2 = toml::parse_file("/etc/myapp/config.toml");
+```
+
+---
+
+## parse_result
+
+`parse_result` is the return type from all parse functions. Its behavior depends on whether exceptions are enabled.
+
+Declared in `include/toml++/impl/parse_result.hpp`.
+
+### With Exceptions (`TOML_EXCEPTIONS=1`, the default)
+
+`parse_result` is simply a type alias for `toml::table`:
+
+```cpp
+// Effectively:
+using parse_result = table;
+```
+
+If parsing fails, `toml::parse_error` (derived from `std::runtime_error`) is thrown:
+
+```cpp
+try
+{
+ toml::table config = toml::parse("invalid [[[toml");
+}
+catch (const toml::parse_error& err)
+{
+ std::cerr << "Parse error: " << err.description() << "\n";
+ std::cerr << " at: " << err.source() << "\n";
+}
+```
+
+### Without Exceptions (`TOML_EXCEPTIONS=0`)
+
+`parse_result` is a discriminated union — it holds either a `toml::table` (success) or a `toml::parse_error` (failure):
+
+```cpp
+class parse_result
+{
+ public:
+ // Check success
+ bool succeeded() const noexcept;
+ bool failed() const noexcept;
+ explicit operator bool() const noexcept; // same as succeeded()
+
+ // Access the table (success)
+ table& get() & noexcept;
+ const table& get() const& noexcept;
+ table&& get() && noexcept;
+
+ table& operator*() & noexcept;
+ table* operator->() noexcept;
+
+ // Access the error (failure)
+ const parse_error& error() const& noexcept;
+
+ // Implicit conversion to table& (success only)
+ operator table&() noexcept;
+ operator const table&() const noexcept;
+};
+```
+
+Usage pattern:
+
+```cpp
+auto result = toml::parse("...");
+
+if (result)
+{
+ // Success — use the table
+ toml::table& config = result;
+ auto val = config["key"].value_or("default"sv);
+}
+else
+{
+ // Failure — inspect the error
+ std::cerr << "Error: " << result.error().description() << "\n";
+ std::cerr << " at " << result.error().source() << "\n";
+}
+```
+
+### Internal Storage (No-Exceptions Mode)
+
+`parse_result` uses aligned storage to hold either type:
+
+```cpp
+// Simplified internal layout:
+alignas(table) mutable unsigned char storage_[sizeof(table)];
+bool err_;
+parse_error err_val_; // only when err_ == true
+```
+
+The `table` is placement-new'd into `storage_` on success. On failure, `err_val_` is populated instead.
+
+---
+
+## parse_error
+
+Declared in `include/toml++/impl/parse_error.hpp`.
+
+### With Exceptions
+
+```cpp
+class parse_error : public std::runtime_error
+{
+ public:
+ std::string_view description() const noexcept;
+ const source_region& source() const noexcept;
+
+ // what() returns the description
+};
+```
+
+### Without Exceptions
+
+```cpp
+class parse_error
+{
+ public:
+ std::string_view description() const noexcept;
+ const source_region& source() const noexcept;
+
+ // Streaming
+ friend std::ostream& operator<<(std::ostream&, const parse_error&);
+};
+```
+
+### Error Information
+
+```cpp
+auto result = toml::parse("x = [1, 2,, 3]", "test.toml");
+
+if (!result)
+{
+ const auto& err = result.error();
+
+ // Human-readable description
+ std::cout << err.description() << "\n";
+ // e.g., "Unexpected character ',' (U+002C) in array"
+
+ // Source location
+ const auto& src = err.source();
+ std::cout << "File: " << *src.path << "\n"; // "test.toml"
+ std::cout << "Line: " << src.begin.line << "\n"; // 1
+ std::cout << "Col: " << src.begin.column << "\n"; // 11
+
+ // Full error with location (via operator<<)
+ std::cout << err << "\n";
+ // "Unexpected character ',' ... at line 1, column 11 of 'test.toml'"
+}
+```
+
+---
+
+## Source Tracking
+
+### `source_position`
+
+```cpp
+struct source_position
+{
+ source_index line; // 1-based line number
+ source_index column; // 1-based column number (byte offset, not codepoint)
+
+ explicit operator bool() const noexcept; // true if line > 0
+
+ friend bool operator==(const source_position&, const source_position&) noexcept;
+ friend bool operator!=(const source_position&, const source_position&) noexcept;
+ friend bool operator< (const source_position&, const source_position&) noexcept;
+ friend bool operator<=(const source_position&, const source_position&) noexcept;
+};
+```
+
+`source_index` is typically `uint32_t` (or `uint16_t` on small builds via `TOML_SMALL_INT_TYPE`).
+
+### `source_region`
+
+```cpp
+struct source_region
+{
+ source_position begin; // start of the element
+ source_position end; // end of the element
+ source_path_ptr path; // shared_ptr<const std::string>
+};
+```
+
+Every `node` in the parsed tree carries a `source_region`:
+
+```cpp
+auto tbl = toml::parse(R"(
+ name = "Alice"
+ age = 30
+)", "config.toml");
+
+const auto& src = tbl["name"].node()->source();
+std::cout << "Defined at "
+ << *src.path << ":"
+ << src.begin.line << ":"
+ << src.begin.column << "\n";
+// "Defined at config.toml:2:5"
+```
+
+`source_path_ptr` is `std::shared_ptr<const std::string>`, shared among all nodes parsed from the same file.
+
+---
+
+## Stream Parsing
+
+Parsing from `std::istream` allows reading from any stream source:
+
+```cpp
+#include <fstream>
+
+std::ifstream file("config.toml");
+auto config = toml::parse(file, "config.toml");
+
+// From stringstream
+std::istringstream ss(R"(key = "value")");
+auto tbl = toml::parse(ss);
+```
+
+The stream is consumed entirely during parsing.
+
+---
+
+## UTF-8 Handling During Parsing
+
+The parser expects UTF-8 encoded input. It handles:
+
+- **BOM stripping**: A leading UTF-8 BOM (`0xEF 0xBB 0xBF`) is silently consumed
+- **Multi-byte sequences**: Bare keys, strings, and comments can contain Unicode
+- **Escape sequences in strings**: `\uXXXX` and `\UXXXXXXXX` are decoded
+- **Validation**: Invalid UTF-8 sequences are rejected with parse errors
+- **Non-characters and surrogates**: Rejected as per the TOML specification
+
+### char8_t Support (C++20)
+
+```cpp
+auto tbl = toml::parse(u8R"(
+ greeting = "Hello, 世界"
+)"sv);
+```
+
+The `char8_t` overloads allow passing C++20 UTF-8 string literals directly.
+
+---
+
+## Windows Compatibility
+
+When `TOML_ENABLE_WINDOWS_COMPAT=1` (default on Windows):
+
+```cpp
+// Accept wstring file paths (converted to UTF-8 internally)
+auto config = toml::parse_file(L"C:\\config\\settings.toml");
+
+// Wide string values can be used with value()
+auto path = config["path"].value<std::wstring>();
+```
+
+---
+
+## Parser Configuration Macros
+
+| Macro | Default | Effect |
+|-------|---------|--------|
+| `TOML_ENABLE_PARSER` | `1` | Set to `0` to remove the parser entirely (serialize-only builds) |
+| `TOML_EXCEPTIONS` | Auto-detected | Controls exception vs. return-code error handling |
+| `TOML_UNRELEASED_FEATURES` | `0` | Enable parsing of TOML features not yet in a released spec |
+| `TOML_ENABLE_WINDOWS_COMPAT` | `1` on Windows | Enable wstring/wchar_t overloads |
+
+---
+
+## Parsing Specific TOML Features
+
+### Strings
+
+```toml
+basic = "hello\nworld" # basic (escape sequences)
+literal = 'no \escapes' # literal (no escapes)
+multi_basic = """
+multiline
+string""" # multi-line basic
+multi_literal = '''
+multiline
+literal''' # multi-line literal
+```
+
+### Numbers
+
+```toml
+int_dec = 42
+int_hex = 0xFF
+int_oct = 0o755
+int_bin = 0b11010110
+float_std = 3.14
+float_exp = 1e10
+float_inf = inf
+float_nan = nan
+underscore = 1_000_000
+```
+
+The parser records the integer format in `value_flags`:
+- `0xFF` → `value_flags::format_as_hexadecimal`
+- `0o755` → `value_flags::format_as_octal`
+- `0b1010` → `value_flags::format_as_binary`
+
+### Inline Tables
+
+```toml
+point = { x = 1, y = 2 } # single-line inline table
+```
+
+Parsed as a `toml::table` with `is_inline()` returning `true`.
+
+### Arrays of Tables
+
+```toml
+[[products]]
+name = "Hammer"
+price = 9.99
+
+[[products]]
+name = "Nail"
+price = 0.05
+```
+
+Parsed as `table["products"]` → `array` containing two `table` nodes.
+
+---
+
+## Error Recovery
+
+The parser does **not** attempt error recovery. Upon encountering the first error, parsing stops and the error is returned (or thrown). This design ensures:
+
+1. No partially-parsed trees with missing data
+2. Clear, unambiguous error messages
+3. The error source points to the exact location of the problem
+
+---
+
+## Thread Safety
+
+- Parsing is **thread-safe**: multiple threads can call `parse()` concurrently with different inputs
+- The parser uses no global state
+- The returned `parse_result` / `table` is independent and owned by the caller
+
+---
+
+## Complete Example
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+#include <fstream>
+
+int main()
+{
+ // --- Parse from string ---
+ auto config = toml::parse(R"(
+ [database]
+ server = "192.168.1.1"
+ ports = [8001, 8001, 8002]
+ enabled = true
+ connection_max = 5000
+
+ [database.credentials]
+ username = "admin"
+ password_file = "/etc/db/pass"
+ )");
+
+#if TOML_EXCEPTIONS
+ // With exceptions, config is a toml::table directly
+ auto server = config["database"]["server"].value_or(""sv);
+ std::cout << "Server: " << server << "\n";
+
+#else
+ // Without exceptions, check for success first
+ if (!config)
+ {
+ std::cerr << "Parse failed:\n" << config.error() << "\n";
+ return 1;
+ }
+
+ auto& tbl = config.get();
+ auto server = tbl["database"]["server"].value_or(""sv);
+ std::cout << "Server: " << server << "\n";
+#endif
+
+ // --- Parse from file ---
+ try
+ {
+ auto file_config = toml::parse_file("app.toml");
+ std::cout << file_config << "\n";
+ }
+ catch (const toml::parse_error& err)
+ {
+ std::cerr << "Failed to parse app.toml:\n"
+ << err.description() << "\n"
+ << " at " << err.source() << "\n";
+ return 1;
+ }
+
+ // --- Source tracking ---
+ auto doc = toml::parse(R"(
+ title = "Example"
+ [owner]
+ name = "Tom"
+ )", "example.toml");
+
+ auto* name_node = doc.get("owner");
+ if (name_node)
+ {
+ const auto& src = name_node->source();
+ std::cout << "owner defined at: "
+ << *src.path << ":"
+ << src.begin.line << ":"
+ << src.begin.column << "\n";
+ }
+
+ // --- Stream parsing ---
+ std::istringstream ss(R"(key = "from stream")");
+ auto stream_result = toml::parse(ss, "stream-input");
+ std::cout << stream_result["key"].value_or(""sv) << "\n";
+
+ return 0;
+}
+```
+
+---
+
+## Related Documentation
+
+- [basic-usage.md](basic-usage.md) — Quick start guide with parsing examples
+- [values.md](values.md) — Value types created by the parser
+- [tables.md](tables.md) — Root table structure
+- [formatting.md](formatting.md) — Serializing parsed data back to TOML
+- [unicode-handling.md](unicode-handling.md) — UTF-8 handling details
diff --git a/docs/handbook/tomlplusplus/path-system.md b/docs/handbook/tomlplusplus/path-system.md
new file mode 100644
index 0000000000..d9ed1a6262
--- /dev/null
+++ b/docs/handbook/tomlplusplus/path-system.md
@@ -0,0 +1,412 @@
+# toml++ — Path System
+
+## Overview
+
+`toml::path` provides structured navigation into a TOML document tree using dot-separated key names and array indices. Rather than chaining `operator[]` calls, a path can be constructed from a string and applied to a node tree in a single operation.
+
+Declared in `include/toml++/impl/path.hpp` with `at_path()` free functions in `at_path.hpp`.
+
+---
+
+## `path_component`
+
+Each segment of a path is a `path_component`, which is either a **key** (string) or an **array index** (integer):
+
+```cpp
+class path_component
+{
+ public:
+ // Type query
+ path_component_type type() const noexcept;
+ // Returns path_component_type::key or path_component_type::array_index
+
+ // Access (key)
+ const toml::key& key() const noexcept;
+
+ // Access (array index)
+ size_t array_index() const noexcept;
+
+ // Comparison
+ friend bool operator==(const path_component&, const path_component&) noexcept;
+ friend bool operator!=(const path_component&, const path_component&) noexcept;
+};
+```
+
+```cpp
+enum class path_component_type : uint8_t
+{
+ key = 0x1,
+ array_index = 0x2
+};
+```
+
+### Internal Storage
+
+`path_component` uses a union with type discrimination:
+
+```cpp
+// Simplified internal layout:
+union storage_t
+{
+ toml::key k; // for key components
+ size_t index; // for array_index components
+};
+
+path_component_type type_;
+storage_t storage_;
+```
+
+---
+
+## `toml::path`
+
+A path is a sequence of `path_component` values:
+
+```cpp
+class path
+{
+ private:
+ std::vector<path_component> components_;
+};
+```
+
+### Construction
+
+#### From String
+
+```cpp
+path(std::string_view str);
+path(std::wstring_view str); // Windows compat
+```
+
+Path string syntax:
+- Dot `.` separates keys: `"server.host"` → key("server"), key("host")
+- Brackets `[N]` denote array indices: `"servers[0].host"` → key("servers"), index(0), key("host")
+- Quoted keys for special chars: `"a.\"dotted.key\".b"`
+
+```cpp
+toml::path p1("server.host"); // 2 components: key, key
+toml::path p2("servers[0].name"); // 3 components: key, index, key
+toml::path p3("[0][1]"); // 2 components: index, index
+toml::path p4("database.\"dotted.key\""); // 2 components
+```
+
+#### From Components
+
+```cpp
+path(); // empty path
+path(const path& other); // copy
+path(path&& other) noexcept; // move
+```
+
+### Size and Emptiness
+
+```cpp
+size_t size() const noexcept; // number of components
+bool empty() const noexcept; // true if no components
+
+explicit operator bool() const noexcept; // true if non-empty
+
+void clear() noexcept; // remove all components
+```
+
+### Element Access
+
+```cpp
+path_component& operator[](size_t index) noexcept;
+const path_component& operator[](size_t index) const noexcept;
+
+// Iterator support
+auto begin() noexcept;
+auto end() noexcept;
+auto begin() const noexcept;
+auto end() const noexcept;
+auto cbegin() const noexcept;
+auto cend() const noexcept;
+```
+
+```cpp
+toml::path p("server.ports[0]");
+
+for (const auto& component : p)
+{
+ if (component.type() == toml::path_component_type::key)
+ std::cout << "key: " << component.key() << "\n";
+ else
+ std::cout << "index: " << component.array_index() << "\n";
+}
+// key: server
+// key: ports
+// index: 0
+```
+
+---
+
+## Path Operations
+
+### Subpath Extraction
+
+```cpp
+path parent_path() const; // all but last component
+path leaf() const; // last component only
+
+path subpath(size_t start, size_t length) const;
+path subpath(std::vector<path_component>::const_iterator start,
+ std::vector<path_component>::const_iterator end) const;
+
+path truncated(size_t n) const; // first n components
+```
+
+```cpp
+toml::path p("a.b.c.d");
+
+auto parent = p.parent_path(); // "a.b.c"
+auto leaf = p.leaf(); // "d"
+auto sub = p.subpath(1, 2); // "b.c"
+auto trunc = p.truncated(2); // "a.b"
+```
+
+### Concatenation
+
+```cpp
+path operator+(const path& rhs) const;
+path operator+(const path_component& rhs) const;
+path operator+(std::string_view rhs) const;
+
+path& operator+=(const path& rhs);
+path& operator+=(const path_component& rhs);
+path& operator+=(std::string_view rhs);
+
+// Prepend
+path& prepend(const path& source);
+path& prepend(path&& source);
+```
+
+```cpp
+toml::path base("server");
+toml::path full = base + "host";
+// full == "server.host"
+
+toml::path p("a.b");
+p += "c.d";
+// p == "a.b.c.d"
+```
+
+### Assignment
+
+```cpp
+path& assign(std::string_view str);
+path& assign(const path& other);
+path& assign(path&& other) noexcept;
+
+path& operator=(std::string_view str);
+path& operator=(const path& other);
+path& operator=(path&& other) noexcept;
+```
+
+---
+
+## Comparison
+
+```cpp
+friend bool operator==(const path& lhs, const path& rhs) noexcept;
+friend bool operator!=(const path& lhs, const path& rhs) noexcept;
+
+friend bool operator==(const path& lhs, std::string_view rhs);
+friend bool operator!=(const path& lhs, std::string_view rhs);
+```
+
+```cpp
+toml::path a("server.host");
+toml::path b("server.host");
+
+std::cout << (a == b) << "\n"; // true
+std::cout << (a == "server.host") << "\n"; // true
+```
+
+---
+
+## Hashing
+
+```cpp
+size_t hash() const noexcept;
+
+// std::hash specialization
+namespace std {
+ template<> struct hash<toml::path> { ... };
+}
+```
+
+Paths can be used as keys in `std::unordered_map` and `std::unordered_set`.
+
+---
+
+## String Conversion
+
+```cpp
+std::string str() const;
+explicit operator std::string() const;
+
+friend std::ostream& operator<<(std::ostream&, const path&);
+```
+
+```cpp
+toml::path p("servers[0].host");
+std::cout << p << "\n"; // servers[0].host
+std::string s = p.str(); // "servers[0].host"
+```
+
+---
+
+## `at_path()` — Path-Based Node Access
+
+Declared in `include/toml++/impl/at_path.hpp`. These free functions apply a path to a node tree:
+
+```cpp
+node_view<node> at_path(node& root, const toml::path& path) noexcept;
+node_view<const node> at_path(const node& root, const toml::path& path) noexcept;
+
+node_view<node> at_path(node& root, std::string_view path) noexcept;
+node_view<const node> at_path(const node& root, std::string_view path) noexcept;
+
+// Windows compat
+node_view<node> at_path(node& root, std::wstring_view path) noexcept;
+```
+
+Returns a `node_view` — null-safe wrapper that returns empty/default if the path doesn't resolve.
+
+```cpp
+auto tbl = toml::parse(R"(
+ [server]
+ host = "localhost"
+ ports = [8080, 8081, 8082]
+
+ [[servers]]
+ name = "alpha"
+
+ [[servers]]
+ name = "beta"
+)");
+
+// Access nested value
+auto host = toml::at_path(tbl, "server.host").value_or(""sv);
+// "localhost"
+
+// Access array element
+auto port = toml::at_path(tbl, "server.ports[1]").value_or(int64_t{0});
+// 8081
+
+// Access array-of-tables element
+auto name = toml::at_path(tbl, "servers[0].name").value_or(""sv);
+// "alpha"
+
+// Non-existent path returns empty node_view
+auto missing = toml::at_path(tbl, "nonexistent.path");
+std::cout << missing.value_or("default"sv) << "\n"; // "default"
+```
+
+### With `toml::path` Objects
+
+```cpp
+toml::path p("server.ports[0]");
+auto port = toml::at_path(tbl, p).value_or(int64_t{0});
+
+// Reuse path for multiple lookups
+for (size_t i = 0; i < 3; i++)
+{
+ toml::path elem_path = toml::path("server.ports") + toml::path("[" + std::to_string(i) + "]");
+ auto val = toml::at_path(tbl, elem_path).value_or(int64_t{0});
+ std::cout << val << "\n";
+}
+```
+
+---
+
+## `operator[]` with Path
+
+`table` and `node_view` also support path-like access via `operator[]`:
+
+```cpp
+auto tbl = toml::parse(R"(
+ [server]
+ host = "localhost"
+)");
+
+// Chained subscript (each [] does a single lookup)
+auto host = tbl["server"]["host"].value_or(""sv);
+
+// With toml::path (single lookup resolving the full path)
+toml::path p("server.host");
+auto host2 = toml::at_path(tbl, p).value_or(""sv);
+```
+
+Note: `operator[]` on `table` does single-key lookups only. `at_path()` resolves multi-component paths.
+
+---
+
+## Complete Example
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+
+int main()
+{
+ auto config = toml::parse(R"(
+ [database]
+ host = "db.example.com"
+ port = 5432
+
+ [database.pools]
+ read = 10
+ write = 5
+
+ [[database.replicas]]
+ host = "replica1.example.com"
+ port = 5433
+
+ [[database.replicas]]
+ host = "replica2.example.com"
+ port = 5434
+ )");
+
+ // Construct paths
+ toml::path db_host("database.host");
+ toml::path db_port("database.port");
+ toml::path pool_read("database.pools.read");
+
+ // Use at_path for access
+ std::cout << "Host: " << toml::at_path(config, db_host).value_or(""sv) << "\n";
+ std::cout << "Port: " << toml::at_path(config, db_port).value_or(int64_t{0}) << "\n";
+ std::cout << "Read pool: " << toml::at_path(config, pool_read).value_or(int64_t{0}) << "\n";
+
+ // Array-of-tables access
+ for (size_t i = 0; i < 2; i++)
+ {
+ auto host_path = toml::path("database.replicas[" + std::to_string(i) + "].host");
+ auto port_path = toml::path("database.replicas[" + std::to_string(i) + "].port");
+
+ auto host = toml::at_path(config, host_path).value_or(""sv);
+ auto port = toml::at_path(config, port_path).value_or(int64_t{0});
+
+ std::cout << "Replica " << i << ": " << host << ":" << port << "\n";
+ }
+
+ // Path manipulation
+ toml::path base("database");
+ auto full = base + "host";
+ std::cout << "Full path: " << full << "\n"; // database.host
+ std::cout << "Parent: " << full.parent_path() << "\n"; // database
+ std::cout << "Leaf: " << full.leaf() << "\n"; // host
+
+ return 0;
+}
+```
+
+---
+
+## Related Documentation
+
+- [basic-usage.md](basic-usage.md) — Simple access patterns
+- [node-system.md](node-system.md) — node_view returned by at_path
+- [tables.md](tables.md) — Table subscript access
diff --git a/docs/handbook/tomlplusplus/tables.md b/docs/handbook/tomlplusplus/tables.md
new file mode 100644
index 0000000000..d573ec0da2
--- /dev/null
+++ b/docs/handbook/tomlplusplus/tables.md
@@ -0,0 +1,551 @@
+# toml++ — Tables
+
+## Overview
+
+`toml::table` is the primary container in toml++. It extends `toml::node` and models an ordered map from `toml::key` objects to child `toml::node` pointers. Every parsed TOML document has a `table` as its root.
+
+Declared in `include/toml++/impl/table.hpp` with implementation in `table.inl`.
+
+---
+
+## Internal Storage
+
+```cpp
+class table : public node
+{
+ private:
+ using map_type = std::map<toml::key, impl::node_ptr, std::less<>>;
+ map_type map_;
+ bool inline_ = false;
+};
+```
+
+- **`map_type`** = `std::map<toml::key, std::unique_ptr<node>, std::less<>>`
+- **`std::less<>`** enables heterogeneous lookup — you can search by `std::string_view` without constructing a `toml::key`
+- **`inline_`** controls whether the table serializes as `{ a = 1, b = 2 }` (inline) or with `[section]` headers (non-inline)
+- Ownership: the table owns all child nodes via `unique_ptr`
+
+---
+
+## Construction
+
+### Default Construction
+
+```cpp
+toml::table tbl; // empty table
+```
+
+### Initializer List Construction
+
+```cpp
+auto tbl = toml::table{
+ { "name", "toml++" },
+ { "version", 3 },
+ { "features", toml::array{ "parsing", "serialization" } },
+ { "metadata", toml::table{
+ { "author", "Mark Gillard" },
+ { "license", "MIT" }
+ }}
+};
+```
+
+This uses `impl::table_init_pair`:
+```cpp
+struct table_init_pair
+{
+ mutable toml::key key;
+ mutable node_ptr value;
+
+ template <typename K, typename V>
+ table_init_pair(K&& k, V&& v, value_flags flags = preserve_source_value_flags);
+};
+```
+
+Values are converted to nodes via `impl::make_node()`, which handles:
+- Native types (`int`, `double`, `const char*`, etc.) → `value<T>`
+- `toml::array` → `array` (moved)
+- `toml::table` → `table` (moved)
+
+### Copy and Move
+
+```cpp
+toml::table copy(original_table); // deep copy — all child nodes are cloned
+toml::table moved(std::move(tbl)); // move — no allocation, transfers ownership
+```
+
+Copy is deep: every child node in the tree is recursively copied.
+
+---
+
+## Iterators
+
+### Types
+
+```cpp
+using table_iterator = impl::table_iterator<false>;
+using const_table_iterator = impl::table_iterator<true>;
+```
+
+`table_iterator` is a **BidirectionalIterator**. Dereferencing yields `table_proxy_pair`:
+
+```cpp
+template <bool IsConst>
+struct table_proxy_pair
+{
+ using value_type = std::conditional_t<IsConst, const node, node>;
+ const toml::key& first;
+ value_type& second;
+};
+```
+
+The `unique_ptr` layer is hidden — you get `(const key&, node&)` pairs.
+
+### Iterator Methods
+
+```cpp
+iterator begin() noexcept;
+iterator end() noexcept;
+const_iterator begin() const noexcept;
+const_iterator end() const noexcept;
+const_iterator cbegin() const noexcept;
+const_iterator cend() const noexcept;
+```
+
+### Range-Based For
+
+```cpp
+for (auto&& [key, value] : tbl)
+{
+ std::cout << key << " = " << value << "\n";
+}
+```
+
+Structured bindings work because `table_proxy_pair` has public `first` and `second` members.
+
+### Iterator to Key String
+
+```cpp
+for (auto it = tbl.begin(); it != tbl.end(); ++it)
+{
+ const toml::key& k = it->first;
+ toml::node& v = it->second;
+ std::cout << k.str() << ": " << v.type() << "\n";
+}
+```
+
+---
+
+## Capacity
+
+```cpp
+size_t size() const noexcept; // number of key-value pairs
+bool empty() const noexcept; // true if size() == 0
+```
+
+---
+
+## Element Access
+
+### `operator[]` — Returns `node_view`
+
+```cpp
+node_view<node> operator[](std::string_view key) noexcept;
+node_view<const node> operator[](std::string_view key) const noexcept;
+```
+
+Returns a `node_view` that wraps the node at that key, or an empty view if the key doesn't exist. This is the safe, chainable accessor:
+
+```cpp
+auto val = tbl["section"]["subsection"]["key"].value_or(42);
+```
+
+### `at()` — Bounds-Checked Access
+
+```cpp
+node& at(std::string_view key);
+const node& at(std::string_view key) const;
+```
+
+Returns a reference to the node at the key. Throws `std::out_of_range` if the key doesn't exist.
+
+### `get()` — Raw Pointer Access
+
+```cpp
+node* get(std::string_view key) noexcept;
+const node* get(std::string_view key) const noexcept;
+```
+
+Returns a pointer to the node, or `nullptr` if not found:
+
+```cpp
+if (auto* n = tbl.get("name"))
+{
+ std::cout << "Found: " << *n << "\n";
+}
+```
+
+### `get_as<T>()` — Typed Pointer Access
+
+```cpp
+template <typename T>
+impl::wrap_node<T>* get_as(std::string_view key) noexcept;
+
+template <typename T>
+const impl::wrap_node<T>* get_as(std::string_view key) const noexcept;
+```
+
+Combines `get()` and `as<T>()`:
+
+```cpp
+if (auto* val = tbl.get_as<std::string>("name"))
+ std::cout << "Name: " << val->get() << "\n";
+
+if (auto* sub = tbl.get_as<toml::table>("database"))
+ std::cout << "Database has " << sub->size() << " keys\n";
+```
+
+### `contains()` — Key Existence Check
+
+```cpp
+bool contains(std::string_view key) const noexcept;
+```
+
+```cpp
+if (tbl.contains("database"))
+ std::cout << "Has database config\n";
+```
+
+---
+
+## Insertion
+
+### `insert()` — Insert If Not Present
+
+```cpp
+template <typename KeyType, typename ValueType>
+std::pair<iterator, bool> insert(KeyType&& key, ValueType&& val,
+ value_flags flags = preserve_source_value_flags);
+```
+
+Inserts a new key-value pair only if the key doesn't already exist. Returns `(iterator, true)` on success, `(iterator_to_existing, false)` if the key was already present:
+
+```cpp
+auto [it, inserted] = tbl.insert("name", "toml++");
+if (inserted)
+ std::cout << "Inserted: " << it->second << "\n";
+else
+ std::cout << "Key already exists\n";
+```
+
+### `insert_or_assign()` — Insert or Replace
+
+```cpp
+template <typename KeyType, typename ValueType>
+std::pair<iterator, bool> insert_or_assign(KeyType&& key, ValueType&& val,
+ value_flags flags = preserve_source_value_flags);
+```
+
+Always succeeds — inserts if new, replaces if existing:
+
+```cpp
+tbl.insert_or_assign("version", 4); // replaces any existing "version"
+```
+
+### `emplace<T>()` — Construct In Place
+
+```cpp
+template <typename ValueType, typename KeyType, typename... Args>
+std::pair<iterator, bool> emplace(KeyType&& key, Args&&... args);
+```
+
+Constructs a new node in place if the key doesn't exist:
+
+```cpp
+tbl.emplace<std::string>("greeting", "Hello, World!");
+tbl.emplace<toml::array>("empty_list");
+tbl.emplace<toml::table>("empty_section");
+```
+
+---
+
+## Removal
+
+### `erase()` — By Key
+
+```cpp
+size_t erase(std::string_view key) noexcept;
+```
+
+Returns 1 if the key was found and removed, 0 otherwise:
+
+```cpp
+tbl.erase("deprecated_key");
+```
+
+### `erase()` — By Iterator
+
+```cpp
+iterator erase(iterator pos) noexcept;
+iterator erase(const_iterator pos) noexcept;
+iterator erase(const_iterator first, const_iterator last) noexcept;
+```
+
+```cpp
+auto it = tbl.find("old_key");
+if (it != tbl.end())
+ tbl.erase(it);
+```
+
+### `clear()`
+
+```cpp
+void clear() noexcept;
+```
+
+Removes all key-value pairs.
+
+---
+
+## Search
+
+### `find()`
+
+```cpp
+iterator find(std::string_view key) noexcept;
+const_iterator find(std::string_view key) const noexcept;
+```
+
+Returns an iterator to the key-value pair, or `end()` if not found.
+
+### `lower_bound()` / `upper_bound()` / `equal_range()`
+
+These operate on the underlying `std::map` with heterogeneous lookup:
+
+```cpp
+iterator lower_bound(std::string_view key) noexcept;
+iterator upper_bound(std::string_view key) noexcept;
+std::pair<iterator, iterator> equal_range(std::string_view key) noexcept;
+// + const overloads
+```
+
+---
+
+## Metadata
+
+### `is_inline()`
+
+```cpp
+bool is_inline() const noexcept;
+void is_inline(bool val) noexcept;
+```
+
+Controls inline serialization. When `true`, the table formats as `{ a = 1, b = 2 }` instead of using `[section]` headers:
+
+```cpp
+auto tbl = toml::table{
+ { "a", 1 },
+ { "b", 2 },
+ { "nested", toml::table{ { "c", 3 } } }
+};
+
+std::cout << tbl << "\n";
+// Output:
+// a = 1
+// b = 2
+//
+// [nested]
+// c = 3
+
+tbl.is_inline(true);
+std::cout << tbl << "\n";
+// Output:
+// { a = 1, b = 2, nested = { c = 3 } }
+```
+
+Runtime-constructed tables default to non-inline. The parser sets `is_inline(true)` for tables parsed from inline syntax.
+
+---
+
+## `for_each()` — Type-Safe Iteration
+
+```cpp
+template <typename Func>
+table& for_each(Func&& visitor) &;
+```
+
+Visits each key-value pair, passing the value as its concrete type:
+
+```cpp
+tbl.for_each([](const toml::key& key, auto& value)
+{
+ std::cout << key << ": ";
+
+ using value_type = std::remove_cvref_t<decltype(value)>;
+ if constexpr (std::is_same_v<value_type, toml::table>)
+ std::cout << "table (" << value.size() << " entries)\n";
+ else if constexpr (std::is_same_v<value_type, toml::array>)
+ std::cout << "array (" << value.size() << " elements)\n";
+ else
+ std::cout << value.get() << "\n";
+});
+```
+
+The visitor is instantiated for all 9 possible value types (table, array, + 7 value types).
+
+---
+
+## Path-Based Access
+
+### `at_path()` Member
+
+```cpp
+node_view<node> at_path(std::string_view path) noexcept;
+node_view<const node> at_path(std::string_view path) const noexcept;
+node_view<node> at_path(const toml::path& path) noexcept;
+```
+
+Resolves dot-separated paths with array indices:
+
+```cpp
+auto tbl = toml::parse(R"(
+ [database]
+ servers = [
+ { host = "alpha", port = 5432 },
+ { host = "beta", port = 5433 }
+ ]
+)");
+
+std::cout << tbl.at_path("database.servers[0].host") << "\n"; // "alpha"
+std::cout << tbl.at_path("database.servers[1].port") << "\n"; // 5433
+```
+
+### `operator[]` with `toml::path`
+
+```cpp
+node_view<node> operator[](const toml::path& path) noexcept;
+```
+
+```cpp
+toml::path p("database.servers[0].host");
+std::cout << tbl[p] << "\n"; // "alpha"
+```
+
+---
+
+## Comparison
+
+### Equality
+
+```cpp
+friend bool operator==(const table& lhs, const table& rhs) noexcept;
+friend bool operator!=(const table& lhs, const table& rhs) noexcept;
+```
+
+Deep structural equality: two tables are equal if they have the same keys with equal values. Source regions and inline-ness are not compared.
+
+---
+
+## Printing
+
+Tables are streamable via the default `toml_formatter`:
+
+```cpp
+std::cout << tbl << "\n";
+```
+
+Equivalent to:
+```cpp
+std::cout << toml::toml_formatter{ tbl } << "\n";
+```
+
+---
+
+## Type Identity
+
+`table` overrides all type-check virtuals from `node`:
+
+```cpp
+node_type type() const noexcept final; // returns node_type::table
+bool is_table() const noexcept final; // returns true
+bool is_array() const noexcept final; // returns false
+bool is_value() const noexcept final; // returns false
+bool is_string() const noexcept final; // returns false
+// ... all other is_*() return false
+
+table* as_table() noexcept final; // returns this
+const table* as_table() const noexcept final; // returns this
+// ... all other as_*() return nullptr
+```
+
+---
+
+## Windows Compatibility
+
+When `TOML_ENABLE_WINDOWS_COMPAT` is enabled, additional overloads accept `std::wstring_view` for key parameters:
+
+```cpp
+node* get(std::wstring_view key);
+bool contains(std::wstring_view key) const;
+node_view<node> operator[](std::wstring_view key) noexcept;
+// etc.
+```
+
+Wide strings are internally narrowed via `impl::narrow()`.
+
+---
+
+## Complete Example
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+
+int main()
+{
+ // Build a table programmatically
+ auto config = toml::table{
+ { "app", toml::table{
+ { "name", "MyApp" },
+ { "version", 2 }
+ }},
+ { "features", toml::array{ "auth", "logging" } }
+ };
+
+ // Navigate
+ std::cout << "App: " << config["app"]["name"].value_or("?"sv) << "\n";
+
+ // Insert
+ config["app"].as_table()->insert("debug", false);
+
+ // Modify
+ config.insert_or_assign("features",
+ toml::array{ "auth", "logging", "metrics" });
+
+ // Check
+ if (config.contains("app"))
+ {
+ auto* app = config.get_as<toml::table>("app");
+ std::cout << "App table has " << app->size() << " keys\n";
+ }
+
+ // Iterate
+ for (auto&& [key, value] : config)
+ {
+ std::cout << key << " (" << value.type() << ")\n";
+ }
+
+ // Serialize
+ std::cout << "\n" << config << "\n";
+
+ return 0;
+}
+```
+
+---
+
+## Related Documentation
+
+- [node-system.md](node-system.md) — Base node interface
+- [arrays.md](arrays.md) — Array container details
+- [values.md](values.md) — Value node details
+- [path-system.md](path-system.md) — Path-based navigation
diff --git a/docs/handbook/tomlplusplus/testing.md b/docs/handbook/tomlplusplus/testing.md
new file mode 100644
index 0000000000..d8469413a2
--- /dev/null
+++ b/docs/handbook/tomlplusplus/testing.md
@@ -0,0 +1,226 @@
+# toml++ — Testing
+
+## Overview
+
+toml++ uses the **Catch2** testing framework. Tests are organized in `tests/` and built via Meson. The test suite includes unit tests for every major feature, TOML specification conformance tests, and third-party test suites.
+
+---
+
+## Test Framework
+
+### Catch2
+
+- Used as the test runner and assertion library
+- Can be vendored (`extern/Catch2/`) or found as a system dependency
+- Tests use `TEST_CASE`, `SECTION`, `REQUIRE`, `CHECK` macros
+
+### Build Configuration
+
+Tests are built with Meson. The test build options from `meson_options.txt`:
+
+```
+option('build_tests', type: 'boolean', value: false)
+option('use_vendored_libs', type: 'boolean', value: true)
+```
+
+Build and run tests:
+
+```bash
+meson setup build -Dbuild_tests=true
+meson compile -C build
+meson test -C build
+```
+
+---
+
+## Test File Organization
+
+From `tests/meson.build`, the test suite consists of:
+
+### Conformance Tests
+
+Third-party test suites that validate the parser against the TOML specification:
+
+| Test Suite | Files | Description |
+|-----------|-------|-------------|
+| BurntSushi (valid) | `conformance_burntsushi_valid.cpp` | Validates that valid TOML parses correctly |
+| BurntSushi (invalid) | `conformance_burntsushi_invalid.cpp` | Validates that invalid TOML is rejected |
+| iarna (valid) | `conformance_iarna_valid.cpp` | Additional valid TOML test cases |
+| iarna (invalid) | `conformance_iarna_invalid.cpp` | Additional invalid TOML test cases |
+
+Test data files are in `tests/` subdirectories corresponding to each third-party suite.
+
+### Parsing Tests
+
+Unit tests for the parser:
+
+| File | Content |
+|------|---------|
+| `parsing_arrays.cpp` | Array parsing edge cases |
+| `parsing_booleans.cpp` | Boolean value parsing |
+| `parsing_comments.cpp` | Comment handling |
+| `parsing_dates_and_times.cpp` | Date/time value parsing |
+| `parsing_floats.cpp` | Float parsing (inf, nan, precision) |
+| `parsing_integers.cpp` | Integer parsing (hex, oct, bin, overflow) |
+| `parsing_key_value_pairs.cpp` | Key-value pair syntax |
+| `parsing_spec_example.cpp` | TOML spec example document |
+| `parsing_strings.cpp` | All 4 string types, escape sequences |
+| `parsing_tables.cpp` | Standard and inline tables |
+
+### Manipulation Tests
+
+Tests for programmatic construction and modification:
+
+| File | Content |
+|------|---------|
+| `manipulating_arrays.cpp` | Array push_back, erase, flatten, etc. |
+| `manipulating_tables.cpp` | Table insert, emplace, merge, etc. |
+| `manipulating_values.cpp` | Value construction, assignment, flags |
+| `manipulating_parse_result.cpp` | parse_result access patterns |
+
+### Formatter Tests
+
+| File | Content |
+|------|---------|
+| `formatters.cpp` | TOML, JSON, and YAML formatter output |
+
+### Path Tests
+
+| File | Content |
+|------|---------|
+| `path.cpp` | Path parsing, navigation, at_path() |
+
+### Other Tests
+
+| File | Content |
+|------|---------|
+| `at_path.cpp` | at_path() function specifically |
+| `for_each.cpp` | for_each() visitor pattern |
+| `user_feedback.cpp` | Tests from user-reported issues |
+| `windows_compat.cpp` | Windows wstring compatibility |
+| `using_iterators.cpp` | Iterator usage patterns |
+| `main.cpp` | Catch2 main entry point |
+| `tests.hpp` | Shared test utilities and macros |
+
+---
+
+## Running Tests
+
+### Full Test Suite
+
+```bash
+cd tomlplusplus
+meson setup build -Dbuild_tests=true
+meson compile -C build
+meson test -C build
+```
+
+### Verbose Output
+
+```bash
+meson test -C build -v
+```
+
+### Running Specific Tests
+
+Catch2 allows filtering by test name:
+
+```bash
+./build/tests/toml_test "parsing integers"
+./build/tests/toml_test "[arrays]"
+```
+
+### Exception / No-Exception Modes
+
+Tests are compiled in both modes when possible:
+
+```bash
+# With exceptions (default)
+meson setup build_exc -Dbuild_tests=true
+
+# Without exceptions
+meson setup build_noexc -Dbuild_tests=true -Dcpp_eh=none
+```
+
+---
+
+## Test Patterns
+
+### Parsing Roundtrip
+
+A common pattern: parse TOML, verify values, re-serialize, verify output:
+
+```cpp
+TEST_CASE("integers - hex")
+{
+ auto tbl = toml::parse("val = 0xFF");
+ CHECK(tbl["val"].value<int64_t>() == 255);
+ CHECK(tbl["val"].as_integer()->flags() == toml::value_flags::format_as_hexadecimal);
+}
+```
+
+### Invalid Input Rejection
+
+```cpp
+TEST_CASE("invalid - unterminated string")
+{
+ CHECK_THROWS_AS(toml::parse("val = \"unterminated"), toml::parse_error);
+}
+```
+
+Or without exceptions:
+
+```cpp
+TEST_CASE("invalid - unterminated string")
+{
+ auto result = toml::parse("val = \"unterminated");
+ CHECK(!result);
+ CHECK(result.error().description().find("unterminated") != std::string_view::npos);
+}
+```
+
+### Manipulation Verification
+
+```cpp
+TEST_CASE("array - push_back")
+{
+ toml::array arr;
+ arr.push_back(1);
+ arr.push_back("two");
+ arr.push_back(3.0);
+
+ REQUIRE(arr.size() == 3);
+ CHECK(arr[0].value<int64_t>() == 1);
+ CHECK(arr[1].value<std::string_view>() == "two");
+ CHECK(arr[2].value<double>() == 3.0);
+}
+```
+
+---
+
+## Adding New Tests
+
+1. Create a `.cpp` file in `tests/`
+2. Include `"tests.hpp"` for common utilities
+3. Add the file to the test source list in `tests/meson.build`
+4. Write `TEST_CASE` blocks using Catch2 macros
+5. Rebuild and run
+
+```cpp
+// tests/my_feature.cpp
+#include "tests.hpp"
+
+TEST_CASE("my feature - basic behavior")
+{
+ auto tbl = toml::parse(R"(key = "value")");
+ REQUIRE(tbl["key"].value<std::string_view>() == "value");
+}
+```
+
+---
+
+## Related Documentation
+
+- [building.md](building.md) — Build system setup
+- [code-style.md](code-style.md) — Code conventions
+- [parsing.md](parsing.md) — Parser being tested
diff --git a/docs/handbook/tomlplusplus/unicode-handling.md b/docs/handbook/tomlplusplus/unicode-handling.md
new file mode 100644
index 0000000000..6cafb3deff
--- /dev/null
+++ b/docs/handbook/tomlplusplus/unicode-handling.md
@@ -0,0 +1,335 @@
+# toml++ — Unicode Handling
+
+## Overview
+
+toml++ fully handles UTF-8 encoded input and output as required by the TOML specification. All TOML documents must be valid UTF-8, and the library validates, decodes, and encodes Unicode throughout parsing and formatting.
+
+Core Unicode utilities are in `include/toml++/impl/unicode.hpp` with auto-generated lookup tables in `unicode_autogenerated.hpp`.
+
+---
+
+## UTF-8 Input Requirements
+
+The parser expects all input to be valid UTF-8:
+
+- **BOM handling**: A leading UTF-8 BOM (`0xEF 0xBB 0xBF`) is silently stripped before parsing begins
+- **Validation**: Invalid byte sequences (overlong encodings, surrogate code points, truncated sequences) produce parse errors
+- **Multi-byte characters**: Fully supported in string values, comments, and bare keys (where permitted by TOML)
+
+```cpp
+// UTF-8 content works naturally
+auto tbl = toml::parse(R"(
+ greeting = "Hello, 世界!"
+ emoji = "🎉"
+ name = "Ñoño"
+)");
+```
+
+---
+
+## Character Classification
+
+The library classifies Unicode code points for parsing with functions in `unicode.hpp`:
+
+### `is_string_delimiter()`
+
+Identifies characters that can start/end strings: `"` (U+0022) and `'` (U+0027).
+
+### `is_ascii_letter()`
+
+`[A-Za-z]` — used in bare key validation and other ASCII-specific checks.
+
+### `is_ascii_whitespace()`
+
+Space (U+0020) and tab (U+0009).
+
+### `is_ascii_line_break()`
+
+LF (U+000A) and CR (U+000D).
+
+### `is_bare_key_character()`
+
+Characters permitted in TOML bare keys: `[A-Za-z0-9_-]` plus Unicode letters/digits when `TOML_LANG_UNRELEASED_FEATURES` is enabled.
+
+### `is_control_character()`
+
+Control characters (U+0000–U+001F, U+007F) excluding tab. These are forbidden in basic strings and must be escaped.
+
+### `is_non_ascii_letter()`
+
+Unicode letter code points outside ASCII — from auto-generated tables in `unicode_autogenerated.hpp`. Used for extended bare key support in unreleased TOML features.
+
+### `is_non_ascii_number()`
+
+Unicode digit code points outside ASCII (e.g., Arabic-Indic digits).
+
+### `is_non_ascii_whitespace()`
+
+Unicode whitespace beyond ASCII space/tab.
+
+---
+
+## Escape Sequences in Strings
+
+TOML basic strings (`"..."` and `"""..."""`) support escape sequences. The parser decodes these into their UTF-8 representations:
+
+| Escape | Meaning | Code Point |
+|--------|---------|------------|
+| `\b` | Backspace | U+0008 |
+| `\t` | Tab | U+0009 |
+| `\n` | Line Feed | U+000A |
+| `\f` | Form Feed | U+000C |
+| `\r` | Carriage Return | U+000D |
+| `\"` | Quote | U+0022 |
+| `\\` | Backslash | U+005C |
+| `\uXXXX` | Unicode (4 hex digits) | U+0000–U+FFFF |
+| `\UXXXXXXXX` | Unicode (8 hex digits) | U+00000000–U+0010FFFF |
+
+### `control_char_escapes` Table
+
+The formatter uses a lookup table for serializing control characters back to escape sequences:
+
+```cpp
+// In impl namespace:
+inline constexpr const char* control_char_escapes[] = {
+ "\\u0000", "\\u0001", "\\u0002", "\\u0003",
+ "\\u0004", "\\u0005", "\\u0006", "\\u0007",
+ "\\b", "\\t", "\\n", "\\u000B",
+ "\\f", "\\r", "\\u000E", "\\u000F",
+ "\\u0010", "\\u0011", "\\u0012", "\\u0013",
+ "\\u0014", "\\u0015", "\\u0016", "\\u0017",
+ "\\u0018", "\\u0019", "\\u001A", "\\u001B",
+ "\\u001C", "\\u001D", "\\u001E", "\\u001F",
+};
+```
+
+---
+
+## Unicode Escape Decoding
+
+The parser processes `\uXXXX` and `\UXXXXXXXX` escapes:
+
+1. Reads 4 or 8 hexadecimal digits
+2. Validates the code point:
+ - Must not be a surrogate (U+D800–U+DFFF)
+ - Must not exceed U+10FFFF
+ - Must not be a non-character (U+FDD0–U+FDEF, U+xFFFE–U+xFFFF)
+3. Encodes to UTF-8 bytes (1–4 bytes depending on code point range)
+
+```toml
+# Valid Unicode escapes
+escape_a = "\u0041" # "A"
+escape_heart = "\u2764" # "❤"
+escape_emoji = "\U0001F600" # "😀"
+```
+
+```cpp
+auto tbl = toml::parse(R"(
+ a = "\u0041"
+ heart = "\u2764"
+)");
+
+std::cout << tbl["a"].value_or(""sv) << "\n"; // A
+std::cout << tbl["heart"].value_or(""sv) << "\n"; // ❤
+```
+
+---
+
+## UTF-8 Encoding in Output
+
+When formatting, the behavior depends on `format_flags::allow_unicode_strings`:
+
+### With `allow_unicode_strings` (default for TOML and YAML formatters)
+
+Non-ASCII characters pass through unescaped:
+
+```cpp
+auto tbl = toml::table{ { "name", "日本語" } };
+std::cout << tbl << "\n";
+// name = "日本語"
+```
+
+### Without `allow_unicode_strings`
+
+Non-ASCII characters are escaped to `\uXXXX` / `\UXXXXXXXX`:
+
+```cpp
+auto tbl = toml::table{ { "name", "日本語" } };
+auto fmt = toml::toml_formatter{
+ tbl,
+ toml::format_flags::indentation // no allow_unicode_strings
+};
+std::cout << fmt << "\n";
+// name = "\u65E5\u672C\u8A9E"
+```
+
+---
+
+## char8_t Support (C++20)
+
+When compiling with C++20, `char8_t` overloads are available for parsing:
+
+```cpp
+auto tbl = toml::parse(u8R"(
+ greeting = "Hello, 世界"
+)"sv);
+```
+
+The `char8_t` strings are internally treated as UTF-8 byte sequences. `std::u8string_view` is accepted by `parse()`.
+
+### `source_path` as u8string
+
+```cpp
+auto tbl = toml::parse(doc, u8"config.toml"sv);
+```
+
+---
+
+## Windows Compatibility (`TOML_ENABLE_WINDOWS_COMPAT`)
+
+When enabled (default on Windows), additional conversion overloads exist:
+
+- `parse_file(std::wstring_view)` — converts wide file path to UTF-8
+- `value<std::wstring>()` — converts stored UTF-8 string to wide string
+- String comparison with `wchar_t*` / `std::wstring_view`
+
+The conversions use Windows API (`MultiByteToWideChar` / `WideCharToMultiByte`) internally.
+
+---
+
+## Bare Key Unicode Rules
+
+Per TOML v1.0.0, bare keys are limited to ASCII letters, digits, hyphen, and underscore:
+
+```toml
+valid-key = "value"
+valid_key_2 = "value"
+# 日本語 = "value" # NOT valid as bare key in TOML v1.0
+```
+
+Non-ASCII keys must be quoted:
+
+```toml
+"日本語" = "value" # valid as quoted key
+```
+
+### Unreleased Features
+
+With `TOML_UNRELEASED_FEATURES=1`, the parser accepts Unicode letters and digits in bare keys as proposed for future TOML versions:
+
+```toml
+# Only with TOML_UNRELEASED_FEATURES=1:
+日本語 = "value" # bare key with Unicode letters
+```
+
+The `is_non_ascii_letter()` and `is_non_ascii_number()` functions from `unicode_autogenerated.hpp` provide the code point tables for this classification.
+
+---
+
+## Auto-Generated Unicode Tables
+
+`include/toml++/impl/unicode_autogenerated.hpp` contains machine-generated lookup tables derived from the Unicode Character Database. These tables classify code points by category:
+
+- **Letter** categories: Lu, Ll, Lt, Lm, Lo
+- **Number** categories: Nd, Nl
+- **Combining marks**: Mn, Mc
+- **Connector punctuation**: Pc
+
+The tables use range-based compression for efficiency:
+
+```cpp
+// Simplified representation:
+struct code_point_range
+{
+ char32_t first;
+ char32_t last;
+};
+
+// Function uses binary search over sorted ranges
+bool is_non_ascii_letter(char32_t cp) noexcept;
+```
+
+---
+
+## String Handling in Formatters
+
+Each formatter handles strings slightly differently:
+
+### TOML Formatter
+
+- Defaults to basic strings with escaping: `"hello\nworld"`
+- Uses literal strings when `allow_literal_strings` is set and string has no single quotes: `'no escapes needed'`
+- Uses multi-line strings when `allow_multi_line_strings` is set and string contains newlines
+- Preserves Unicode with `allow_unicode_strings` (default on)
+
+### JSON Formatter
+
+- Always uses double-quoted strings
+- Escapes all required JSON characters
+- Does not use literal or multi-line strings
+- Unicode behavior follows `allow_unicode_strings` flag
+
+### YAML Formatter
+
+- Uses double-quoted strings
+- `allow_unicode_strings` is on by default
+- Escapes control characters
+
+---
+
+## Complete Example
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+
+int main()
+{
+ // Parse document with Unicode content
+ auto config = toml::parse(R"(
+ title = "日本語テスト"
+ greeting = "Hello, 世界! 🌍"
+ escaped = "\u0048\u0065\u006C\u006C\u006F"
+ path = "C:\\Users\\名前\\config"
+
+ [metadata]
+ "quoted.key" = "value"
+ author = "José García"
+ )");
+
+ // Read values — Unicode is preserved
+ auto title = config["title"].value_or(""sv);
+ std::cout << "Title: " << title << "\n";
+ // Title: 日本語テスト
+
+ auto greeting = config["greeting"].value_or(""sv);
+ std::cout << "Greeting: " << greeting << "\n";
+ // Greeting: Hello, 世界! 🌍
+
+ // Escaped values are decoded
+ auto escaped = config["escaped"].value_or(""sv);
+ std::cout << "Escaped: " << escaped << "\n";
+ // Escaped: Hello
+
+ // Serialize back — Unicode preserved by default
+ std::cout << "\n=== TOML (Unicode) ===\n";
+ std::cout << config << "\n";
+
+ // Serialize with Unicode escaping
+ std::cout << "\n=== TOML (Escaped) ===\n";
+ std::cout << toml::toml_formatter{
+ config,
+ toml::format_flags::indentation // no allow_unicode_strings
+ } << "\n";
+
+ return 0;
+}
+```
+
+---
+
+## Related Documentation
+
+- [parsing.md](parsing.md) — Parser UTF-8 input handling
+- [formatting.md](formatting.md) — Unicode output control via format_flags
+- [values.md](values.md) — String value type
diff --git a/docs/handbook/tomlplusplus/values.md b/docs/handbook/tomlplusplus/values.md
new file mode 100644
index 0000000000..453140df89
--- /dev/null
+++ b/docs/handbook/tomlplusplus/values.md
@@ -0,0 +1,547 @@
+# toml++ — Values
+
+## Overview
+
+`toml::value<T>` represents leaf TOML values — the concrete data in a TOML document. Each `value` wraps one of seven native C++ types corresponding to the TOML data types.
+
+Declared in `include/toml++/impl/value.hpp` with supporting types in `forward_declarations.hpp` and `date_time.hpp`.
+
+---
+
+## Native Types
+
+The seven supported value types map to TOML types via `toml::value_type_of<T>`:
+
+| TOML Type | C++ Storage Type | `node_type` Enum | Alias |
+|-------------------|---------------------|---------------------------|--------------------|
+| String | `std::string` | `node_type::string` | `value<std::string>` |
+| Integer | `int64_t` | `node_type::integer` | `value<int64_t>` |
+| Float | `double` | `node_type::floating_point` | `value<double>` |
+| Boolean | `bool` | `node_type::boolean` | `value<bool>` |
+| Date | `toml::date` | `node_type::date` | `value<date>` |
+| Time | `toml::time` | `node_type::time` | `value<time>` |
+| Date-Time | `toml::date_time` | `node_type::date_time` | `value<date_time>` |
+
+Only these seven instantiations of `value<T>` exist. The template is constrained:
+
+```cpp
+template <typename T>
+class value : public node
+{
+ static_assert(
+ impl::is_native<T>,
+ "Template parameter must be one of the TOML native value types"
+ );
+
+ private:
+ value_type val_;
+ value_flags flags_ = value_flags::none;
+};
+```
+
+Where `value_type` is the `impl::native_type_of<T>` alias.
+
+---
+
+## Type Traits
+
+```cpp
+// Check at compile time
+toml::is_string<value<std::string>> // true
+toml::is_integer<value<int64_t>> // true
+toml::is_floating_point<value<double>> // true
+toml::is_boolean<value<bool>> // true
+toml::is_date<value<date>> // true
+toml::is_time<value<time>> // true
+toml::is_date_time<value<date_time>> // true
+
+// Works on the raw types too
+toml::is_integer<int64_t> // true
+toml::is_number<int64_t> // true (integer or float)
+toml::is_number<double> // true
+
+// Supertype checks
+toml::is_value<value<int64_t>> // true (any value<T>)
+toml::is_chronological<value<date>> // true (date, time, or date_time)
+```
+
+---
+
+## Construction
+
+### From Compatible Types
+
+```cpp
+toml::value<int64_t> i{ 42 };
+toml::value<double> f{ 3.14 };
+toml::value<std::string> s{ "hello" };
+toml::value<bool> b{ true };
+
+// Implicit promotion from smaller integer types
+toml::value<int64_t> from_int{ 42 }; // int → int64_t
+toml::value<int64_t> from_short{ short(5) }; // short → int64_t
+
+// Implicit promotion from float → double
+toml::value<double> from_float{ 1.5f }; // float → double
+```
+
+The `native_value_maker` mechanism handles promotions:
+
+```cpp
+// In impl namespace:
+template <typename T>
+struct native_value_maker;
+
+// For integer types (int, unsigned, short, etc.):
+// Promotes to int64_t
+
+// For floating-point (float):
+// Promotes to double
+
+// For string types (const char*, string_view, etc.):
+// Converts to std::string
+
+// For char8_t strings (u8"..."):
+// Transcodes to std::string
+```
+
+### Copy and Move
+
+```cpp
+toml::value<std::string> original{ "hello" };
+toml::value<std::string> copy{ original }; // deep copy
+toml::value<std::string> moved{ std::move(original) }; // move
+```
+
+### Assignment
+
+```cpp
+auto v = toml::value<int64_t>{ 10 };
+v = 42; // assign from raw value (operator=(ValueType))
+v = copy; // assign from another value (operator=(const value&))
+v = std::move(other); // move assign
+```
+
+---
+
+## Retrieving Values
+
+### `get()` — Direct Access
+
+```cpp
+ValueType& get() & noexcept;
+ValueType&& get() && noexcept;
+const ValueType& get() const& noexcept;
+```
+
+Returns a direct reference to the stored value:
+
+```cpp
+auto v = toml::value<std::string>{ "hello" };
+std::string& s = v.get();
+s += " world";
+std::cout << v.get() << "\n"; // "hello world"
+```
+
+### `operator ValueType&()` — Implicit Conversion
+
+```cpp
+explicit operator ValueType&() noexcept;
+explicit operator const ValueType&() const noexcept;
+```
+
+```cpp
+auto v = toml::value<int64_t>{ 42 };
+int64_t x = static_cast<int64_t>(v);
+```
+
+### `operator*()` / `operator->()`
+
+```cpp
+ValueType& operator*() & noexcept;
+const ValueType& operator*() const& noexcept;
+ValueType* operator->() noexcept;
+const ValueType* operator->() const noexcept;
+```
+
+Dereference-style access:
+
+```cpp
+auto v = toml::value<std::string>{ "hello" };
+std::cout << v->length() << "\n"; // 5
+std::cout << (*v).size() << "\n"; // 5
+```
+
+---
+
+## Value Flags
+
+`value_flags` is a bitmask controlling how values are formatted when serialized:
+
+```cpp
+enum class value_flags : uint16_t
+{
+ none = 0,
+ format_as_binary = 1, // 0b10101
+ format_as_octal = 2, // 0o755
+ format_as_hexadecimal = 4, // 0xFF
+
+ // Special sentinel (default behavior):
+ // preserve_source_value_flags
+};
+```
+
+### Getting / Setting Flags
+
+```cpp
+value_flags flags() const noexcept;
+value<T>& flags(value_flags new_flags) noexcept;
+```
+
+```cpp
+auto v = toml::value<int64_t>{ 255 };
+v.flags(toml::value_flags::format_as_hexadecimal);
+
+std::cout << toml::toml_formatter{ toml::table{ { "val", v } } };
+// Output: val = 0xFF
+```
+
+### Source Format Preservation
+
+When parsing, the library records the source format in the flags. When printing, if `preserve_source_value_flags` is used (the default), the original format is retained:
+
+```toml
+port = 0xFF
+mask = 0o777
+bits = 0b1010
+```
+
+After parsing and re-serializing, these retain their hex/octal/binary format.
+
+---
+
+## Date and Time Types
+
+Defined in `include/toml++/impl/date_time.hpp`.
+
+### `toml::date`
+
+```cpp
+struct date
+{
+ uint16_t year;
+ uint8_t month; // 1-12
+ uint8_t day; // 1-31
+
+ // Comparison operators
+ friend bool operator==(const date&, const date&) noexcept;
+ friend bool operator!=(const date&, const date&) noexcept;
+ friend bool operator< (const date&, const date&) noexcept;
+ friend bool operator<=(const date&, const date&) noexcept;
+ friend bool operator> (const date&, const date&) noexcept;
+ friend bool operator>=(const date&, const date&) noexcept;
+
+ // Streaming
+ friend std::ostream& operator<<(std::ostream&, const date&);
+};
+```
+
+```cpp
+auto d = toml::date{ 2024, 1, 15 };
+auto v = toml::value<toml::date>{ d };
+std::cout << v << "\n"; // 2024-01-15
+```
+
+### `toml::time`
+
+```cpp
+struct time
+{
+ uint8_t hour; // 0-23
+ uint8_t minute; // 0-59
+ uint8_t second; // 0-59 (0-60 for leap second)
+ uint32_t nanosecond; // 0-999999999
+
+ // Comparison and streaming operators
+};
+```
+
+```cpp
+auto t = toml::time{ 14, 30, 0 };
+auto v = toml::value<toml::time>{ t };
+std::cout << v << "\n"; // 14:30:00
+```
+
+### `toml::time_offset`
+
+```cpp
+struct time_offset
+{
+ int16_t minutes; // UTC offset: -720 to +840
+
+ // Convenience for UTC:
+ static constexpr time_offset utc() noexcept { return { 0 }; }
+
+ // Comparison operators
+};
+```
+
+### `toml::date_time`
+
+```cpp
+struct date_time
+{
+ toml::date date;
+ toml::time time;
+ optional<time_offset> offset; // nullopt = local date-time
+
+ // Constructor overloads:
+ constexpr date_time(const toml::date& d, const toml::time& t) noexcept;
+ constexpr date_time(const toml::date& d, const toml::time& t,
+ const toml::time_offset& off) noexcept;
+
+ bool is_local() const noexcept; // true if no offset
+
+ // Comparison and streaming operators
+};
+```
+
+#### TOML Date-Time Variants
+
+```toml
+# Offset date-time (has time zone)
+odt = 2024-01-15T14:30:00+05:30
+odt_utc = 2024-01-15T09:00:00Z
+
+# Local date-time (no time zone)
+ldt = 2024-01-15T14:30:00
+
+# Local date
+ld = 2024-01-15
+
+# Local time
+lt = 14:30:00
+```
+
+```cpp
+auto tbl = toml::parse(R"(
+ odt = 2024-01-15T14:30:00+05:30
+ ldt = 2024-01-15T14:30:00
+ ld = 2024-01-15
+ lt = 14:30:00
+)");
+
+auto odt = tbl["odt"].value<toml::date_time>();
+// odt->offset has value, odt->is_local() == false
+
+auto ldt = tbl["ldt"].value<toml::date_time>();
+// ldt->offset is nullopt, ldt->is_local() == true
+
+auto ld = tbl["ld"].value<toml::date>();
+// ld->year == 2024, month == 1, day == 15
+
+auto lt = tbl["lt"].value<toml::time>();
+// lt->hour == 14, minute == 30, second == 0
+```
+
+---
+
+## Type Identity
+
+```cpp
+// For value<int64_t>:
+node_type type() const noexcept final; // node_type::integer
+bool is_integer() const noexcept final; // true
+bool is_number() const noexcept final; // true
+bool is_value() const noexcept final; // true
+// All other is_*() return false
+
+value<int64_t>* as_integer() noexcept final; // returns this
+// All other as_*() return nullptr
+```
+
+---
+
+## Retrieving Values from Nodes
+
+From the base `node` or `node_view`, there are multiple retrieval patterns:
+
+### `value<T>()` — Get with Type Coercion
+
+```cpp
+// As node method:
+optional<T> value() const noexcept;
+```
+
+Attempts to return the value as `T`, with permitted coercions:
+- `int64_t` → `int`, `unsigned`, `size_t`, etc. (bounds-checked)
+- `double` → `float` (precision loss allowed)
+- `double` ↔ `int64_t` (within representable range)
+
+```cpp
+auto tbl = toml::parse("x = 42");
+
+auto as_int = tbl["x"].value<int64_t>(); // optional(42)
+auto as_double = tbl["x"].value<double>(); // optional(42.0)
+auto as_int32 = tbl["x"].value<int>(); // optional(42)
+auto as_string = tbl["x"].value<std::string>(); // nullopt (type mismatch)
+```
+
+### `value_exact<T>()` — No Coercion
+
+```cpp
+optional<T> value_exact() const noexcept;
+```
+
+Only succeeds if the stored type exactly matches `T` (no int→double or similar coercions):
+
+```cpp
+auto tbl = toml::parse("x = 42");
+
+auto exact_int = tbl["x"].value_exact<int64_t>(); // optional(42)
+auto exact_dbl = tbl["x"].value_exact<double>(); // nullopt (it's an int)
+```
+
+### `value_or()` — With Default
+
+```cpp
+template <typename T>
+auto value_or(T&& default_value) const noexcept;
+```
+
+Returns the value or a default:
+
+```cpp
+auto tbl = toml::parse("name = \"Alice\"");
+
+auto name = tbl["name"].value_or("unknown"sv); // "Alice"
+auto age = tbl["age"].value_or(int64_t{ 0 }); // 0 (key missing)
+```
+
+---
+
+## Comparison
+
+```cpp
+// Between values of same type
+friend bool operator==(const value& lhs, const value& rhs) noexcept;
+
+// Between value and raw type
+friend bool operator==(const value<T>& lhs, const T& rhs) noexcept;
+```
+
+```cpp
+auto a = toml::value<int64_t>{ 42 };
+auto b = toml::value<int64_t>{ 42 };
+
+std::cout << (a == b) << "\n"; // true
+std::cout << (a == 42) << "\n"; // true
+std::cout << (a != 99) << "\n"; // true
+```
+
+For `value<std::string>`, comparison also works with `std::string_view` and `const char*`:
+
+```cpp
+auto s = toml::value<std::string>{ "hello" };
+std::cout << (s == "hello") << "\n"; // true
+std::cout << (s == "world"sv) << "\n"; // false
+```
+
+---
+
+## `make_value<T>`
+
+Utility function in `make_node.hpp` for constructing values:
+
+```cpp
+template <typename T, typename... Args>
+auto make_value(Args&&... args)
+ -> decltype(std::make_unique<impl::wrap_node<T>>(std::forward<Args>(args)...));
+```
+
+Returns `std::unique_ptr<value<T>>`:
+
+```cpp
+auto v = toml::make_value<int64_t>(42);
+// v is std::unique_ptr<toml::value<int64_t>>
+```
+
+---
+
+## Printing
+
+Values stream via the default formatter:
+
+```cpp
+auto v = toml::value<std::string>{ "hello world" };
+std::cout << v << "\n"; // "hello world"
+
+auto d = toml::value<toml::date>{ toml::date{ 2024, 6, 15 } };
+std::cout << d << "\n"; // 2024-06-15
+
+auto i = toml::value<int64_t>{ 255 };
+i.flags(toml::value_flags::format_as_hexadecimal);
+std::cout << i << "\n"; // 0xFF
+```
+
+---
+
+## Complete Example
+
+```cpp
+#include <toml++/toml.hpp>
+#include <iostream>
+
+int main()
+{
+ auto config = toml::parse(R"(
+ title = "My App"
+ version = 3
+ debug = true
+ pi = 3.14159
+ created = 2024-01-15T10:30:00Z
+ expires = 2025-12-31
+ check_time = 08:00:00
+ )");
+
+ // Type-safe retrieval with defaults
+ auto title = config["title"].value_or("Untitled"sv);
+ auto version = config["version"].value_or(int64_t{ 1 });
+ auto debug = config["debug"].value_or(false);
+ auto pi = config["pi"].value_or(0.0);
+
+ std::cout << "Title: " << title << "\n";
+ std::cout << "Version: " << version << "\n";
+ std::cout << "Debug: " << debug << "\n";
+ std::cout << "Pi: " << pi << "\n";
+
+ // Date-time access
+ if (auto dt = config["created"].value<toml::date_time>())
+ {
+ std::cout << "Created: " << dt->date.year
+ << "-" << (int)dt->date.month
+ << "-" << (int)dt->date.day << "\n";
+
+ if (!dt->is_local())
+ std::cout << " Offset: " << dt->offset->minutes << " min\n";
+ }
+
+ // Modify and re-serialize
+ auto* v = config["version"].as_integer();
+ if (v) *v = 4;
+
+ std::cout << "\n" << config << "\n";
+
+ return 0;
+}
+```
+
+---
+
+## Related Documentation
+
+- [node-system.md](node-system.md) — Base node class and type dispatch
+- [arrays.md](arrays.md) — Array container
+- [tables.md](tables.md) — Table container
+- [parsing.md](parsing.md) — Parsing values from TOML text
+- [formatting.md](formatting.md) — Formatting values for output