{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://globestudio.app/schema/config.json",
  "title": "Globestudio configuration",
  "description": "Schema for a Globestudio configuration file (the JSON payload produced by Export → Share → Export configuration). Matches the importConfig contract in src/App.jsx.",
  "type": "object",
  "additionalProperties": true,
  "required": ["version"],
  "properties": {
    "version": {
      "type": ["integer", "string"],
      "description": "Schema version. Currently 1.",
      "examples": [1]
    },

    "selection": {
      "type": "string",
      "description": "Geographic selection. Use `world`, `country:USA` (3-letter ISO 3166-1 code), `continent:Europe`, or `subregion:Western Europe`.",
      "examples": ["world", "country:USA", "continent:Europe", "subregion:Western Europe"]
    },
    "stateSelection": {
      "type": "string",
      "description": "When selection is `country:USA`, the 2-letter US state postal code (`CA`, `TX`, `NY`, `all`).",
      "examples": ["all", "CA", "TX"]
    },

    "background": { "type": "string", "description": "Canvas background — hex color.", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
    "transparent": { "type": "boolean", "description": "Use transparent background instead of `background`." },
    "backgroundStyle": { "type": "string", "enum": ["solid", "space", "flow", "transparent"], "description": "`solid` paints a flat color; `space` paints a starfield-and-nebula scene; `flow` paints an animated gradient-mesh shader; `transparent` paints nothing and implies `transparent: true`." },

    "density": { "type": "number", "minimum": 1, "maximum": 100, "description": "Dot density across the visible area." },
    "dotSize": { "type": "number", "minimum": 0.1, "maximum": 30, "description": "Dot radius." },
    "dotColor": { "type": "string", "description": "Dot color — hex.", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
    "dotColorAlpha": { "type": "number", "minimum": 0, "maximum": 1 },
    "dotGradient": {
      "type": ["object", "null"],
      "description": "Linear gradient applied to dots (overrides `dotColor` when set).",
      "properties": {
        "from": { "type": "string", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
        "to": { "type": "string", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
        "angle": { "type": "number", "minimum": 0, "maximum": 360 }
      }
    },
    "dotsVisible": { "type": "boolean" },

    "shape": {
      "type": "string",
      "enum": ["Circle", "Square", "Hexagon", "Triangle", "Pentagon", "Diamond", "Star", "Plus", "Ring", "Voxel", "ParticleGrid", "ASCII", "Custom"]
    },
    "asciiSymbol": { "type": "string", "description": "Symbol used when `shape` is `ASCII`." },
    "customShape": { "type": ["string", "object", "null"], "description": "Custom SVG/path source when `shape` is `Custom`." },
    "dotRotation": { "type": "number", "minimum": 0, "maximum": 360 },
    "sizeVary": { "type": "boolean", "description": "Add per-dot size jitter." },
    "rotateAnimating": { "type": "boolean", "description": "Legacy field. The current control is `shapeRotationSpeed` (0–100, where 0 = stopped); when this is `false` on import, shapeRotationSpeed is forced to 0." },
    "shapeRotationSpeed": { "type": "number", "minimum": 0, "maximum": 100, "description": "Shape rotation speed 0–100 (0 stops the animation)." },

    "renderMode": { "type": "string", "enum": ["dots", "solid"] },

    "worldFill": { "type": "string", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
    "worldFillAlpha": { "type": "number", "minimum": 0, "maximum": 1 },
    "worldFillGradient": { "type": ["object", "null"] },
    "worldFillVisible": { "type": "boolean" },

    "worldStroke": { "type": "string", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
    "worldStrokeAlpha": { "type": "number", "minimum": 0, "maximum": 1 },
    "worldStrokeGradient": { "type": ["object", "null"] },
    "worldStrokeVisible": { "type": "boolean" },
    "worldStrokeWidth": { "type": "number", "minimum": 0, "maximum": 10 },

    "mapDepth": { "type": "number", "minimum": 0, "maximum": 100, "description": "FOV-style depth for the 3D map view." },
    "tiltX": { "type": "number", "minimum": -180, "maximum": 180 },
    "tiltY": { "type": "number", "minimum": -180, "maximum": 180 },

    "shaderSettings": {
      "type": "object",
      "description": "Per-shader configuration. `effect` picks which shader runs; the remaining fields are knobs honored by the selected effect.",
      "properties": {
        "effect": {
          "type": "string",
          "enum": ["none", "halftone", "risograph", "newsprint", "aurora", "pixel", "bayer", "atkinson", "edge", "threshold", "toon", "crt", "glitch", "badtv", "bloom", "metal", "iridescent", "pencil", "corrupt", "chromatic", "wave", "stripes", "rgb", "chroma"]
        },
        "intensity": { "type": "number", "minimum": 0, "maximum": 100 },
        "cellSize": { "type": "number", "minimum": 0, "maximum": 30 },
        "grain": { "type": "number", "minimum": 0, "maximum": 100 },
        "scanlines": { "type": "number", "minimum": 0, "maximum": 100 },
        "motion": { "type": "number", "minimum": 0, "maximum": 100 },
        "threshold": { "type": "number", "minimum": 0, "maximum": 100 },
        "split": { "type": "number", "minimum": 0, "maximum": 100 }
      },
      "additionalProperties": true
    },

    "globeSettings": {
      "type": "object",
      "description": "3D globe chrome — auto-spin, glow halo, grid mesh, network arcs, etc.",
      "properties": {
        "look": { "type": "string", "enum": ["classic", "borderless"] },
        "autoSpin": { "type": "boolean", "description": "Legacy field. The current control is `autoSpinSpeed` (0–100, where 0 stops the spin); when this is `false` on import, autoSpinSpeed is forced to 0." },
        "autoSpinSpeed": { "type": "number", "minimum": 0, "maximum": 100, "description": "Globe auto-rotation speed 0–100 (0 stops the spin)." },
        "glow": { "type": "boolean" },
        "glowStrength": { "type": "number", "minimum": 0, "maximum": 100 },
        "glowSpread": { "type": "number", "minimum": 0, "maximum": 100 },
        "grid": { "type": "boolean" },
        "gridStrength": { "type": "number", "minimum": 0, "maximum": 100 },
        "gridColor": { "type": "string", "description": "Hex string. Solid tint for all grid lines unless gridGradient is set." },
        "gridGradient": {
          "type": ["object", "null"],
          "description": "Optional 2-stop gradient applied as per-vertex colors interpolated by latitude (south → north).",
          "properties": {
            "from": { "type": "string" },
            "to": { "type": "string" }
          }
        },
        "network": { "type": "boolean", "description": "City pulses + great-circle route arcs." },
        "data": { "type": "boolean", "description": "Show the pasted data markers and their arcs. false hides them but keeps dataPoints." },
        "dataPoints": {
          "type": "array",
          "description": "Data markers, one per point, sized by value. The Data paste box writes this. Points with a missing or out of range lat or lng are dropped, and only the first 250 valid points are kept.",
          "maxItems": 250,
          "items": {
            "type": "object",
            "required": ["lat", "lng"],
            "properties": {
              "lat": { "type": "number", "minimum": -90, "maximum": 90, "description": "Latitude in degrees." },
              "lng": { "type": "number", "minimum": -180, "maximum": 180, "description": "Longitude in degrees." },
              "value": { "type": "number", "default": 1, "description": "Sets the marker size. Missing or not a number reads as 1." }
            }
          },
          "examples": [[{ "lat": 40.7, "lng": -74, "value": 10 }, { "lat": 51.5, "lng": -0.1, "value": 6 }]]
        },
        "dataArcs": { "type": "boolean", "description": "Draw an arc from each data point to the next, in list order. Needs at least 2 points; arcs show in globe view only." },
        "dataMarkerColor": { "type": ["string", "null"], "pattern": "^#?[0-9a-fA-F]{3,8}$", "description": "Hex color for the data markers and their arcs. null uses the default cyan (#7edfff).", "examples": ["#ff8800", null] },
        "networkStrength": { "type": "number", "minimum": 0, "maximum": 100 },
        "networkArcs": { "type": ["number", "boolean"], "minimum": 0, "maximum": 100, "description": "Arc density 0–100, or legacy bool (true = default density, false = hidden)." },
        "networkPulses": { "type": ["number", "boolean"], "minimum": 0, "maximum": 100, "description": "Pulse density 0–100, or legacy bool (true = default density, false = hidden)." },
        "networkMono": { "type": "boolean", "description": "Deprecated. Was the polychrome/mono toggle for network rendering. Ignored — use arcColor / pulseColor instead." },
        "arcColor": { "type": ["string", "null"], "description": "Hex string. Uniform tint for all network arc lines/trails/heads. null falls back to per-route hardcoded polychrome." },
        "pulseColor": { "type": ["string", "null"], "description": "Hex string. Uniform tint for all city pulse cores + rings. null falls back to per-hub hardcoded polychrome." },
        "surface": { "type": "boolean" },
        "surfaceStrength": { "type": "number", "minimum": 0, "maximum": 100 },
        "surfaceColor": { "type": "string", "description": "Hex string. Solid tint for the sphere fill unless surfaceGradient is set." },
        "surfaceGradient": {
          "type": ["object", "null"],
          "description": "Optional 2-stop gradient applied as per-vertex colors on the sphere geometry, interpolated by latitude (south → north).",
          "properties": {
            "from": { "type": "string" },
            "to": { "type": "string" }
          }
        }
      },
      "additionalProperties": true
    },

    "spaceSettings": {
      "type": "object",
      "description": "Starfield + nebula tuning. Applies when backgroundStyle is `space`.",
      "properties": {
        "density": { "type": "number", "minimum": 0, "maximum": 100 },
        "motion": { "type": "number", "minimum": 0, "maximum": 100 },
        "nebula": { "type": "number", "minimum": 0, "maximum": 100 },
        "hue": { "type": "number", "minimum": 0, "maximum": 360 },
        "brightness": { "type": "number", "minimum": 0, "maximum": 200 }
      }
    },

    "flowSettings": {
      "type": "object",
      "description": "Animated gradient-mesh tuning. Applies when backgroundStyle is `flow`.",
      "properties": {
        "colorA": { "type": "string", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
        "colorB": { "type": "string", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
        "colorC": { "type": "string", "pattern": "^#?[0-9a-fA-F]{3,8}$" },
        "motion": { "type": "number", "minimum": 0, "maximum": 100 },
        "turbulence": { "type": "number", "minimum": 0, "maximum": 100 },
        "grain": { "type": "number", "minimum": 0, "maximum": 100 },
        "scale": { "type": "number", "minimum": 0, "maximum": 100 },
        "brightness": { "type": "number", "minimum": 0, "maximum": 200 }
      }
    },

    "animationsEnabled": { "type": "boolean" },

    "preset": {
      "type": "string",
      "description": "Optional — when set, the importer applies this preset's defaults first, then layers the other fields on top.",
      "enum": ["default", "halftone", "risograph", "newsprint", "aurora", "pixel", "bayer", "atkinson", "wireframe", "crt", "glitch", "badtv", "bloom", "metal", "iridescent", "pencil", "corrupt", "toon", "threshold", "vapor", "topographic"]
    },

    "flatProjection": {
      "type": "string",
      "enum": ["mercator", "equalEarth", "naturalEarth1", "winkel3", "robinson"],
      "description": "Projection for the flat map view. It applies to solid maps (renderMode \"solid\"); dotted maps always use Mercator."
    },

    "riversVisible": { "type": "boolean" },
    "citiesVisible": { "type": "boolean" },
    "citiesMinPop": { "type": "number", "minimum": 0 }
  }
}
