{
 "note": "Day/night cycle and weather contract (ADR-030: accepted by the owner on 2026-10-06, built from day one, with toggles in the GM arena). Look and sound only: no state changes a server outcome. Every number is a proposed starting value until it is measured on the reference devices.",
 "decision": "ADR-030",
 "status": "accepted_direction_not_implemented",
 "proposed": true,
 "rules": [
  "Presentation only: time of day and weather never change damage, hit shapes, timings, AI, drops or movement.",
  "One sky per channel: the zone server owns the world clock and each area's weather state, so every client in the channel shows the same time and weather.",
  "Telegraphs, ground previews and nameplates pass the contrast check in every phase and every weather state on Low.",
  "Weather never hides a threat: haze, rain and storms shorten the view of decoration only, never of telegraphs, enemies in the fight or the player.",
  "Every tier shows every state: Low changes the grade, fog and sky and drops weather particles first.",
  "Lightning and other flashes obey the flash limit (ACC-01) and Reduce flashing.",
  "Each area keeps its mood from world.json as a grade and fog profile over every phase; its signature phase is the story-camera capture time.",
  "Review and performance: every area, character and effect is captured in the review matrix, and perf scenes run in the area's heaviest state."
 ],
 "clock": {
  "proposed": true,
  "note": "One full day every two hours, so any normal session sees both day and night. The server sends the world time; clients only interpolate. dawnStartsAt (proposed, M0-12) places the phases on a 24-hour clock for /gm time HH:MM: dawn starts at 05:00, so day runs 07:24–19:24, dusk to 21:48 and night to 05:00. A phase jump (/gm time night) lands in the middle of that phase, clear of the blends.",
  "dayLengthMinutes": 120,
  "phaseBlendSeconds": 90,
  "dawnStartsAt": "05:00"
 },
 "phases": [
  { "id": "dawn", "share": 0.1, "look": "low warm sun, long shadows, ground mist where the area has water" },
  { "id": "day", "share": 0.5, "look": "high sun, the area's base grade" },
  { "id": "dusk", "share": 0.1, "look": "low amber sun; lanterns and glowing props come on" },
  { "id": "night", "share": 0.3, "look": "moonlit blue key light with a brightness floor; lanterns, embers and effects carry the light" }
 ],
 "nightFloor": "Night is a look, not darkness: characters still read at 64 px (WLD-06) and telegraphs keep full contrast.",
 "weather": [
  { "id": "clear", "look": "none", "sound": "area bed only", "particles": "none" },
  { "id": "dust_haze", "look": "warm fog, soft sun", "sound": "light wind layer", "particles": "sparse drifting dust" },
  { "id": "overcast", "look": "flat cool light, no hard shadows", "sound": "low wind layer", "particles": "none" },
  { "id": "rain", "look": "wet ground response, darker grade", "sound": "rain layer by surface", "particles": "rain streaks and ground splashes" },
  { "id": "sandstorm", "look": "dense ochre fog, strong wind on plants and cloth", "sound": "strong wind layer", "particles": "fast dust sheets" }
 ],
 "areaWeather": {
  "note": "Read from each area's mood in world.json and the biome table in docs/production/ENVIRONMENT.md; confirm in the area brief.",
  "amber_gate": ["clear", "dust_haze"],
  "dewgrass_meadow": ["clear", "overcast", "rain"],
  "old_orchard": ["clear", "overcast", "rain"],
  "reedwater_banks": ["clear", "overcast", "rain"],
  "clay_cut": ["clear", "dust_haze"],
  "cinder_kilns": ["clear", "dust_haze"],
  "saltway_flats": ["clear", "dust_haze", "sandstorm"],
  "bellshade_court": ["clear", "dust_haze"],
  "stormquill_ridge": ["overcast", "rain"],
  "ashroot_grove": ["overcast", "dust_haze"],
  "jade_seal_ruins": ["clear", "overcast"],
  "caravan_crown_arena": ["clear", "dust_haze", "sandstorm"]
 },
 "signaturePhase": {
  "note": "The phase that shows the area's mood best; used for the 12 story-camera captures.",
  "amber_gate": "dusk",
  "dewgrass_meadow": "dawn",
  "old_orchard": "day",
  "reedwater_banks": "day",
  "clay_cut": "day",
  "cinder_kilns": "night",
  "saltway_flats": "day",
  "bellshade_court": "dusk",
  "stormquill_ridge": "day",
  "ashroot_grove": "dusk",
  "jade_seal_ruins": "night",
  "caravan_crown_arena": "dusk"
 },
 "weatherChange": {
  "proposed": true,
  "note": "Weather is per area and changes slowly, so a fight never changes look halfway through a telegraph. Automatic weather (proposed, M0-12): server time is cut into slots of minHoldMinutes; each slot picks one of the area's allowed states by weights (relative odds, a seeded hash of seed, area and slot, so every client and the server agree), and a change blends over blendSeconds at the slot start. heaviness lists the states from lightest to heaviest by fog density and particle load; the review matrix's 'heaviest the area allows' is the last allowed state in that order. A GM force applies at once, without a blend.",
  "minHoldMinutes": 10,
  "blendSeconds": 60,
  "weights": { "clear": 6, "dust_haze": 3, "overcast": 3, "rain": 2, "sandstorm": 1 },
  "heaviness": ["clear", "overcast", "dust_haze", "rain", "sandstorm"]
 },
 "budget": {
  "proposed": true,
  "note": "Inside the tier totals of docs/tech/PERFORMANCE.md, never on top of them. tierLiveParticleTotals (M0-12B) copies the total column of that table's 'Live particles (own / total)' row so the client reads it from data: a tier's weather cap is total x share, Low uses lowTierWeatherParticles.",
  "weatherShareOfTotalLiveParticles": 0.1,
  "lowTierWeatherParticles": 0,
  "extraDrawCallsMax": 2,
  "tierLiveParticleTotals": { "low": 900, "mid": 2000, "high": 5000, "ultra": 8000 }
 },
 "look": {
  "proposed": true,
  "note": "M0-12B starting look of the placeholder sky in the Boot scene (GM arena stand-in); area grades from world.json come later. Colours are sRGB hex. Per phase: the key light (the sun, the moon at night: one directional light so every tier lights the night) with elevation/azimuth in degrees (light rotation = Euler(elevation, azimuth, 0)), its colour and URP intensity, the flat ambient colour, the sky (camera clear colour) and exp2 fog colour and density per metre. Per weather: fog density added, a hue tint for fog and sky at the phase's brightness (tintWeight 0-1), key and ambient scales, the share of the tier's weather particle cap and the particle kind. ambientFloor is the night brightness floor: the minimum relative luminance (WCAG, 0-1) of the ambient light in any state. Tune on the reference devices.",
  "phases": {
   "dawn": { "key": "sun", "elevation": 12, "azimuth": 70, "color": "#ffb27a", "intensity": 0.8, "ambient": "#7c6f78", "sky": "#e8aa86", "fog": "#d9ad92", "fogDensity": 0.01 },
   "day": { "key": "sun", "elevation": 55, "azimuth": -20, "color": "#fff1dc", "intensity": 1.3, "ambient": "#8a97a8", "sky": "#93c0e8", "fog": "#b8cad9", "fogDensity": 0.004 },
   "dusk": { "key": "sun", "elevation": 9, "azimuth": -110, "color": "#ff9554", "intensity": 0.7, "ambient": "#6a5866", "sky": "#c47a5e", "fog": "#b0806c", "fogDensity": 0.008 },
   "night": { "key": "moon", "elevation": 40, "azimuth": -150, "color": "#9fb4ff", "intensity": 0.3, "ambient": "#2b3450", "sky": "#0c1322", "fog": "#18203a", "fogDensity": 0.009 }
  },
  "weather": {
   "clear": { "fogDensityAdd": 0, "tint": "#ffffff", "tintWeight": 0, "keyScale": 1, "ambientScale": 1, "particles": 0, "particle": "none" },
   "dust_haze": { "fogDensityAdd": 0.016, "tint": "#d4ab72", "tintWeight": 0.55, "keyScale": 0.75, "ambientScale": 1.05, "particles": 0.25, "particle": "dust" },
   "overcast": { "fogDensityAdd": 0.006, "tint": "#8e98a2", "tintWeight": 0.5, "keyScale": 0.35, "ambientScale": 1.2, "particles": 0, "particle": "none" },
   "rain": { "fogDensityAdd": 0.012, "tint": "#6d7b8a", "tintWeight": 0.5, "keyScale": 0.4, "ambientScale": 0.85, "particles": 1, "particle": "rain" },
   "sandstorm": { "fogDensityAdd": 0.045, "tint": "#b9884c", "tintWeight": 0.8, "keyScale": 0.3, "ambientScale": 1, "particles": 1, "particle": "sheets" }
  },
  "ambientFloor": 0.035,
  "particles": {
   "note": "Weather particle kinds: wind speed and fall speed in m/s, size in metres (a streak's width), stretch = streak length factor (1 = round sprite; the streak lies along its own velocity), gust = random spread of each particle's velocity per axis in m/s (varied streak angles, so wind reads as wind), alpha, lifetime in seconds, colour. They spawn in a box (metres) that follows the camera; nearFade (metres) fades particles in from the camera to that view depth, so none fills the screen. One particle system, so at most one extra draw call. M0-12F: sheets went from 0.35 m round blobs (stretch 3) to thin 0.05 m x 0.8 m wind streaks.",
   "spawnBox": [36, 14, 36],
   "nearFade": 2.5,
   "kinds": {
    "dust": { "speed": 1.5, "fall": 0.2, "size": 0.18, "stretch": 1, "gust": 0.3, "alpha": 0.35, "lifetime": 6, "color": "#d8b47e" },
    "sheets": { "speed": 11, "fall": 0.5, "size": 0.05, "stretch": 16, "gust": 2.5, "alpha": 0.4, "lifetime": 2.5, "color": "#c99a5b" },
    "rain": { "speed": 0.8, "fall": 16, "size": 0.035, "stretch": 6, "gust": 0.5, "alpha": 0.45, "lifetime": 1, "color": "#c6d2de" }
   }
  },
  "telegraph": {
   "note": "Ground telegraphs are unlit, fog-exempt and drawn after weather particles, so night, fog and weather never hide them. fill is the monster telegraph colour of docs/production/VFX.md; edgeLight (inner edge) and edgeDark (outer rim) are proposed so one edge always contrasts with any ground. minContrast is the WCAG 2 non-text contrast ratio (1.4.11, 3:1) that the better of fill, edgeLight and edgeDark must reach against the ground next to the telegraph, measured on the rendered frame on Low in every review-matrix state.",
   "fill": "#ff5a2a",
   "edgeLight": "#fff1e0",
   "edgeDark": "#120500",
   "minContrast": 3
  }
 },
 "audio": "Each area bed gets a night variant, and weather adds one looped layer on the ambience bus (wind, rain). The requests are added to gen-audio-requests.mjs when that generator is next changed; none exist yet.",
 "gm": [
  { "cmd": "/gm time <dawn|day|dusk|night|HH:MM>", "does": "Jump the GM arena's clock to a phase or a time." },
  { "cmd": "/gm cycle <on|off|x1|x10|x60>", "does": "Run, pause or speed up the day in the GM arena." },
  { "cmd": "/gm weather <clear|dust_haze|overcast|rain|sandstorm|auto>", "does": "Force a weather state in the GM arena, or return to automatic." }
 ],
 "gmPanel": "The GM arena shows Time and Weather buttons that send the same commands. They affect that GM instance only.",
 "reviewMatrix": [
  { "phase": "day", "weather": "clear" },
  { "phase": "night", "weather": "clear" },
  { "phase": "dusk", "weather": "dust_haze" },
  { "phase": "night", "weather": "heaviest the area allows" }
 ]
}
