diff --git a/src/components/advanced/AdvancedTools.tsx b/src/components/advanced/AdvancedTools.tsx index 78f7fe1..673003c 100644 --- a/src/components/advanced/AdvancedTools.tsx +++ b/src/components/advanced/AdvancedTools.tsx @@ -1,87 +1,51 @@ /** * Advanced Analysis tools container. + * Single-column, all-expanded layout — every tool visible at full width. */ -import { useState } from 'react'; import { TreatmentDetection } from './TreatmentDetection'; import { OriginCharacteristics } from './OriginCharacteristics'; import { ProportionAnalyzer } from './ProportionAnalyzer'; -import { CalculatorCard } from '../calculator/CalculatorCard'; +import { ToolSection } from '../ui/ToolSection'; -type AdvancedId = 'treatment' | 'origin' | 'proportion'; - -interface AdvancedConfig { - id: AdvancedId; - label: string; - iconPath: string; - description: string; - component: React.ReactNode; -} - -const ICON_PATHS: Record = { +const ICON_PATHS = { treatment: 'M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z', globe: 'M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z', ruler: 'M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01', }; -const ADVANCED_TOOLS: AdvancedConfig[] = [ - { - id: 'treatment', - label: 'Treatment Detection', - iconPath: ICON_PATHS.treatment, - description: 'Visual & instrumental indicators for gem treatments', - component: , - }, - { - id: 'origin', - label: 'Origin Characteristics', - iconPath: ICON_PATHS.globe, - description: 'Geographic origin determination features', - component: , - }, - { - id: 'proportion', - label: 'Proportion Analyzer', - iconPath: ICON_PATHS.ruler, - description: 'Cut quality analysis from proportion measurements', - component: , - }, -]; - -export function AdvancedTools({ defaultExpanded = 'treatment' }: { defaultExpanded?: AdvancedId | null }) { - const [expandedTool, setExpandedTool] = useState(defaultExpanded); - - const handleToggle = (id: AdvancedId) => { - setExpandedTool(prev => (prev === id ? null : id)); - }; - +export function AdvancedTools() { return (
-
-

- Advanced gemmological analysis tools including treatment detection, origin determination characteristics, and cut proportion evaluation. -

-
- -
- {ADVANCED_TOOLS.map(tool => ( - handleToggle(tool.id)} - > - {tool.component} - - ))} -
- -

- Tip: Click on any tool header to expand or collapse it. -

+ + + + + + + + + + +
); } diff --git a/src/components/advanced/TreatmentDetection.tsx b/src/components/advanced/TreatmentDetection.tsx index ec79792..29dbc5f 100644 --- a/src/components/advanced/TreatmentDetection.tsx +++ b/src/components/advanced/TreatmentDetection.tsx @@ -161,15 +161,16 @@ export function TreatmentDetection() { }); return ( -
+
+ {/* Treatment types overview — responsive grid */}
-

Common Treatment Types

-
+

Common Treatment Types

+
{TREATMENT_TYPES.map(type => (
-
-
{type.name}
- +
{type.name}
+
-

{type.description}

-

- Purpose: {type.purpose} -

+

{type.purpose}

))}
-
-

Detection Indicators

+ {/* Detection indicators — filter bar + results table */} +
+

Detection Indicators

-
- setSearchTerm(e.target.value)} - className="flex-1 px-3 py-2 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-crystal-500" - /> - +
+
+ setSearchTerm(e.target.value)} + className="w-full px-3 py-1.5 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-cyan-400 focus:border-cyan-400" + /> +
+
+ +
+ {filteredTreatments.length} treatment{filteredTreatments.length !== 1 ? 's' : ''}
-
- {filteredTreatments.map((treatment, idx) => ( -
-
-
-
{treatment.treatment}
-

{treatment.gems}

-
- - {treatment.permanence.startsWith('Permanent') ? 'Permanent' : 'Temporary'} - -
- -
-
- Visual Indicators: -

{treatment.visualIndicators}

-
-
- Instrumental Tests: -

{treatment.instrumentalTests}

-
-
- Disclosure: -

{treatment.disclosure}

-
-
-
- ))} +
+ + + + + + + + + + + {filteredTreatments.map((treatment, idx) => ( + + + + + + + ))} + +
TreatmentVisualInstrumentalStatus
+

{treatment.treatment}

+

{treatment.gems}

+
{treatment.visualIndicators}{treatment.instrumentalTests} + + {treatment.permanence.startsWith('Permanent') ? 'Permanent' : 'Temporary'} + +
+ {filteredTreatments.length === 0 && ( +

No treatments found matching your criteria.

+ )}
- - {filteredTreatments.length === 0 && ( -

No treatments found matching your criteria.

- )}
-
-

⚠️ Important Disclosure Requirements

-
    -
  • • All treatments must be disclosed to buyers in most markets
  • -
  • • Undisclosed treatments can result in legal liability
  • -
  • • Some treatments (fracture filling, dyeing) drastically reduce value
  • -
  • • Standard treatments (heat in corundum) may not require disclosure in some markets
  • -
  • • When in doubt, always disclose - transparency builds trust
  • -
