diff --git a/README.md b/README.md
new file mode 100644
index 0000000..116f6e2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+# Daggerheart Environment Overlay
+
+A FoundryVTT module for the **Daggerheart** system that allows GMs to link **Environment** type actors to specific scenes. When a linked scene is active, the environment actor's token is displayed as an interactive, persistent overlay on the canvas.
+
+## Features
+
+- **Scene Linking**: Drag and drop an Environment actor into the Scene Configuration to link it.
+- **Interactive Overlay**: Click the overlay to instantly open the Environment actor's sheet.
+- **Movable Interface**: Hold `Alt` + Left Click and Drag to reposition the overlay. The position is saved per-scene.
+- **Customizable**:
+ - Toggle the display of the actor's name.
+ - Customize the border color of the overlay token.
+- **Permission Support**: The overlay respects ownership permissions, ensuring only relevant users can see or interact with it.
diff --git a/module.json b/module.json
new file mode 100644
index 0000000..76b3af7
--- /dev/null
+++ b/module.json
@@ -0,0 +1,35 @@
+{
+ "id": "dh-environment-overlay",
+ "title": "Daggerheart Environment Overlay",
+ "description": "Links Environment actors to scenes, displaying them as interactive, movable overlays on the canvas.",
+ "version": "1.0.0",
+ "compatibility": {
+ "minimum": "13",
+ "verified": "13"
+ },
+ "authors": [
+ {
+ "name": "Antigravity"
+ }
+ ],
+ "relationships": {
+ "systems": [
+ {
+ "id": "daggerheart",
+ "type": "system",
+ "compatibility": {
+ "minimum": "1.0.0"
+ }
+ }
+ ]
+ },
+ "esmodules": [
+ "scripts/module.js"
+ ],
+ "styles": [
+ "styles/module.css"
+ ],
+ "url": "",
+ "manifest": "",
+ "download": ""
+}
\ No newline at end of file
diff --git a/scripts/module.js b/scripts/module.js
new file mode 100644
index 0000000..ac7865d
--- /dev/null
+++ b/scripts/module.js
@@ -0,0 +1,286 @@
+const MODULE_ID = "dh-environment-overlay";
+const FLAG_KEY = "environmentUuid";
+
+Hooks.once("init", () => {
+ console.log(`${MODULE_ID} | Initializing Daggerheart Environment Overlay`);
+
+ game.settings.register(MODULE_ID, "borderColor", {
+ name: "Overlay Border Color",
+ hint: "Color of the environment token border.",
+ scope: "world",
+ config: true,
+ type: String,
+ default: "#f3c267",
+ onChange: () => renderEnvironmentOverlay()
+ });
+
+ game.settings.register(MODULE_ID, "showName", {
+ name: "Show Actor Name",
+ hint: "Display the environment actor's name below the token.",
+ scope: "world",
+ config: true,
+ type: Boolean,
+ default: true,
+ onChange: () => renderEnvironmentOverlay()
+ });
+});
+
+/**
+ * Handle Scene Configuration Render
+ */
+Hooks.on("renderSceneConfig", async (app, html, data) => {
+ // Determine if html is a jQuery object or HTMLElement
+ const $html = html instanceof HTMLElement ? $(html) : html;
+
+ // Target the Daggerheart specific tab
+ const dhTab = $html.find('.tab[data-tab="dh"]');
+
+ const environmentUuid = app.document.getFlag(MODULE_ID, FLAG_KEY);
+
+ let currentActor = null;
+ if (environmentUuid) {
+ currentActor = await fromUuid(environmentUuid);
+ }
+
+ const dropZoneHtml = `
+
+
+
+
+ ${currentActor ? `
+
+
+ ${currentActor.name}
+
+
+ ` : `
+
+ Drag & Drop Environment Actor Here
+
+ `}
+
+
+
Link a Daggerheart Environment Actor to this scene. Its token will appear as an overlay.
+
+ `;
+
+ // Inject into the DH tab if it exists
+ if (dhTab.length > 0) {
+ dhTab.append(dropZoneHtml);
+ } else {
+ // Fallback: Try to put it in the first tab or top of form
+ const form = $html.find("form");
+ const nameGroup = $html.find("input[name='name']").closest(".form-group");
+ if (nameGroup.length > 0) {
+ nameGroup.after(dropZoneHtml);
+ } else {
+ form.prepend(dropZoneHtml);
+ }
+ }
+
+ const dropZone = $html.find(".dh-environment-wrapper");
+
+ // Add drag/drop listeners
+ if (dropZone.length === 0) return;
+
+ dropZone[0].addEventListener("dragover", (event) => {
+ event.preventDefault();
+ dropZone.find(".dh-environment-drop-zone").addClass("drag-hover");
+ });
+
+ dropZone[0].addEventListener("dragleave", (event) => {
+ event.preventDefault();
+ dropZone.find(".dh-environment-drop-zone").removeClass("drag-hover");
+ });
+
+ dropZone[0].addEventListener("drop", async (event) => {
+ event.preventDefault();
+ dropZone.find(".dh-environment-drop-zone").removeClass("drag-hover");
+
+ try {
+ const data = TextEditor.getDragEventData(event);
+ if (data.type !== "Actor") return;
+
+ const actor = await fromUuid(data.uuid);
+ if (!actor || actor.type !== "environment") {
+ ui.notifications.warn("DH Environment Overlay | Please drop a valid 'Environment' type Actor.");
+ return;
+ }
+
+ updateDropZoneDisplay(dropZone, actor);
+
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ // Handle Remove
+ dropZone.on("click", ".dh-environment-remove", () => {
+ updateDropZoneDisplay(dropZone, null);
+ });
+
+ // Helper to update display and hidden input
+ function updateDropZoneDisplay(wrapper, actor) {
+ // Find or create hidden input
+ let input = wrapper.find(`input[name="flags.${MODULE_ID}.${FLAG_KEY}"]`);
+ if (input.length === 0) {
+ input = $(``);
+ wrapper.append(input);
+ }
+
+ input.val(actor ? actor.uuid : "");
+
+ const content = actor ? `
+
+
+ ${actor.name}
+
+
+ ` : `
+
+ Drag & Drop Environment Actor Here
+
+ `;
+
+ wrapper.children().not("input").remove();
+ wrapper.append(content);
+ }
+
+ // Ensure hidden input exists for initial validation if null
+ let existingInput = $html.find(`input[name="flags.${MODULE_ID}.${FLAG_KEY}"]`);
+ if (existingInput.length === 0) {
+ dropZone.append(``);
+ }
+
+ // Force resize because we added content
+ app.setPosition({ height: "auto" });
+
+});
+
+/**
+ * Handle Overlay Rendering
+ */
+async function renderEnvironmentOverlay() {
+ // Remove existing
+ const existing = $("#dh-environment-overlay");
+ if (existing.length) existing.remove();
+
+ if (!canvas.scene) return;
+
+ const environmentUuid = canvas.scene.getFlag(MODULE_ID, FLAG_KEY);
+ if (!environmentUuid) return;
+
+ try {
+ const actor = await fromUuid(environmentUuid);
+ if (!actor) return; // Maybe deleted?
+
+ // PERMISSION CHECK
+ // testUserPermission second arg "OBSERVER" means user needs at least OBSERVER level.
+ // If users need only LIMITED, use "LIMITED".
+ // For opening the sheet (which we do on click), usually OBSERVER/OWNER is needed to see much,
+ // but even LIMITED users can open sheet (partial view).
+ // Let's assume OBSERVER is good to see the overlay (since they can see the token).
+ // Actually, if it's an "Environment" actor, players might need at least Limited to interactions.
+ // Let's stick to OBSERVER as safe default, or LIMITED if desired.
+ // User said "players who have permissions to access the actor".
+ // This implies at least LIMITED.
+ if (!actor.testUserPermission(game.user, "LIMITED")) {
+ return;
+ }
+
+ const position = canvas.scene.getFlag(MODULE_ID, "overlayPosition") || { top: 100, right: 320 };
+ const borderColor = game.settings.get(MODULE_ID, "borderColor");
+ const showName = game.settings.get(MODULE_ID, "showName");
+
+ let styleStr = "";
+ if (position.left !== undefined) styleStr += `left: ${position.left}px;`;
+ else if (position.right !== undefined) styleStr += `right: ${position.right}px;`;
+
+ if (position.top !== undefined) styleStr += `top: ${position.top}px;`;
+ else if (position.bottom !== undefined) styleStr += `bottom: ${position.bottom}px;`;
+
+ const overlay = $(`
+