+ ✓
+ Easy Updates: Change hints in one place, updates everywhere
+
+
+ ✓
+ Type Safety: TypeScript ensures correct usage
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/ui/FormFieldWrapper.README.md b/src/components/ui/FormFieldWrapper.README.md
new file mode 100644
index 00000000..ed90cb52
--- /dev/null
+++ b/src/components/ui/FormFieldWrapper.README.md
@@ -0,0 +1,264 @@
+# FormFieldWrapper Component
+
+A unified form field component that automatically provides hints, validation messages, and terminology tooltips based on field type.
+
+## Features
+
+- ✅ **Automatic hints** based on field type (speed, height, URL, email, etc.)
+- ✅ **Built-in validation** display with error messages
+- ✅ **Terminology tooltips** on labels (hover to see definitions)
+- ✅ **Character counting** for textareas
+- ✅ **50% less boilerplate** compared to manual field creation
+- ✅ **Type-safe** with TypeScript
+- ✅ **Consistent styling** across all forms
+
+## Quick Start
+
+### Before (Manual)
+```tsx
+
+
+
+
+ Official website URL (must start with https:// or http://)
+
+ {errors.website_url && (
+
{errors.website_url.message}
+ )}
+
+```
+
+### After (With FormFieldWrapper)
+```tsx
+
+```
+
+## Basic Usage
+
+```tsx
+import { FormFieldWrapper } from '@/components/ui/form-field-wrapper';
+import { useForm } from 'react-hook-form';
+
+function MyForm() {
+ const { register, formState: { errors } } = useForm();
+
+ return (
+
+ );
+}
+```
+
+## With Terminology Tooltips
+
+```tsx
+
+```
+
+## Using Presets
+
+```tsx
+import { FormFieldWrapper, formFieldPresets } from '@/components/ui/form-field-wrapper';
+
+
+```
+
+## Available Field Types
+
+- `url` - Website URLs with protocol hint
+- `email` - Email addresses with format hint
+- `phone` - Phone numbers with flexible format hint
+- `slug` - URL slugs with character restrictions
+- `height-requirement` - Height in cm with metric hint
+- `age-requirement` - Age requirements
+- `capacity` - Capacity per hour
+- `duration` - Duration in seconds
+- `speed` - Max speed (km/h)
+- `height` - Max height (meters)
+- `length` - Track length (meters)
+- `inversions` - Number of inversions
+- `g-force` - G-force values
+- `source-url` - Reference URL for verification
+- `submission-notes` - Notes for moderators (textarea with char count)
+
+## Available Presets
+
+```tsx
+formFieldPresets.websiteUrl({})
+formFieldPresets.email({})
+formFieldPresets.phone({})
+formFieldPresets.sourceUrl({})
+formFieldPresets.submissionNotes({})
+formFieldPresets.heightRequirement({})
+formFieldPresets.capacity({})
+formFieldPresets.duration({})
+formFieldPresets.speed({})
+formFieldPresets.height({})
+formFieldPresets.length({})
+formFieldPresets.inversions({})
+formFieldPresets.gForce({})
+```
+
+## Custom Hints
+
+Override automatic hints with custom text:
+
+```tsx
+
+```
+
+## Hide Hints
+
+```tsx
+
+```
+
+## Migration Guide
+
+To migrate existing fields:
+
+1. **Identify the field structure** to replace
+2. **Choose appropriate `fieldType`** from the list above
+3. **Add `termKey`** if field relates to terminology
+4. **Replace** the entire div block with `FormFieldWrapper`
+
+Example migration:
+
+```tsx
+// BEFORE
+
+
+
+
+ Speed must be in km/h, between 0-500. Example: "193" for 193 km/h (120 mph)
+
+ {errors.max_speed_kmh && (
+
{errors.max_speed_kmh.message}
+ )}
+
+
+// AFTER
+
+```
+
+## Demo
+
+View a live interactive demo at `/examples/form-field-wrapper` (in development mode) by visiting the `FormFieldWrapperDemo` component.
+
+## Props Reference
+
+| Prop | Type | Description |
+|------|------|-------------|
+| `id` | `string` | Field identifier (required) |
+| `label` | `string` | Field label text (required) |
+| `fieldType` | `FormFieldType` | Type for automatic hints |
+| `termKey` | `string` | Terminology key for tooltip |
+| `showTermIcon` | `boolean` | Show tooltip icon (default: true) |
+| `required` | `boolean` | Show required asterisk |
+| `optional` | `boolean` | Show optional badge |
+| `hint` | `string` | Custom hint (overrides automatic) |
+| `error` | `string` | Error message from validation |
+| `value` | `string \| number` | Current value for char counting |
+| `maxLength` | `number` | Max length for char counting |
+| `inputProps` | `InputProps` | Props to pass to Input |
+| `textareaProps` | `TextareaProps` | Props to pass to Textarea |
+| `className` | `string` | Additional wrapper classes |
+| `hideHint` | `boolean` | Hide automatic hint |
+
+## Benefits
+
+1. **Consistency** - All fields follow the same structure
+2. **Less Code** - ~50% reduction in boilerplate
+3. **Smart Defaults** - Automatic hints based on field type
+4. **Built-in Terminology** - Hover tooltips for technical terms
+5. **Easy Updates** - Change hints in one place, updates everywhere
+6. **Type Safety** - TypeScript ensures correct usage
diff --git a/src/components/ui/form-field-wrapper.tsx b/src/components/ui/form-field-wrapper.tsx
new file mode 100644
index 00000000..25459c8e
--- /dev/null
+++ b/src/components/ui/form-field-wrapper.tsx
@@ -0,0 +1,356 @@
+import * as React from "react";
+import { Input } from "@/components/ui/input";
+import { Textarea } from "@/components/ui/textarea";
+import { Label } from "@/components/ui/label";
+import { TermTooltip } from "@/components/ui/term-tooltip";
+import { fieldHints } from "@/lib/enhancedValidation";
+import { cn } from "@/lib/utils";
+
+/**
+ * Field types that automatically get hints and terminology support
+ */
+export type FormFieldType =
+ | 'text'
+ | 'number'
+ | 'url'
+ | 'email'
+ | 'phone'
+ | 'textarea'
+ | 'slug'
+ | 'height-requirement'
+ | 'age-requirement'
+ | 'capacity'
+ | 'duration'
+ | 'speed'
+ | 'height'
+ | 'length'
+ | 'inversions'
+ | 'g-force'
+ | 'source-url'
+ | 'submission-notes';
+
+interface FormFieldWrapperProps {
+ /** Field identifier */
+ id: string;
+
+ /** Field label text */
+ label: string;
+
+ /** Field type - determines automatic hints and validation */
+ fieldType?: FormFieldType;
+
+ /** Terminology key for tooltip (e.g., 'lsm', 'rmc') */
+ termKey?: string;
+
+ /** Show tooltip icon on label */
+ showTermIcon?: boolean;
+
+ /** Whether field is required */
+ required?: boolean;
+
+ /** Whether field is optional (shows badge) */
+ optional?: boolean;
+
+ /** Custom hint text (overrides automatic hint) */
+ hint?: string;
+
+ /** Error message from validation (pass errors.field?.message) */
+ error?: string;
+
+ /** Current value for character counting */
+ value?: string | number;
+
+ /** Maximum length for character counting */
+ maxLength?: number;
+
+ /** Input props to pass through */
+ inputProps?: React.ComponentProps;
+
+ /** Textarea props to pass through (when fieldType is 'textarea') */
+ textareaProps?: React.ComponentProps;
+
+ /** Additional className for wrapper */
+ className?: string;
+
+ /** Hide automatic hint */
+ hideHint?: boolean;
+}
+
+/**
+ * Get automatic hint based on field type
+ */
+function getAutoHint(fieldType?: FormFieldType): string | undefined {
+ if (!fieldType) return undefined;
+
+ const hintMap: Record = {
+ 'text': undefined,
+ 'number': undefined,
+ 'url': fieldHints.websiteUrl,
+ 'email': fieldHints.email,
+ 'phone': fieldHints.phone,
+ 'textarea': undefined,
+ 'slug': fieldHints.slug,
+ 'height-requirement': fieldHints.heightRequirement,
+ 'age-requirement': fieldHints.ageRequirement,
+ 'capacity': fieldHints.capacity,
+ 'duration': fieldHints.duration,
+ 'speed': fieldHints.speed,
+ 'height': fieldHints.height,
+ 'length': fieldHints.length,
+ 'inversions': fieldHints.inversions,
+ 'g-force': fieldHints.gForce,
+ 'source-url': fieldHints.sourceUrl,
+ 'submission-notes': fieldHints.submissionNotes,
+ };
+
+ return hintMap[fieldType];
+}
+
+/**
+ * Get input type from field type
+ */
+function getInputType(fieldType?: FormFieldType): string {
+ if (!fieldType) return 'text';
+
+ const typeMap: Record = {
+ 'text': 'text',
+ 'number': 'number',
+ 'url': 'url',
+ 'email': 'email',
+ 'phone': 'tel',
+ 'textarea': 'text',
+ 'slug': 'text',
+ 'height-requirement': 'number',
+ 'age-requirement': 'number',
+ 'capacity': 'number',
+ 'duration': 'number',
+ 'speed': 'number',
+ 'height': 'number',
+ 'length': 'number',
+ 'inversions': 'number',
+ 'g-force': 'number',
+ 'source-url': 'url',
+ 'submission-notes': 'text',
+ };
+
+ return typeMap[fieldType] || 'text';
+}
+
+/**
+ * Unified form field wrapper with automatic hints, validation, and terminology
+ *
+ * @example
+ * ```tsx
+ *
+ * ```
+ *
+ * @example With terminology tooltip
+ * ```tsx
+ *
+ * ```
+ *
+ * @example Textarea with character count
+ * ```tsx
+ *
+ * ```
+ */
+export function FormFieldWrapper({
+ id,
+ label,
+ fieldType,
+ termKey,
+ showTermIcon = true,
+ required = false,
+ optional = false,
+ hint,
+ error,
+ value,
+ maxLength,
+ inputProps,
+ textareaProps,
+ className,
+ hideHint = false,
+}: FormFieldWrapperProps) {
+ const isTextarea = fieldType === 'textarea' || fieldType === 'submission-notes';
+ const autoHint = getAutoHint(fieldType);
+ const displayHint = hint || autoHint;
+ const inputType = getInputType(fieldType);
+
+ // Character count for textareas with maxLength
+ const showCharCount = isTextarea && maxLength && typeof value === 'string';
+ const charCount = typeof value === 'string' ? value.length : 0;
+
+ return (
+
+ {/* Label with optional terminology tooltip */}
+
+
+ {/* Input or Textarea */}
+ {isTextarea ? (
+
+ ) : (
+
+ )}
+
+ {/* Hint text (if not hidden and exists) */}
+ {!hideHint && displayHint && !error && (
+