+ {/* Disclosure requirements — inline warning */} +
+ Disclosure: + All treatments must be disclosed to buyers + • + Fracture filling & dyeing drastically reduce value + • + When in doubt, always disclose
); diff --git a/src/components/calculator/ConversionTools.tsx b/src/components/calculator/ConversionTools.tsx index 5158961..14a9521 100644 --- a/src/components/calculator/ConversionTools.tsx +++ b/src/components/calculator/ConversionTools.tsx @@ -1,101 +1,63 @@ /** * Unit Conversion tools container. - * Shows all conversion tools in expandable cards. + * Single-column, all-expanded layout — every tool visible at full width. */ -import { useState } from 'react'; import { WeightConverter } from './WeightConverter'; import { LengthConverter } from './LengthConverter'; import { TemperatureConverter } from './TemperatureConverter'; import { PricePerCaratConverter } from './PricePerCaratConverter'; -import { CalculatorCard } from './CalculatorCard'; +import { ToolSection } from '../ui/ToolSection'; -type ConversionId = 'weight' | 'length' | 'temperature' | 'price'; - -interface ConversionConfig { - id: ConversionId; - label: string; - iconPath: string; - description: string; - component: React.ReactNode; -} - -const ICON_PATHS: Record = { +const ICON_PATHS = { weight: 'M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z', ruler: 'M6 20h12M6 4h12m-6 0v16', thermometer: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z', currency: 'M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z', }; -const CONVERSIONS: ConversionConfig[] = [ - { - id: 'weight', - label: 'Weight Converter', - iconPath: ICON_PATHS.weight, - description: 'Convert ct/g/mg', - component: , - }, - { - id: 'length', - label: 'Length Converter', - iconPath: ICON_PATHS.ruler, - description: 'Convert mm/inches', - component: , - }, - { - id: 'temperature', - label: 'Temperature Converter', - iconPath: ICON_PATHS.thermometer, - description: 'Convert °C/°F', - component: , - }, - { - id: 'price', - label: 'Price Per Carat', - iconPath: ICON_PATHS.currency, - description: 'Calculate total price and markup', - component: , - }, -]; - -interface ConversionToolsProps { - defaultExpanded?: ConversionId | null; -} - -export function ConversionTools({ defaultExpanded = 'weight' }: ConversionToolsProps) { - const [expandedCalc, setExpandedCalc] = useState(defaultExpanded); - - const handleToggle = (id: ConversionId) => { - setExpandedCalc(prev => (prev === id ? null : id)); - }; - +export function ConversionTools() { return (
-
-

- Unit conversion tools for weight, length, temperature, and pricing calculations. -

-
- -
- {CONVERSIONS.map(calc => ( - handleToggle(calc.id)} - > - {calc.component} - - ))} -
- -

- Tip: Click on any converter header to expand or collapse it. -

+ + + + + + + + + + + + + + +
); } diff --git a/src/components/calculator/MeasurementTools.tsx b/src/components/calculator/MeasurementTools.tsx index 841874e..9e4ad3f 100644 --- a/src/components/calculator/MeasurementTools.tsx +++ b/src/components/calculator/MeasurementTools.tsx @@ -1,9 +1,8 @@ /** * Measurement & Calculation tools container. - * Shows all measurement tools in expandable cards. + * Single-column, all-expanded layout — every tool visible at full width. */ -import { useState } from 'react'; import { SGCalculator } from './SGCalculator'; import { RICalculator } from './RICalculator'; import { BirefringenceCalc } from './BirefringenceCalc'; @@ -11,26 +10,9 @@ import { CriticalAngleCalc } from './CriticalAngleCalc'; import { CaratEstimator } from './CaratEstimator'; import { DispersionCalculator } from './DispersionCalculator'; import { DensityEstimator } from './DensityEstimator'; -import { CalculatorCard } from './CalculatorCard'; +import { ToolSection } from '../ui/ToolSection'; -type MeasurementId = - | 'sg' - | 'ri-lookup' - | 'birefringence' - | 'critical-angle' - | 'carat-estimate' - | 'dispersion' - | 'density'; - -interface MeasurementConfig { - id: MeasurementId; - label: string; - iconPath: string; - description: string; - component: React.ReactNode; -} - -const ICON_PATHS: Record = { +const ICON_PATHS = { scale: 'M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3', search: 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z', layers: 'M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z', @@ -40,99 +22,78 @@ const ICON_PATHS: Record = { beaker: 'M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z', }; -const MEASUREMENTS: MeasurementConfig[] = [ - { - id: 'sg', - label: 'Specific Gravity', - iconPath: ICON_PATHS.scale, - description: 'Calculate SG from hydrostatic weighing', - component: , - }, - { - id: 'ri-lookup', - label: 'RI Lookup', - iconPath: ICON_PATHS.search, - description: 'Find gems by refractive index', - component: , - }, - { - id: 'birefringence', - label: 'Birefringence', - iconPath: ICON_PATHS.layers, - description: 'Calculate birefringence from RI values', - component: , - }, - { - id: 'critical-angle', - label: 'Critical Angle', - iconPath: ICON_PATHS['arrow-right'], - description: 'Calculate TIR critical angle', - component: , - }, - { - id: 'dispersion', - label: 'Dispersion', - iconPath: ICON_PATHS.sparkles, - description: 'Calculate fire from RI at different wavelengths', - component: , - }, - { - id: 'carat-estimate', - label: 'Carat Estimate', - iconPath: ICON_PATHS.cube, - description: 'Estimate weight from dimensions', - component: , - }, - { - id: 'density', - label: 'Density Estimator', - iconPath: ICON_PATHS.beaker, - description: 'Alternative SG for irregular shapes', - component: , - }, -]; +export function MeasurementTools() { + return ( +
+ + + -interface MeasurementToolsProps { - defaultExpanded?: MeasurementId | null; -} + + + -export function MeasurementTools({ defaultExpanded = 'sg' }: MeasurementToolsProps) { - const [expandedCalc, setExpandedCalc] = useState(defaultExpanded); + + + - const handleToggle = (id: MeasurementId) => { - setExpandedCalc(prev => (prev === id ? null : id)); - }; + + + - return ( -
- {/* Introduction */} -
-

- Select a tool below to expand it. These measurement and calculation tools help with gem identification, grading, and analysis. -

-
+ + + - {/* Tools grid */} -
- {MEASUREMENTS.map(calc => ( - handleToggle(calc.id)} - > - {calc.component} - - ))} -
+ + + - {/* Tip */} -

- Tip: Click on any tool header to expand or collapse it. -

+ + +
); } diff --git a/src/components/identification/FractureCleavageGuide.tsx b/src/components/identification/FractureCleavageGuide.tsx index 6a048f9..f2558a4 100644 --- a/src/components/identification/FractureCleavageGuide.tsx +++ b/src/components/identification/FractureCleavageGuide.tsx @@ -77,65 +77,67 @@ export function FractureCleavageGuide() { }); return ( -
+
+ {/* Fracture types — responsive grid */}
-

Fracture Types

-
+

Fracture Types

+
{FRACTURE_TYPES.map(fracture => (
-
{fracture.type}
-

{fracture.description}

-

- Appearance: {fracture.appearance} -

-

- Examples: {fracture.examples} -

+
{fracture.type}
+

{fracture.description}

+

{fracture.examples}

))}
-
-

Cleavage Reference

+ {/* Cleavage reference — filter bar + full-width table */} +
+

Cleavage Reference

-
- setSearchTerm(e.target.value)} - className="flex-1 px-3 py-2 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-crystal-500" - /> - +
+
+ setSearchTerm(e.target.value)} + className="w-full px-3 py-1.5 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-400 focus:border-amber-400" + /> +
+
+ +
+ {filteredGems.length} gem{filteredGems.length !== 1 ? 's' : ''}
-
- +
+
- - - - - - + + + + + + - - {filteredGems.map(gem => ( - - - + {filteredGems.map((gem, i) => ( + + + - - - + + + ))}
GemstoneCleavageDirectionsFractureNotes
GemstoneCleavageDirectionsFractureNotes
{gem.gem} +
{gem.gem} {gem.directions}{gem.fracture}{gem.notes}{gem.directions}{gem.fracture}{gem.notes}
-
- - {filteredGems.length === 0 && ( -

No gemstones found matching your criteria.

- )} -
- -
-
-

Understanding Cleavage

-
    -
  • • Perfect: Splits easily along smooth planes
  • -
  • • Distinct: Visible planes, moderate ease of splitting
  • -
  • • Indistinct: Difficult to observe/achieve
  • -
  • • None: No cleavage planes present
  • -
-
- -
-

Cleavage vs Fracture

-
    -
  • • Cleavage: Breaks along crystallographic planes
  • -
  • • Fracture: Random breaking not related to structure
  • -
  • • Cleavage surfaces are smooth and planar
  • -
  • • Fracture surfaces are irregular and curved
  • -
+ {filteredGems.length === 0 && ( +

No gemstones found matching your criteria.

+ )}
-
-

Handling Gems with Perfect Cleavage

-
    -
  • • Avoid ultrasonic cleaners (vibrations can cause cleavage)
  • -
  • • Use bezel or protective settings instead of prong settings
  • -
  • • Avoid steam cleaning for gems with cleavage
  • -
  • • Handle with extreme care during setting and sizing
  • -
  • • Not recommended for rings (high-impact jewelry)
  • -
+ {/* Tips — compact inline strips */} +
+ • Perfect cleavage splits easily along smooth planes + • Cleavage = along crystal planes; Fracture = random breaking + • Avoid ultrasonic cleaning for gems with perfect cleavage + • Use bezel or protective settings for fragile gems
); diff --git a/src/components/identification/GemComparison.tsx b/src/components/identification/GemComparison.tsx index 3f9fe0d..fc545cf 100644 --- a/src/components/identification/GemComparison.tsx +++ b/src/components/identification/GemComparison.tsx @@ -1,6 +1,7 @@ /** * Gem Comparison Tool * Side-by-side property comparison (2-4 gems) + * Full-width layout: gem selector pills on top, comparison table stretches full width below. */ import { useState } from 'react'; @@ -34,6 +35,17 @@ const GEM_DATABASE: GemData[] = [ { name: 'Alexandrite', ri_low: 1.745, ri_high: 1.755, sg: 3.71, hardness: 8.5, dispersion: 0.015, system: 'Orthorhombic', pleochroism: 'Very Strong' }, ]; +const PROPERTIES = [ + { key: 'ri_low' as const, label: 'RI (Low)', format: (g: GemData) => g.ri_low.toFixed(3) }, + { key: 'ri_high' as const, label: 'RI (High)', format: (g: GemData) => g.ri_high.toFixed(3) }, + { key: 'birefringence' as const, label: 'Birefringence', format: (g: GemData) => (g.ri_high - g.ri_low).toFixed(3) }, + { key: 'sg' as const, label: 'Specific Gravity', format: (g: GemData) => g.sg.toFixed(2) }, + { key: 'hardness' as const, label: 'Hardness (Mohs)', format: (g: GemData) => String(g.hardness) }, + { key: 'dispersion' as const, label: 'Dispersion', format: (g: GemData) => g.dispersion.toFixed(3) }, + { key: 'system' as const, label: 'Crystal System', format: (g: GemData) => g.system }, + { key: 'pleochroism' as const, label: 'Pleochroism', format: (g: GemData) => g.pleochroism }, +]; + export function GemComparison() { const [selected, setSelected] = useState(['Diamond', 'Ruby']); @@ -52,135 +64,73 @@ export function GemComparison() { const selectedGems = selected.map(name => GEM_DATABASE.find(g => g.name === name)!); return ( -
-
-

- Select 2-4 gems to compare their optical and physical properties side-by-side. -

-
- -
-
-

- Selected Gems ({selected.length}/4) -

- {selected.length > 1 && ( - - )} -
-
+
+ {/* Gem selector + counter — horizontal bar */} +
+
{GEM_DATABASE.map(gem => ( ))}
+
+ {selected.length}/4 + {selected.length > 1 && ( + + )} +
- {/* Comparison Table */} -
- + {/* Comparison table — stretches full width */} +
+
- - + + {selectedGems.map(gem => ( - ))} - - - - {selectedGems.map(gem => ( - + {PROPERTIES.map((prop, i) => ( + + - ))} - - - - {selectedGems.map(gem => ( - - ))} - - - - {selectedGems.map(gem => ( - - ))} - - - - {selectedGems.map(gem => ( - - ))} - - - - {selectedGems.map(gem => ( - - ))} - - - - {selectedGems.map(gem => ( - - ))} - - - - {selectedGems.map(gem => ( - - ))} - - - - {selectedGems.map(gem => ( - - ))} - + {selectedGems.map(gem => ( + + ))} + + ))}
Property
Property + {gem.name}
RI (Low) - {gem.ri_low.toFixed(3)} +
+ {prop.label}
RI (High) - {gem.ri_high.toFixed(3)} -
Birefringence - {(gem.ri_high - gem.ri_low).toFixed(3)} -
Specific Gravity - {gem.sg.toFixed(2)} -
Hardness (Mohs) - {gem.hardness} -
Dispersion - {gem.dispersion.toFixed(3)} -
Crystal System - {gem.system} -
Pleochroism - {gem.pleochroism} -
+ {prop.format(gem)} +
-
-

Comparison Tips

-
    -
  • • Compare RI to separate gems with similar appearance
  • -
  • • SG and hardness help distinguish gems of similar RI
  • -
  • • Birefringence and pleochroism indicate crystal system
  • -
  • • Dispersion affects brilliance and fire in well-cut stones
  • -
+ {/* Tips — compact inline */} +
+ • Compare RI to separate similar-looking gems + • SG + hardness distinguish gems with similar RI + • Dispersion indicates fire in well-cut stones
); diff --git a/src/components/identification/HardnessReference.tsx b/src/components/identification/HardnessReference.tsx index 159da96..5a7de17 100644 --- a/src/components/identification/HardnessReference.tsx +++ b/src/components/identification/HardnessReference.tsx @@ -1,6 +1,6 @@ /** * Mohs Hardness Scale Reference - * Includes wear resistance notes and gemstone hardness lookup + * Full-width layout: Mohs scale on left, searchable gem lookup on right (desktop). */ import { useState } from 'react'; @@ -8,7 +8,6 @@ import { useState } from 'react'; interface MohsLevel { hardness: number; mineral: string; - description: string; wearResistance: string; } @@ -20,40 +19,45 @@ interface GemHardness { } const MOHS_SCALE: MohsLevel[] = [ - { hardness: 10, mineral: 'Diamond', description: 'Hardest natural material', wearResistance: 'Excellent for all jewelry' }, - { hardness: 9, mineral: 'Corundum (Ruby/Sapphire)', description: 'Extremely durable', wearResistance: 'Excellent for all jewelry' }, - { hardness: 8, mineral: 'Topaz/Spinel', description: 'Very durable', wearResistance: 'Excellent for daily wear' }, - { hardness: 7.5, mineral: 'Beryl (Emerald/Aquamarine)', description: 'Durable with care', wearResistance: 'Good for most jewelry' }, - { hardness: 7, mineral: 'Quartz', description: 'Resistant to scratching', wearResistance: 'Good for most jewelry' }, - { hardness: 6.5, mineral: 'Tanzanite/Peridot', description: 'Moderate durability', wearResistance: 'Fair - avoid rings' }, - { hardness: 6, mineral: 'Feldspar (Moonstone)', description: 'Easily scratched', wearResistance: 'Fair - pendants/earrings' }, - { hardness: 5, mineral: 'Apatite', description: 'Fragile', wearResistance: 'Poor - collector only' }, - { hardness: 4, mineral: 'Fluorite', description: 'Very fragile', wearResistance: 'Poor - collector only' }, - { hardness: 3, mineral: 'Calcite', description: 'Extremely fragile', wearResistance: 'Not suitable for jewelry' }, + { hardness: 10, mineral: 'Diamond', wearResistance: 'Excellent for all jewelry' }, + { hardness: 9, mineral: 'Corundum (Ruby/Sapphire)', wearResistance: 'Excellent for all jewelry' }, + { hardness: 8, mineral: 'Topaz / Spinel', wearResistance: 'Excellent for daily wear' }, + { hardness: 7.5, mineral: 'Beryl (Emerald / Aquamarine)', wearResistance: 'Good for most jewelry' }, + { hardness: 7, mineral: 'Quartz', wearResistance: 'Good for most jewelry' }, + { hardness: 6.5, mineral: 'Tanzanite / Peridot', wearResistance: 'Fair — avoid rings' }, + { hardness: 6, mineral: 'Feldspar (Moonstone)', wearResistance: 'Fair — pendants/earrings' }, + { hardness: 5, mineral: 'Apatite', wearResistance: 'Poor — collector only' }, + { hardness: 4, mineral: 'Fluorite', wearResistance: 'Poor — collector only' }, + { hardness: 3, mineral: 'Calcite', wearResistance: 'Not suitable for jewelry' }, ]; const COMMON_GEMS: GemHardness[] = [ - { name: 'Diamond', hardness: 10.0, wearability: 'Excellent', notes: 'Perfect cleavage - avoid sharp blows' }, - { name: 'Ruby', hardness: 9.0, wearability: 'Excellent', notes: 'No cleavage - extremely durable' }, - { name: 'Sapphire', hardness: 9.0, wearability: 'Excellent', notes: 'No cleavage - extremely durable' }, + { name: 'Diamond', hardness: 10.0, wearability: 'Excellent', notes: 'Perfect cleavage — avoid sharp blows' }, + { name: 'Ruby', hardness: 9.0, wearability: 'Excellent', notes: 'No cleavage — extremely durable' }, + { name: 'Sapphire', hardness: 9.0, wearability: 'Excellent', notes: 'No cleavage — extremely durable' }, { name: 'Alexandrite', hardness: 8.5, wearability: 'Excellent', notes: 'Good toughness' }, - { name: 'Chrysoberyl', hardness: 8.5, wearability: 'Excellent', notes: 'Excellent toughness' }, - { name: 'Spinel', hardness: 8.0, wearability: 'Excellent', notes: 'No cleavage - excellent toughness' }, - { name: 'Topaz', hardness: 8.0, wearability: 'Good', notes: 'Perfect cleavage - handle with care' }, - { name: 'Emerald', hardness: '7.5-8', wearability: 'Fair', notes: 'Usually included - avoid ultrasonic' }, - { name: 'Aquamarine', hardness: '7.5-8', wearability: 'Good', notes: 'Better toughness than emerald' }, - { name: 'Tourmaline', hardness: '7-7.5', wearability: 'Good', notes: 'Can be brittle - avoid sharp blows' }, - { name: 'Garnet', hardness: '6.5-7.5', wearability: 'Good', notes: 'Varies by type - generally durable' }, + { name: 'Spinel', hardness: 8.0, wearability: 'Excellent', notes: 'No cleavage — excellent toughness' }, + { name: 'Topaz', hardness: 8.0, wearability: 'Good', notes: 'Perfect cleavage — handle with care' }, + { name: 'Emerald', hardness: '7.5–8', wearability: 'Fair', notes: 'Usually included — avoid ultrasonic' }, + { name: 'Aquamarine', hardness: '7.5–8', wearability: 'Good', notes: 'Better toughness than emerald' }, + { name: 'Tourmaline', hardness: '7–7.5', wearability: 'Good', notes: 'Can be brittle — avoid sharp blows' }, + { name: 'Garnet', hardness: '6.5–7.5', wearability: 'Good', notes: 'Varies by type — generally durable' }, { name: 'Amethyst', hardness: 7.0, wearability: 'Good', notes: 'Durable but can fade in sunlight' }, - { name: 'Citrine', hardness: 7.0, wearability: 'Good', notes: 'Durable quartz variety' }, - { name: 'Peridot', hardness: '6.5-7', wearability: 'Fair', notes: 'Avoid rings - better for pendants' }, - { name: 'Tanzanite', hardness: '6-6.5', wearability: 'Fair', notes: 'Perfect cleavage - very fragile' }, - { name: 'Moonstone', hardness: '6-6.5', wearability: 'Fair', notes: 'Two cleavage directions - fragile' }, - { name: 'Opal', hardness: '5-6.5', wearability: 'Poor', notes: 'Hydrous - can crack/craze with drying' }, - { name: 'Turquoise', hardness: '5-6', wearability: 'Poor', notes: 'Porous - avoid chemicals/heat' }, - { name: 'Pearl', hardness: '2.5-4.5', wearability: 'Poor', notes: 'Organic - avoid acids/cosmetics/heat' }, + { name: 'Peridot', hardness: '6.5–7', wearability: 'Fair', notes: 'Avoid rings — better for pendants' }, + { name: 'Tanzanite', hardness: '6–6.5', wearability: 'Fair', notes: 'Perfect cleavage — very fragile' }, + { name: 'Moonstone', hardness: '6–6.5', wearability: 'Fair', notes: 'Two cleavage directions — fragile' }, + { name: 'Opal', hardness: '5–6.5', wearability: 'Poor', notes: 'Hydrous — can crack with drying' }, + { name: 'Turquoise', hardness: '5–6', wearability: 'Poor', notes: 'Porous — avoid chemicals/heat' }, + { name: 'Pearl', hardness: '2.5–4.5', wearability: 'Poor', notes: 'Organic — avoid acids/cosmetics' }, ]; +const WEARABILITY_COLORS: Record = { + Excellent: 'bg-green-100 text-green-700', + Good: 'bg-blue-100 text-blue-700', + Fair: 'bg-amber-100 text-amber-700', + Poor: 'bg-red-100 text-red-700', +}; + export function HardnessReference() { const [searchTerm, setSearchTerm] = useState(''); const [filterWearability, setFilterWearability] = useState('all'); @@ -65,102 +69,87 @@ export function HardnessReference() { }); return ( -
-
-

Mohs Hardness Scale

-
- - - - - - - - - - +
+ {/* Two-column layout: Mohs scale | Gem lookup */} +
+ {/* Left: Mohs scale — compact table */} +
+
+

Mohs Scale

+
+
HardnessReference MineralDescriptionWear Resistance
+ {MOHS_SCALE.map(level => ( - - - - - + + + + ))}
{level.hardness}{level.mineral}{level.description}{level.wearResistance}
{level.hardness}{level.mineral}{level.wearResistance}
-
-
-

Gemstone Hardness Lookup

- -
- setSearchTerm(e.target.value)} - className="flex-1 px-3 py-2 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-crystal-500" - /> - -
+ {/* Right: Searchable gem lookup */} +
+
+ setSearchTerm(e.target.value)} + className="flex-1 px-3 py-1.5 text-sm border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-amber-400" + /> + +
-
- - - - - - - - - - - {filteredGems.map(gem => ( - - - - - +
+
GemstoneHardnessWearabilitySpecial Notes
{gem.name}{gem.hardness} - - {gem.wearability} - - {gem.notes}
+ + + + + + - ))} - -
GemHardnessWearNotes
+ + + {filteredGems.map((gem, i) => ( + + {gem.name} + {gem.hardness} + + + {gem.wearability} + + + {gem.notes} + + ))} + + + {filteredGems.length === 0 && ( +

No matches found.

+ )} +
- - {filteredGems.length === 0 && ( -

No gemstones found matching your criteria.

- )}
-
-

Hardness vs Toughness

-
    -
  • • Hardness = resistance to scratching (Mohs scale)
  • -
  • • Toughness = resistance to breaking/chipping
  • -
  • • Diamond has high hardness but lower toughness (perfect cleavage)
  • -
  • • Jade has moderate hardness (6-7) but excellent toughness (no cleavage)
  • -
+ {/* Info strip */} +
+ • Hardness = resistance to scratching + • Toughness = resistance to breaking (not the same thing) + • Diamond is hard but has perfect cleavage — jade is softer but tougher
); diff --git a/src/components/identification/IdentificationTools.tsx b/src/components/identification/IdentificationTools.tsx index 0299ded..d1ade86 100644 --- a/src/components/identification/IdentificationTools.tsx +++ b/src/components/identification/IdentificationTools.tsx @@ -1,87 +1,51 @@ /** * Gem Identification tools container. + * Single-column, all-expanded layout — every tool visible at full width. */ -import { useState } from 'react'; import { GemComparison } from './GemComparison'; import { HardnessReference } from './HardnessReference'; import { FractureCleavageGuide } from './FractureCleavageGuide'; -import { CalculatorCard } from '../calculator/CalculatorCard'; +import { ToolSection } from '../ui/ToolSection'; -type IdentificationId = 'comparison' | 'hardness' | 'fracture'; - -interface IdentificationConfig { - id: IdentificationId; - label: string; - iconPath: string; - description: string; - component: React.ReactNode; -} - -const ICON_PATHS: Record = { +const ICON_PATHS = { compare: 'M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01', hardness: 'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z', split: 'M14 10l-2 1m0 0l-2-1m2 1v2.5M20 7l-2 1m2-1l-2-1m2 1v2.5M14 4l-2-1-2 1M4 7l2-1M4 7l2 1M4 7v2.5M12 21l-2-1m2 1l2-1m-2 1v-2.5M6 18l-2-1v-2.5M18 18l2-1v-2.5', }; -const IDENTIFICATION_TOOLS: IdentificationConfig[] = [ - { - id: 'comparison', - label: 'Gem Comparison', - iconPath: ICON_PATHS.compare, - description: 'Side-by-side property comparison (2-4 gems)', - component: , - }, - { - id: 'hardness', - label: 'Hardness Reference', - iconPath: ICON_PATHS.hardness, - description: 'Mohs scale + wearability guide', - component: , - }, - { - id: 'fracture', - label: 'Fracture & Cleavage', - iconPath: ICON_PATHS.split, - description: 'Cleavage directions + fracture patterns', - component: , - }, -]; - -export function IdentificationTools({ defaultExpanded = 'comparison' }: { defaultExpanded?: IdentificationId | null }) { - const [expandedTool, setExpandedTool] = useState(defaultExpanded); - - const handleToggle = (id: IdentificationId) => { - setExpandedTool(prev => (prev === id ? null : id)); - }; - +export function IdentificationTools() { return (
-
-

- Gem identification tools including side-by-side comparison, Mohs hardness reference, and cleavage/fracture patterns. -

-
- -
- {IDENTIFICATION_TOOLS.map(tool => ( - handleToggle(tool.id)} - > - {tool.component} - - ))} -
- -

- Tip: Click on any tool header to expand or collapse it. -

+ + + + + + + + + + +
); } diff --git a/src/components/lab/LabTools.tsx b/src/components/lab/LabTools.tsx index 3ffa757..35ff554 100644 --- a/src/components/lab/LabTools.tsx +++ b/src/components/lab/LabTools.tsx @@ -1,87 +1,51 @@ /** * Lab Equipment tools container. + * Single-column, all-expanded layout — every tool visible at full width. */ -import { useState } from 'react'; import { ChelseaFilter } from './ChelseaFilter'; import { SpectroscopeCalculator } from './SpectroscopeCalculator'; import { HeavyLiquidReference } from './HeavyLiquidReference'; -import { CalculatorCard } from '../calculator/CalculatorCard'; +import { ToolSection } from '../ui/ToolSection'; -type LabId = 'chelsea' | 'spectroscope' | 'heavy-liquid'; - -interface LabConfig { - id: LabId; - label: string; - iconPath: string; - description: string; - component: React.ReactNode; -} - -const ICON_PATHS: Record = { +const ICON_PATHS = { filter: 'M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-.293.707L12 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 018 17v-5.586L3.293 6.707A1 1 0 013 6V4z', spectrum: 'M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01', beaker: 'M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z', }; -const LAB_TOOLS: LabConfig[] = [ - { - id: 'chelsea', - label: 'Chelsea Filter', - iconPath: ICON_PATHS.filter, - description: 'Expected color reactions', - component: , - }, - { - id: 'spectroscope', - label: 'Spectroscope Calculator', - iconPath: ICON_PATHS.spectrum, - description: 'Wavelength ↔ color + absorption lines', - component: , - }, - { - id: 'heavy-liquid', - label: 'Heavy Liquid Reference', - iconPath: ICON_PATHS.beaker, - description: 'SG separation by liquid density', - component: , - }, -]; - -export function LabTools({ defaultExpanded = 'chelsea' }: { defaultExpanded?: LabId | null }) { - const [expandedTool, setExpandedTool] = useState(defaultExpanded); - - const handleToggle = (id: LabId) => { - setExpandedTool(prev => (prev === id ? null : id)); - }; - +export function LabTools() { return (
-
-

- Lab equipment reference tools including Chelsea filter reactions, spectroscope line calculator, and heavy liquid SG separation guide. -

-
- -
- {LAB_TOOLS.map(tool => ( - handleToggle(tool.id)} - > - {tool.component} - - ))} -
- -

- Tip: Click on any tool header to expand or collapse it. -

+ + + + + + + + + + +
); } diff --git a/src/components/optical/DichroscopeResults.tsx b/src/components/optical/DichroscopeResults.tsx index 544dece..e7d6558 100644 --- a/src/components/optical/DichroscopeResults.tsx +++ b/src/components/optical/DichroscopeResults.tsx @@ -1,6 +1,6 @@ /** * Dichroscope Results Lookup - * Input colors seen → suggest possible gems + * Full-width layout: filter bar across the top, matching gems in a responsive grid below. */ import { useState } from 'react'; @@ -18,7 +18,7 @@ const DICHROIC_GEMS: DichroscopeData[] = [ { gem: 'Sapphire (Blue)', color1: 'Blue', color2: 'Greenish-blue', strength: 'Strong', notes: 'Varies with saturation' }, { gem: 'Emerald', color1: 'Blue-green', color2: 'Yellow-green', strength: 'Moderate', notes: 'More visible in darker stones' }, { gem: 'Alexandrite', color1: 'Red/Purple', color2: 'Orange/Green', strength: 'Very Strong', notes: 'Color change + pleochroism' }, - { gem: 'Tanzanite', color1: 'Blue/Violet', color2: 'Purple/Red', strength: 'Very Strong', notes: 'Trichroic - see 3 colors' }, + { gem: 'Tanzanite', color1: 'Blue/Violet', color2: 'Purple/Red', strength: 'Very Strong', notes: 'Trichroic — see 3 colors' }, { gem: 'Tourmaline (Green)', color1: 'Dark green', color2: 'Light green', strength: 'Strong', notes: 'Best down c-axis' }, { gem: 'Tourmaline (Pink)', color1: 'Dark pink', color2: 'Light pink', strength: 'Strong', notes: 'Visible in most directions' }, { gem: 'Iolite', color1: 'Violet-blue', color2: 'Pale yellow', strength: 'Very Strong', notes: 'Dramatic color shift' }, @@ -30,6 +30,13 @@ const DICHROIC_GEMS: DichroscopeData[] = [ { gem: 'Morganite', color1: 'Pink', color2: 'Pale pink', strength: 'Weak', notes: 'Requires good stone' }, ]; +const STRENGTH_COLORS: Record = { + 'Very Strong': 'bg-red-100 text-red-700', + 'Strong': 'bg-orange-100 text-orange-700', + 'Moderate': 'bg-yellow-100 text-yellow-700', + 'Weak': 'bg-slate-100 text-slate-600', +}; + export function DichroscopeResults() { const [color1, setColor1] = useState(''); const [color2, setColor2] = useState(''); @@ -43,48 +50,35 @@ export function DichroscopeResults() { }); return ( -
-
-

- Enter the colors you observe through the dichroscope to identify possible gems. Rotate the stone to see both colors. -

-
- -
-
- +
+ {/* Filter bar — single row across full width */} +
+
+ setColor1(e.target.value)} - className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-crystal-500 focus:border-crystal-500" - placeholder="e.g., blue, red, green" + className="w-full px-3 py-1.5 text-sm border border-slate-300 rounded-lg focus:ring-2 focus:ring-purple-400 focus:border-purple-400" + placeholder="e.g., blue, red" />
- -
- +
+ setColor2(e.target.value)} - className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-crystal-500 focus:border-crystal-500" + className="w-full px-3 py-1.5 text-sm border border-slate-300 rounded-lg focus:ring-2 focus:ring-purple-400 focus:border-purple-400" placeholder="e.g., greenish-blue" />
- -
- +
+
+ {filtered.length} gem{filtered.length !== 1 ? 's' : ''}
+ {/* Results — responsive grid */} {filtered.length > 0 ? ( -
-

- Matching Gems ({filtered.length}) -

+
{filtered.map((gem, idx) => ( -
-
-
-
{gem.gem}
-
-
- Color 1:{' '} - {gem.color1} -
-
- Color 2:{' '} - {gem.color2} -
-
-

{gem.notes}

+
+
+
{gem.gem}
+ + {gem.strength} + +
+
+
+ 1:{' '} + {gem.color1}
- - {gem.strength} - + 2:{' '} + {gem.color2}
+

{gem.notes}

))}
) : ( -
- No matching gems found. Try different color terms. -
+
No matching gems found. Try different color terms.
)} -
-

Using a Dichroscope

-
    -
  • • View the stone through both windows while rotating
  • -
  • • Isotropic gems (cubic) show NO pleochroism
  • -
  • • Uniaxial gems (tetragonal, hexagonal, trigonal) show 2 colors
  • -
  • • Biaxial gems (orthorhombic, monoclinic, triclinic) show 2-3 colors
  • -
  • • Best viewed in strong light against white background
  • -
+ {/* Usage tips — inline */} +
+ • Isotropic gems (cubic) show no pleochroism + • Uniaxial gems show 2 colors + • Biaxial gems can show 2–3 colors + • Best viewed in strong light against white
); diff --git a/src/components/optical/OpticalTools.tsx b/src/components/optical/OpticalTools.tsx index 1d97b91..26fee02 100644 --- a/src/components/optical/OpticalTools.tsx +++ b/src/components/optical/OpticalTools.tsx @@ -1,96 +1,63 @@ /** * Optical Properties tools container. + * Single-column, all-expanded layout — every tool visible at full width. */ -import { useState } from 'react'; import { DichroscopeResults } from './DichroscopeResults'; import { PolariscopeGuide } from './PolariscopeGuide'; import { RefractometerSimulator } from './RefractometerSimulator'; import { PleochroismReference } from './PleochroismReference'; -import { CalculatorCard } from '../calculator/CalculatorCard'; +import { ToolSection } from '../ui/ToolSection'; -type OpticalId = 'dichroscope' | 'polariscope' | 'refractometer' | 'pleochroism'; - -interface OpticalConfig { - id: OpticalId; - label: string; - iconPath: string; - description: string; - component: React.ReactNode; -} - -const ICON_PATHS: Record = { +const ICON_PATHS = { eye: 'M15 12a3 3 0 11-6 0 3 3 0 016 0z M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z', adjust: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z M15 12a3 3 0 11-6 0 3 3 0 016 0z', beaker: 'M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z', book: 'M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253', }; -const OPTICAL_TOOLS: OpticalConfig[] = [ - { - id: 'dichroscope', - label: 'Dichroscope Results', - iconPath: ICON_PATHS.eye, - description: 'Input colors seen → suggest gems', - component: , - }, - { - id: 'polariscope', - label: 'Polariscope Guide', - iconPath: ICON_PATHS.adjust, - description: 'Interpret isotropic/anisotropic reactions', - component: , - }, - { - id: 'refractometer', - label: 'Refractometer Simulator', - iconPath: ICON_PATHS.beaker, - description: 'Practice reading shadow edges', - component: , - }, - { - id: 'pleochroism', - label: 'Pleochroism Reference', - iconPath: ICON_PATHS.book, - description: 'Expected colors by gem species', - component: , - }, -]; - -export function OpticalTools({ defaultExpanded = 'dichroscope' }: { defaultExpanded?: OpticalId | null }) { - const [expandedTool, setExpandedTool] = useState(defaultExpanded); - - const handleToggle = (id: OpticalId) => { - setExpandedTool(prev => (prev === id ? null : id)); - }; - +export function OpticalTools() { return (
-
-

- Optical property tools for gem identification using dichroscope, polariscope, refractometer, and pleochroism analysis. -

-
- -
- {OPTICAL_TOOLS.map(tool => ( - handleToggle(tool.id)} - > - {tool.component} - - ))} -
- -

- Tip: Click on any tool header to expand or collapse it. -

+ + + + + + + + + + + + + + +
); } diff --git a/src/components/ui/ToolSection.tsx b/src/components/ui/ToolSection.tsx new file mode 100644 index 0000000..f0640a7 --- /dev/null +++ b/src/components/ui/ToolSection.tsx @@ -0,0 +1,121 @@ +/** + * Full-width tool section with sticky header and colored accent. + * Replaces CalculatorCard for tool category pages — always expanded, + * single-column layout that uses 100% available width. + */ + +import type { ReactNode } from 'react'; + +type Accent = 'emerald' | 'blue' | 'purple' | 'rose' | 'amber' | 'cyan'; + +const ACCENT_CLASSES: Record = { + emerald: { + border: 'border-emerald-200', + iconBg: 'bg-emerald-100', + iconText: 'text-emerald-600', + headerBg: 'bg-emerald-50', + titleText: 'text-emerald-800', + strip: 'bg-emerald-500', + }, + blue: { + border: 'border-blue-200', + iconBg: 'bg-blue-100', + iconText: 'text-blue-600', + headerBg: 'bg-blue-50', + titleText: 'text-blue-800', + strip: 'bg-blue-500', + }, + purple: { + border: 'border-purple-200', + iconBg: 'bg-purple-100', + iconText: 'text-purple-600', + headerBg: 'bg-purple-50', + titleText: 'text-purple-800', + strip: 'bg-purple-500', + }, + rose: { + border: 'border-rose-200', + iconBg: 'bg-rose-100', + iconText: 'text-rose-600', + headerBg: 'bg-rose-50', + titleText: 'text-rose-800', + strip: 'bg-rose-500', + }, + amber: { + border: 'border-amber-200', + iconBg: 'bg-amber-100', + iconText: 'text-amber-600', + headerBg: 'bg-amber-50', + titleText: 'text-amber-800', + strip: 'bg-amber-500', + }, + cyan: { + border: 'border-cyan-200', + iconBg: 'bg-cyan-100', + iconText: 'text-cyan-600', + headerBg: 'bg-cyan-50', + titleText: 'text-cyan-800', + strip: 'bg-cyan-500', + }, +}; + +interface ToolSectionProps { + id: string; + title: string; + description: string; + iconPath: string; + accent: Accent; + children: ReactNode; +} + +export function ToolSection({ + id, + title, + description, + iconPath, + accent, + children, +}: ToolSectionProps) { + const colors = ACCENT_CLASSES[accent]; + + return ( +
+ {/* Colored accent strip + sticky header */} +
+
+
+
+ +
+
+

{title}

+

{description}

+
+
+
+ + {/* Content — always visible, full width */} +
+ {children} +
+
+ ); +}