From 601afa88064fb61f044c577530f41aa906d1d8c9 Mon Sep 17 00:00:00 2001 From: Nick Beckley Date: Sat, 20 Jun 2026 18:44:58 +0100 Subject: [PATCH] Implemented the first-pass Location Floor Plans foundation. --- PlotLine/Controllers/FloorPlansController.cs | 133 +++++ PlotLine/Data/Repositories.cs | 103 ++++ PlotLine/Models/CoreModels.cs | 56 +++ PlotLine/Program.cs | 2 + PlotLine/Services/CoreServices.cs | 258 ++++++++++ PlotLine/Services/ProjectAccessFilter.cs | 4 + PlotLine/Sql/071_LocationFloorPlans.sql | 453 +++++++++++++++++ PlotLine/ViewModels/CoreViewModels.cs | 80 +++ PlotLine/Views/FloorPlans/Edit.cshtml | 457 ++++++++++++++++++ PlotLine/Views/FloorPlans/Index.cshtml | 79 +++ .../Views/Shared/_ProjectSectionNav.cshtml | 1 + PlotLine/wwwroot/css/site.css | 231 +++++++++ PlotLine/wwwroot/css/site.min.css | 2 +- 13 files changed, 1858 insertions(+), 1 deletion(-) create mode 100644 PlotLine/Controllers/FloorPlansController.cs create mode 100644 PlotLine/Sql/071_LocationFloorPlans.sql create mode 100644 PlotLine/Views/FloorPlans/Edit.cshtml create mode 100644 PlotLine/Views/FloorPlans/Index.cshtml diff --git a/PlotLine/Controllers/FloorPlansController.cs b/PlotLine/Controllers/FloorPlansController.cs new file mode 100644 index 0000000..47bc007 --- /dev/null +++ b/PlotLine/Controllers/FloorPlansController.cs @@ -0,0 +1,133 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using PlotLine.Services; +using PlotLine.ViewModels; + +namespace PlotLine.Controllers; + +[Authorize] +public sealed class FloorPlansController(IFloorPlanService floorPlans) : Controller +{ + public async Task Index(int projectId) + { + var model = await floorPlans.ListAsync(projectId); + return model is null ? NotFound() : View(model); + } + + public async Task Edit(int id) + { + var model = await floorPlans.GetEditorAsync(id); + return model is null ? NotFound() : View(model); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Create(FloorPlanEditViewModel model) + { + if (!ModelState.IsValid) + { + return RedirectToAction(nameof(Index), new { projectId = model.ProjectID }); + } + + var floorPlanId = await floorPlans.SavePlanAsync(model); + return RedirectToAction(nameof(Edit), new { id = floorPlanId }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SavePlan(FloorPlanEditViewModel model) + { + if (!ModelState.IsValid) + { + var editor = await floorPlans.GetEditorAsync(model.FloorPlanID); + if (editor is null) + { + return NotFound(); + } + + editor.FloorPlan = model; + return View("Edit", editor); + } + + var floorPlanId = await floorPlans.SavePlanAsync(model); + TempData["FloorPlanMessage"] = "Floor plan saved."; + return RedirectToAction(nameof(Edit), new { id = floorPlanId }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SaveLayout(FloorPlanEditorViewModel model) + { + try + { + await floorPlans.SaveLayoutAsync(model); + TempData["FloorPlanMessage"] = "Layout saved."; + } + catch (InvalidOperationException ex) + { + TempData["FloorPlanError"] = ex.Message; + } + + return RedirectToAction(nameof(Edit), new { id = model.FloorPlan.FloorPlanID }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DeletePlan(int id, int projectId) + { + await floorPlans.DeletePlanAsync(id); + TempData["FloorPlanMessage"] = "Floor plan deleted."; + return RedirectToAction(nameof(Index), new { projectId }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task AddFloor(FloorPlanFloorEditViewModel model) + { + if (ModelState.IsValid) + { + await floorPlans.SaveFloorAsync(model); + TempData["FloorPlanMessage"] = "Floor added."; + } + + return RedirectToAction(nameof(Edit), new { id = model.FloorPlanID }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DeleteFloor(int floorPlanId, int floorPlanFloorId) + { + await floorPlans.DeleteFloorAsync(floorPlanFloorId); + TempData["FloorPlanMessage"] = "Floor deleted."; + return RedirectToAction(nameof(Edit), new { id = floorPlanId }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task AddBlock(int floorPlanId, FloorPlanBlockEditViewModel model) + { + try + { + if (ModelState.IsValid) + { + await floorPlans.SaveBlockAsync(model); + TempData["FloorPlanMessage"] = "Block added."; + } + } + catch (InvalidOperationException ex) + { + TempData["FloorPlanError"] = ex.Message; + } + + return RedirectToAction(nameof(Edit), new { id = floorPlanId }); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task DeleteBlock(int floorPlanId, int floorPlanBlockId) + { + await floorPlans.DeleteBlockAsync(floorPlanBlockId); + TempData["FloorPlanMessage"] = "Block deleted."; + return RedirectToAction(nameof(Edit), new { id = floorPlanId }); + } +} diff --git a/PlotLine/Data/Repositories.cs b/PlotLine/Data/Repositories.cs index 6408ebe..a0d3407 100644 --- a/PlotLine/Data/Repositories.cs +++ b/PlotLine/Data/Repositories.cs @@ -214,6 +214,20 @@ public interface ILocationRepository Task> ListAssetsByLocationAsync(int locationId); } +public interface IFloorPlanRepository +{ + Task> ListByProjectAsync(int projectId); + Task GetAsync(int floorPlanId); + Task<(FloorPlan? FloorPlan, IReadOnlyList Floors, IReadOnlyList Blocks)> GetEditorAsync(int floorPlanId); + Task GetFloorAsync(int floorPlanFloorId); + Task SavePlanAsync(FloorPlan floorPlan); + Task DeletePlanAsync(int floorPlanId); + Task SaveFloorAsync(FloorPlanFloor floor); + Task DeleteFloorAsync(int floorPlanFloorId); + Task SaveBlockAsync(FloorPlanBlock block); + Task DeleteBlockAsync(int floorPlanBlockId); +} + public interface IWarningRepository { Task GetLookupsAsync(); @@ -522,6 +536,95 @@ public sealed class LocationRepository(ISqlConnectionFactory connectionFactory) } } +public sealed class FloorPlanRepository(ISqlConnectionFactory connectionFactory) : IFloorPlanRepository +{ + public async Task> ListByProjectAsync(int projectId) + { + using var connection = connectionFactory.CreateConnection(); + var rows = await connection.QueryAsync("dbo.FloorPlan_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure); + return rows.ToList(); + } + + public async Task GetAsync(int floorPlanId) + { + using var connection = connectionFactory.CreateConnection(); + return await connection.QuerySingleOrDefaultAsync("dbo.FloorPlan_Get", new { FloorPlanID = floorPlanId }, commandType: CommandType.StoredProcedure); + } + + public async Task<(FloorPlan? FloorPlan, IReadOnlyList Floors, IReadOnlyList Blocks)> GetEditorAsync(int floorPlanId) + { + using var connection = connectionFactory.CreateConnection(); + using var result = await connection.QueryMultipleAsync("dbo.FloorPlan_GetEditor", new { FloorPlanID = floorPlanId }, commandType: CommandType.StoredProcedure); + var floorPlan = await result.ReadSingleOrDefaultAsync(); + var floors = (await result.ReadAsync()).ToList(); + var blocks = (await result.ReadAsync()).ToList(); + return (floorPlan, floors, blocks); + } + + public async Task GetFloorAsync(int floorPlanFloorId) + { + using var connection = connectionFactory.CreateConnection(); + return await connection.QuerySingleOrDefaultAsync("dbo.FloorPlanFloor_Get", new { FloorPlanFloorID = floorPlanFloorId }, commandType: CommandType.StoredProcedure); + } + + public async Task SavePlanAsync(FloorPlan floorPlan) + { + using var connection = connectionFactory.CreateConnection(); + return await connection.QuerySingleAsync( + "dbo.FloorPlan_Save", + new { floorPlan.FloorPlanID, floorPlan.ProjectID, floorPlan.Name, floorPlan.Description }, + commandType: CommandType.StoredProcedure); + } + + public async Task DeletePlanAsync(int floorPlanId) + { + using var connection = connectionFactory.CreateConnection(); + await connection.ExecuteAsync("dbo.FloorPlan_Delete", new { FloorPlanID = floorPlanId }, commandType: CommandType.StoredProcedure); + } + + public async Task SaveFloorAsync(FloorPlanFloor floor) + { + using var connection = connectionFactory.CreateConnection(); + return await connection.QuerySingleAsync( + "dbo.FloorPlanFloor_Save", + new { floor.FloorPlanFloorID, floor.FloorPlanID, floor.Name, floor.SortOrder, floor.GridWidth, floor.GridHeight }, + commandType: CommandType.StoredProcedure); + } + + public async Task DeleteFloorAsync(int floorPlanFloorId) + { + using var connection = connectionFactory.CreateConnection(); + await connection.ExecuteAsync("dbo.FloorPlanFloor_Delete", new { FloorPlanFloorID = floorPlanFloorId }, commandType: CommandType.StoredProcedure); + } + + public async Task SaveBlockAsync(FloorPlanBlock block) + { + using var connection = connectionFactory.CreateConnection(); + return await connection.QuerySingleAsync( + "dbo.FloorPlanBlock_Save", + new + { + block.FloorPlanBlockID, + block.FloorPlanFloorID, + block.LocationID, + block.X, + block.Y, + block.WidthCells, + block.HeightCells, + block.BlockType, + block.LabelOverride, + block.Notes + }, + commandType: CommandType.StoredProcedure); + } + + public async Task DeleteBlockAsync(int floorPlanBlockId) + { + using var connection = connectionFactory.CreateConnection(); + await connection.ExecuteAsync("dbo.FloorPlanBlock_Delete", new { FloorPlanBlockID = floorPlanBlockId }, commandType: CommandType.StoredProcedure); + } +} + public sealed class WarningRepository(ISqlConnectionFactory connectionFactory) : IWarningRepository { public async Task GetLookupsAsync() diff --git a/PlotLine/Models/CoreModels.cs b/PlotLine/Models/CoreModels.cs index eb8e2cc..c484d25 100644 --- a/PlotLine/Models/CoreModels.cs +++ b/PlotLine/Models/CoreModels.cs @@ -1125,6 +1125,62 @@ public sealed class LocationRelationship public bool IsArchived { get; set; } } +public static class FloorPlanBlockTypes +{ + public const string Room = "Room"; + public const string Corridor = "Corridor"; + public const string OpenSpace = "OpenSpace"; + public const string Exterior = "Exterior"; + public const string Other = "Other"; + + public static readonly IReadOnlyList All = [Room, Corridor, OpenSpace, Exterior, Other]; + + public static bool IsValid(string? blockType) => All.Contains(blockType ?? string.Empty, StringComparer.OrdinalIgnoreCase); +} + +public sealed class FloorPlan +{ + public int FloorPlanID { get; set; } + public int ProjectID { get; set; } + public string Name { get; set; } = string.Empty; + public string? Description { get; set; } + public DateTime CreatedDate { get; set; } + public DateTime? ModifiedDate { get; set; } + public int FloorCount { get; set; } + public int BlockCount { get; set; } +} + +public sealed class FloorPlanFloor +{ + public int FloorPlanFloorID { get; set; } + public int FloorPlanID { get; set; } + public string Name { get; set; } = string.Empty; + public int SortOrder { get; set; } + public int GridWidth { get; set; } = 24; + public int GridHeight { get; set; } = 16; + public DateTime CreatedDate { get; set; } + public DateTime? ModifiedDate { get; set; } +} + +public sealed class FloorPlanBlock +{ + public int FloorPlanBlockID { get; set; } + public int FloorPlanFloorID { get; set; } + public int LocationID { get; set; } + public string LocationName { get; set; } = string.Empty; + public string LocationPath { get; set; } = string.Empty; + public int X { get; set; } + public int Y { get; set; } + public int WidthCells { get; set; } = 1; + public int HeightCells { get; set; } = 1; + public string BlockType { get; set; } = FloorPlanBlockTypes.Room; + public string? LabelOverride { get; set; } + public string? Notes { get; set; } + public DateTime CreatedDate { get; set; } + public DateTime? ModifiedDate { get; set; } + public string DisplayLabel => string.IsNullOrWhiteSpace(LabelOverride) ? LocationName : LabelOverride; +} + public sealed class SceneAssetLocation { public int SceneAssetLocationID { get; set; } diff --git a/PlotLine/Program.cs b/PlotLine/Program.cs index bda7ea3..ae31a18 100644 --- a/PlotLine/Program.cs +++ b/PlotLine/Program.cs @@ -106,6 +106,7 @@ public class Program builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -142,6 +143,7 @@ public class Program builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/PlotLine/Services/CoreServices.cs b/PlotLine/Services/CoreServices.cs index 0beab72..dda5afc 100644 --- a/PlotLine/Services/CoreServices.cs +++ b/PlotLine/Services/CoreServices.cs @@ -177,6 +177,19 @@ public interface ILocationService Task DeleteSceneAssetLocationAsync(int sceneAssetLocationId); } +public interface IFloorPlanService +{ + Task ListAsync(int projectId); + Task GetEditorAsync(int floorPlanId); + Task SavePlanAsync(FloorPlanEditViewModel model); + Task DeletePlanAsync(int floorPlanId); + Task SaveFloorAsync(FloorPlanFloorEditViewModel model); + Task DeleteFloorAsync(int floorPlanFloorId); + Task SaveBlockAsync(FloorPlanBlockEditViewModel model); + Task SaveLayoutAsync(FloorPlanEditorViewModel model); + Task DeleteBlockAsync(int floorPlanBlockId); +} + public interface IContinuityValidationService { Task GetDashboardAsync(int projectId, int? bookId, int? chapterId, int? sceneId, int? warningTypeId, int? warningSeverityId, string? entityType, string? category, bool includeDismissed, bool includeIntentional, bool showAcknowledgedWarnings, ContinuityValidationSummary? summary = null); @@ -7509,6 +7522,251 @@ public sealed class LocationService(IProjectRepository projects, ILocationReposi } } +public sealed class FloorPlanService( + IProjectRepository projects, + IFloorPlanRepository floorPlans, + ILocationRepository locations, + IProjectActivityService activity) : IFloorPlanService +{ + public async Task ListAsync(int projectId) + { + var project = await projects.GetAsync(projectId); + return project is null ? null : new FloorPlanListViewModel + { + Project = project, + FloorPlans = await floorPlans.ListByProjectAsync(projectId), + NewFloorPlan = new FloorPlanEditViewModel { ProjectID = projectId, Name = "New floor plan" } + }; + } + + public async Task GetEditorAsync(int floorPlanId) + { + var data = await floorPlans.GetEditorAsync(floorPlanId); + if (data.FloorPlan is null) + { + return null; + } + + var project = await projects.GetAsync(data.FloorPlan.ProjectID); + if (project is null) + { + return null; + } + + var projectLocations = await locations.ListByProjectAsync(project.ProjectID); + return new FloorPlanEditorViewModel + { + Project = project, + FloorPlan = new FloorPlanEditViewModel + { + FloorPlanID = data.FloorPlan.FloorPlanID, + ProjectID = data.FloorPlan.ProjectID, + Name = data.FloorPlan.Name, + Description = data.FloorPlan.Description + }, + Floors = data.Floors.Select(ToFloorEdit).ToList(), + Blocks = data.Blocks.Select(ToBlockEdit).ToList(), + NewFloor = new FloorPlanFloorEditViewModel + { + FloorPlanID = data.FloorPlan.FloorPlanID, + Name = "New floor", + SortOrder = data.Floors.Any() ? data.Floors.Max(x => x.SortOrder) + 10 : 10, + GridWidth = 24, + GridHeight = 16 + }, + NewBlock = new FloorPlanBlockEditViewModel + { + FloorPlanFloorID = data.Floors.FirstOrDefault()?.FloorPlanFloorID ?? 0, + BlockType = FloorPlanBlockTypes.Room, + WidthCells = 2, + HeightCells = 1 + }, + LocationOptions = projectLocations + .Select(x => new SelectListItem(string.IsNullOrWhiteSpace(x.LocationPath) ? x.LocationName : x.LocationPath, x.LocationID.ToString())) + .ToList(), + BlockTypeOptions = FloorPlanBlockTypes.All.Select(x => new SelectListItem(BlockTypeLabel(x), x)).ToList() + }; + } + + public async Task SavePlanAsync(FloorPlanEditViewModel model) + { + var isNew = model.FloorPlanID == 0; + var floorPlanId = await floorPlans.SavePlanAsync(new FloorPlan + { + FloorPlanID = model.FloorPlanID, + ProjectID = model.ProjectID, + Name = model.Name.Trim(), + Description = model.Description + }); + + if (isNew) + { + await floorPlans.SaveFloorAsync(new FloorPlanFloor + { + FloorPlanID = floorPlanId, + Name = "Ground Floor", + SortOrder = 10, + GridWidth = 24, + GridHeight = 16 + }); + } + + await activity.RecordAsync(model.ProjectID, isNew ? "Created" : "Updated", "Floor Plan", floorPlanId, model.Name); + return floorPlanId; + } + + public async Task DeletePlanAsync(int floorPlanId) + { + var floorPlan = await floorPlans.GetAsync(floorPlanId); + await floorPlans.DeletePlanAsync(floorPlanId); + if (floorPlan is not null) + { + await activity.RecordAsync(floorPlan.ProjectID, "Deleted", "Floor Plan", floorPlanId, floorPlan.Name); + } + } + + public async Task SaveFloorAsync(FloorPlanFloorEditViewModel model) + { + ValidateFloor(model); + return await floorPlans.SaveFloorAsync(new FloorPlanFloor + { + FloorPlanFloorID = model.FloorPlanFloorID, + FloorPlanID = model.FloorPlanID, + Name = model.Name.Trim(), + SortOrder = model.SortOrder, + GridWidth = model.GridWidth, + GridHeight = model.GridHeight + }); + } + + public Task DeleteFloorAsync(int floorPlanFloorId) => floorPlans.DeleteFloorAsync(floorPlanFloorId); + + public async Task SaveBlockAsync(FloorPlanBlockEditViewModel model) + { + var floor = await floorPlans.GetFloorAsync(model.FloorPlanFloorID) + ?? throw new InvalidOperationException("Floor not found."); + var plan = await floorPlans.GetAsync(floor.FloorPlanID) + ?? throw new InvalidOperationException("Floor plan not found."); + var projectId = plan.ProjectID; + await ValidateBlockAsync(model, floor, projectId); + return await floorPlans.SaveBlockAsync(ToBlock(model)); + } + + public async Task SaveLayoutAsync(FloorPlanEditorViewModel model) + { + var data = await floorPlans.GetEditorAsync(model.FloorPlan.FloorPlanID); + if (data.FloorPlan is null) + { + throw new InvalidOperationException("Floor plan not found."); + } + + await SavePlanAsync(model.FloorPlan); + var floorsById = data.Floors.ToDictionary(x => x.FloorPlanFloorID); + + foreach (var floorModel in model.Floors) + { + ValidateFloor(floorModel); + if (floorModel.FloorPlanID != data.FloorPlan.FloorPlanID) + { + throw new InvalidOperationException("Floor does not belong to this floor plan."); + } + + await SaveFloorAsync(floorModel); + } + + foreach (var blockModel in model.Blocks) + { + if (!floorsById.TryGetValue(blockModel.FloorPlanFloorID, out var floor)) + { + throw new InvalidOperationException("Block floor not found."); + } + + await ValidateBlockAsync(blockModel, floor, data.FloorPlan.ProjectID); + await floorPlans.SaveBlockAsync(ToBlock(blockModel)); + } + } + + public Task DeleteBlockAsync(int floorPlanBlockId) => floorPlans.DeleteBlockAsync(floorPlanBlockId); + + private async Task ValidateBlockAsync(FloorPlanBlockEditViewModel model, FloorPlanFloor floor, int projectId) + { + if (!FloorPlanBlockTypes.IsValid(model.BlockType)) + { + throw new InvalidOperationException("Choose a valid block type."); + } + + if (model.X < 0 || model.Y < 0 || model.WidthCells < 1 || model.HeightCells < 1) + { + throw new InvalidOperationException("Blocks must have a valid position and size."); + } + + if (model.X + model.WidthCells > floor.GridWidth || model.Y + model.HeightCells > floor.GridHeight) + { + throw new InvalidOperationException("Block is outside the floor grid."); + } + + var location = await locations.GetAsync(model.LocationID); + if (location is null || location.ProjectID != projectId) + { + throw new InvalidOperationException("Choose a location from this project."); + } + } + + private static void ValidateFloor(FloorPlanFloorEditViewModel model) + { + if (model.GridWidth is < 8 or > 80 || model.GridHeight is < 8 or > 80) + { + throw new InvalidOperationException("Grid width and height must be between 8 and 80."); + } + } + + private static FloorPlanFloorEditViewModel ToFloorEdit(FloorPlanFloor floor) => new() + { + FloorPlanFloorID = floor.FloorPlanFloorID, + FloorPlanID = floor.FloorPlanID, + Name = floor.Name, + SortOrder = floor.SortOrder, + GridWidth = floor.GridWidth, + GridHeight = floor.GridHeight + }; + + private static FloorPlanBlockEditViewModel ToBlockEdit(FloorPlanBlock block) => new() + { + FloorPlanBlockID = block.FloorPlanBlockID, + FloorPlanFloorID = block.FloorPlanFloorID, + LocationID = block.LocationID, + LocationName = block.LocationName, + LocationPath = block.LocationPath, + X = block.X, + Y = block.Y, + WidthCells = block.WidthCells, + HeightCells = block.HeightCells, + BlockType = block.BlockType, + LabelOverride = block.LabelOverride, + Notes = block.Notes + }; + + private static FloorPlanBlock ToBlock(FloorPlanBlockEditViewModel model) => new() + { + FloorPlanBlockID = model.FloorPlanBlockID, + FloorPlanFloorID = model.FloorPlanFloorID, + LocationID = model.LocationID, + X = model.X, + Y = model.Y, + WidthCells = model.WidthCells, + HeightCells = model.HeightCells, + BlockType = model.BlockType, + LabelOverride = model.LabelOverride, + Notes = model.Notes + }; + + private static string BlockTypeLabel(string blockType) => blockType switch + { + FloorPlanBlockTypes.OpenSpace => "Open space", + _ => blockType + }; +} + public sealed class CharacterService( IProjectRepository projects, IBookRepository books, diff --git a/PlotLine/Services/ProjectAccessFilter.cs b/PlotLine/Services/ProjectAccessFilter.cs index ca37b9d..8bda11f 100644 --- a/PlotLine/Services/ProjectAccessFilter.cs +++ b/PlotLine/Services/ProjectAccessFilter.cs @@ -52,6 +52,9 @@ public sealed class ProjectAccessFilter(IProjectAccessService access, ICurrentUs ["ParentLocationID"] = "Location", ["FromLocationID"] = "Location", ["ToLocationID"] = "Location", + ["FloorPlanID"] = "FloorPlan", + ["FloorPlanFloorID"] = "FloorPlanFloor", + ["FloorPlanBlockID"] = "FloorPlanBlock", ["PlotLineID"] = "PlotLine", ["ParentPlotLineID"] = "PlotLine", ["PlotThreadID"] = "PlotThread", @@ -68,6 +71,7 @@ public sealed class ProjectAccessFilter(IProjectAccessService access, ICurrentUs ["Chapters"] = new(StringComparer.OrdinalIgnoreCase) { ["id"] = "Chapter", ["chapterId"] = "Chapter", ["bookId"] = "Book" }, ["Characters"] = new(StringComparer.OrdinalIgnoreCase) { ["id"] = "Character", ["characterId"] = "Character", ["projectId"] = "Project" }, ["Locations"] = new(StringComparer.OrdinalIgnoreCase) { ["id"] = "Location", ["locationId"] = "Location", ["returnLocationId"] = "Location", ["projectId"] = "Project" }, + ["FloorPlans"] = new(StringComparer.OrdinalIgnoreCase) { ["id"] = "FloorPlan", ["floorPlanId"] = "FloorPlan", ["floorPlanFloorId"] = "FloorPlanFloor", ["floorPlanBlockId"] = "FloorPlanBlock", ["projectId"] = "Project" }, ["PlotLines"] = new(StringComparer.OrdinalIgnoreCase) { ["id"] = "PlotLine", ["plotLineId"] = "PlotLine", ["projectId"] = "Project" }, ["PlotThreads"] = new(StringComparer.OrdinalIgnoreCase) { ["id"] = "PlotThread", ["plotThreadId"] = "PlotThread", ["plotLineId"] = "PlotLine", ["projectId"] = "Project" }, ["ProjectMetrics"] = new(StringComparer.OrdinalIgnoreCase) { ["projectId"] = "Project" }, diff --git a/PlotLine/Sql/071_LocationFloorPlans.sql b/PlotLine/Sql/071_LocationFloorPlans.sql new file mode 100644 index 0000000..e4e194e --- /dev/null +++ b/PlotLine/Sql/071_LocationFloorPlans.sql @@ -0,0 +1,453 @@ +IF OBJECT_ID(N'dbo.FloorPlanBlocks', N'U') IS NULL +BEGIN + CREATE TABLE dbo.FloorPlanBlocks + ( + FloorPlanBlockID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_FloorPlanBlocks PRIMARY KEY, + FloorPlanFloorID int NOT NULL, + LocationID int NOT NULL, + X int NOT NULL, + Y int NOT NULL, + WidthCells int NOT NULL, + HeightCells int NOT NULL, + BlockType nvarchar(40) NOT NULL CONSTRAINT DF_FloorPlanBlocks_BlockType DEFAULT N'Room', + LabelOverride nvarchar(200) NULL, + Notes nvarchar(max) NULL, + CreatedDate datetime2 NOT NULL CONSTRAINT DF_FloorPlanBlocks_CreatedDate DEFAULT SYSUTCDATETIME(), + ModifiedDate datetime2 NULL + ); +END; +GO + +IF OBJECT_ID(N'dbo.FloorPlanFloors', N'U') IS NULL +BEGIN + CREATE TABLE dbo.FloorPlanFloors + ( + FloorPlanFloorID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_FloorPlanFloors PRIMARY KEY, + FloorPlanID int NOT NULL, + Name nvarchar(100) NOT NULL, + SortOrder int NOT NULL CONSTRAINT DF_FloorPlanFloors_SortOrder DEFAULT 0, + GridWidth int NOT NULL CONSTRAINT DF_FloorPlanFloors_GridWidth DEFAULT 24, + GridHeight int NOT NULL CONSTRAINT DF_FloorPlanFloors_GridHeight DEFAULT 16, + CreatedDate datetime2 NOT NULL CONSTRAINT DF_FloorPlanFloors_CreatedDate DEFAULT SYSUTCDATETIME(), + ModifiedDate datetime2 NULL + ); +END; +GO + +IF OBJECT_ID(N'dbo.FloorPlans', N'U') IS NULL +BEGIN + CREATE TABLE dbo.FloorPlans + ( + FloorPlanID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_FloorPlans PRIMARY KEY, + ProjectID int NOT NULL, + Name nvarchar(200) NOT NULL, + Description nvarchar(max) NULL, + CreatedDate datetime2 NOT NULL CONSTRAINT DF_FloorPlans_CreatedDate DEFAULT SYSUTCDATETIME(), + ModifiedDate datetime2 NULL + ); +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_FloorPlans_Projects') +BEGIN + ALTER TABLE dbo.FloorPlans + ADD CONSTRAINT FK_FloorPlans_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID); +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_FloorPlanFloors_FloorPlans') +BEGIN + ALTER TABLE dbo.FloorPlanFloors + ADD CONSTRAINT FK_FloorPlanFloors_FloorPlans FOREIGN KEY (FloorPlanID) REFERENCES dbo.FloorPlans(FloorPlanID) ON DELETE CASCADE; +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_FloorPlanBlocks_FloorPlanFloors') +BEGIN + ALTER TABLE dbo.FloorPlanBlocks + ADD CONSTRAINT FK_FloorPlanBlocks_FloorPlanFloors FOREIGN KEY (FloorPlanFloorID) REFERENCES dbo.FloorPlanFloors(FloorPlanFloorID) ON DELETE CASCADE; +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_FloorPlanBlocks_Locations') +BEGIN + ALTER TABLE dbo.FloorPlanBlocks + ADD CONSTRAINT FK_FloorPlanBlocks_Locations FOREIGN KEY (LocationID) REFERENCES dbo.Locations(LocationID); +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CK_FloorPlanFloors_GridSize') +BEGIN + ALTER TABLE dbo.FloorPlanFloors + ADD CONSTRAINT CK_FloorPlanFloors_GridSize CHECK (GridWidth BETWEEN 8 AND 80 AND GridHeight BETWEEN 8 AND 80); +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CK_FloorPlanBlocks_PositionSize') +BEGIN + ALTER TABLE dbo.FloorPlanBlocks + ADD CONSTRAINT CK_FloorPlanBlocks_PositionSize CHECK (X >= 0 AND Y >= 0 AND WidthCells >= 1 AND HeightCells >= 1); +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CK_FloorPlanBlocks_BlockType') +BEGIN + ALTER TABLE dbo.FloorPlanBlocks + ADD CONSTRAINT CK_FloorPlanBlocks_BlockType CHECK (BlockType IN (N'Room', N'Corridor', N'OpenSpace', N'Exterior', N'Other')); +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_FloorPlans_ProjectID' AND object_id = OBJECT_ID(N'dbo.FloorPlans')) +BEGIN + CREATE INDEX IX_FloorPlans_ProjectID ON dbo.FloorPlans(ProjectID); +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_FloorPlanFloors_FloorPlanID' AND object_id = OBJECT_ID(N'dbo.FloorPlanFloors')) +BEGIN + CREATE INDEX IX_FloorPlanFloors_FloorPlanID ON dbo.FloorPlanFloors(FloorPlanID); +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_FloorPlanBlocks_FloorPlanFloorID' AND object_id = OBJECT_ID(N'dbo.FloorPlanBlocks')) +BEGIN + CREATE INDEX IX_FloorPlanBlocks_FloorPlanFloorID ON dbo.FloorPlanBlocks(FloorPlanFloorID); +END; +GO + +IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_FloorPlanBlocks_LocationID' AND object_id = OBJECT_ID(N'dbo.FloorPlanBlocks')) +BEGIN + CREATE INDEX IX_FloorPlanBlocks_LocationID ON dbo.FloorPlanBlocks(LocationID); +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlan_ListByProject + @ProjectID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT + fp.FloorPlanID, + fp.ProjectID, + fp.Name, + fp.Description, + fp.CreatedDate, + fp.ModifiedDate, + COUNT(DISTINCT fpf.FloorPlanFloorID) AS FloorCount, + COUNT(fpb.FloorPlanBlockID) AS BlockCount + FROM dbo.FloorPlans fp + LEFT JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanID = fp.FloorPlanID + LEFT JOIN dbo.FloorPlanBlocks fpb ON fpb.FloorPlanFloorID = fpf.FloorPlanFloorID + WHERE fp.ProjectID = @ProjectID + GROUP BY fp.FloorPlanID, fp.ProjectID, fp.Name, fp.Description, fp.CreatedDate, fp.ModifiedDate + ORDER BY fp.Name; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlan_Get + @FloorPlanID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT FloorPlanID, ProjectID, Name, Description, CreatedDate, ModifiedDate + FROM dbo.FloorPlans + WHERE FloorPlanID = @FloorPlanID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlan_GetEditor + @FloorPlanID int +AS +BEGIN + SET NOCOUNT ON; + + EXEC dbo.FloorPlan_Get @FloorPlanID; + + SELECT FloorPlanFloorID, FloorPlanID, Name, SortOrder, GridWidth, GridHeight, CreatedDate, ModifiedDate + FROM dbo.FloorPlanFloors + WHERE FloorPlanID = @FloorPlanID + ORDER BY SortOrder, Name; + + SELECT + fpb.FloorPlanBlockID, + fpb.FloorPlanFloorID, + fpb.LocationID, + lp.LocationName, + COALESCE(NULLIF(lp.LocationPath, N''), lp.LocationName) AS LocationPath, + fpb.X, + fpb.Y, + fpb.WidthCells, + fpb.HeightCells, + fpb.BlockType, + fpb.LabelOverride, + fpb.Notes, + fpb.CreatedDate, + fpb.ModifiedDate + FROM dbo.FloorPlanBlocks fpb + INNER JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = fpb.FloorPlanFloorID + INNER JOIN dbo.LocationPaths lp ON lp.LocationID = fpb.LocationID + WHERE fpf.FloorPlanID = @FloorPlanID + ORDER BY fpf.SortOrder, fpb.Y, fpb.X, lp.LocationName; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlan_Save + @FloorPlanID int, + @ProjectID int, + @Name nvarchar(200), + @Description nvarchar(max) = NULL +AS +BEGIN + SET NOCOUNT ON; + + SET @Name = NULLIF(LTRIM(RTRIM(@Name)), N''); + IF @Name IS NULL + THROW 51000, 'Floor plan name is required.', 1; + + IF @FloorPlanID = 0 + BEGIN + INSERT dbo.FloorPlans (ProjectID, Name, Description) + VALUES (@ProjectID, @Name, @Description); + + SELECT CONVERT(int, SCOPE_IDENTITY()); + RETURN; + END; + + UPDATE dbo.FloorPlans + SET Name = @Name, + Description = @Description, + ModifiedDate = SYSUTCDATETIME() + WHERE FloorPlanID = @FloorPlanID + AND ProjectID = @ProjectID; + + SELECT @FloorPlanID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlan_Delete + @FloorPlanID int +AS +BEGIN + SET NOCOUNT ON; + + DELETE dbo.FloorPlans + WHERE FloorPlanID = @FloorPlanID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlanFloor_Get + @FloorPlanFloorID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT FloorPlanFloorID, FloorPlanID, Name, SortOrder, GridWidth, GridHeight, CreatedDate, ModifiedDate + FROM dbo.FloorPlanFloors + WHERE FloorPlanFloorID = @FloorPlanFloorID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlanFloor_Save + @FloorPlanFloorID int, + @FloorPlanID int, + @Name nvarchar(100), + @SortOrder int, + @GridWidth int, + @GridHeight int +AS +BEGIN + SET NOCOUNT ON; + + SET @Name = NULLIF(LTRIM(RTRIM(@Name)), N''); + IF @Name IS NULL + THROW 51001, 'Floor name is required.', 1; + + IF @GridWidth NOT BETWEEN 8 AND 80 OR @GridHeight NOT BETWEEN 8 AND 80 + THROW 51002, 'Grid width and height must be between 8 and 80.', 1; + + IF @FloorPlanFloorID = 0 + BEGIN + INSERT dbo.FloorPlanFloors (FloorPlanID, Name, SortOrder, GridWidth, GridHeight) + VALUES (@FloorPlanID, @Name, @SortOrder, @GridWidth, @GridHeight); + + SELECT CONVERT(int, SCOPE_IDENTITY()); + RETURN; + END; + + UPDATE dbo.FloorPlanFloors + SET Name = @Name, + SortOrder = @SortOrder, + GridWidth = @GridWidth, + GridHeight = @GridHeight, + ModifiedDate = SYSUTCDATETIME() + WHERE FloorPlanFloorID = @FloorPlanFloorID + AND FloorPlanID = @FloorPlanID; + + SELECT @FloorPlanFloorID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlanFloor_Delete + @FloorPlanFloorID int +AS +BEGIN + SET NOCOUNT ON; + + DELETE dbo.FloorPlanFloors + WHERE FloorPlanFloorID = @FloorPlanFloorID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlanBlock_Save + @FloorPlanBlockID int, + @FloorPlanFloorID int, + @LocationID int, + @X int, + @Y int, + @WidthCells int, + @HeightCells int, + @BlockType nvarchar(40), + @LabelOverride nvarchar(200) = NULL, + @Notes nvarchar(max) = NULL +AS +BEGIN + SET NOCOUNT ON; + + DECLARE @GridWidth int, @GridHeight int, @ProjectID int; + + SELECT + @GridWidth = fpf.GridWidth, + @GridHeight = fpf.GridHeight, + @ProjectID = fp.ProjectID + FROM dbo.FloorPlanFloors fpf + INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID + WHERE fpf.FloorPlanFloorID = @FloorPlanFloorID; + + IF @ProjectID IS NULL + THROW 51003, 'Floor not found.', 1; + + IF NOT EXISTS (SELECT 1 FROM dbo.Locations WHERE LocationID = @LocationID AND ProjectID = @ProjectID AND IsArchived = 0) + THROW 51004, 'Location must belong to this project.', 1; + + IF @BlockType NOT IN (N'Room', N'Corridor', N'OpenSpace', N'Exterior', N'Other') + THROW 51005, 'Choose a valid block type.', 1; + + IF @X < 0 OR @Y < 0 OR @WidthCells < 1 OR @HeightCells < 1 OR @X + @WidthCells > @GridWidth OR @Y + @HeightCells > @GridHeight + THROW 51006, 'Block is outside the floor grid.', 1; + + IF @FloorPlanBlockID = 0 + BEGIN + INSERT dbo.FloorPlanBlocks (FloorPlanFloorID, LocationID, X, Y, WidthCells, HeightCells, BlockType, LabelOverride, Notes) + VALUES (@FloorPlanFloorID, @LocationID, @X, @Y, @WidthCells, @HeightCells, @BlockType, NULLIF(LTRIM(RTRIM(@LabelOverride)), N''), @Notes); + + SELECT CONVERT(int, SCOPE_IDENTITY()); + RETURN; + END; + + UPDATE dbo.FloorPlanBlocks + SET FloorPlanFloorID = @FloorPlanFloorID, + LocationID = @LocationID, + X = @X, + Y = @Y, + WidthCells = @WidthCells, + HeightCells = @HeightCells, + BlockType = @BlockType, + LabelOverride = NULLIF(LTRIM(RTRIM(@LabelOverride)), N''), + Notes = @Notes, + ModifiedDate = SYSUTCDATETIME() + WHERE FloorPlanBlockID = @FloorPlanBlockID; + + SELECT @FloorPlanBlockID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.FloorPlanBlock_Delete + @FloorPlanBlockID int +AS +BEGIN + SET NOCOUNT ON; + + DELETE dbo.FloorPlanBlocks + WHERE FloorPlanBlockID = @FloorPlanBlockID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.ProjectAccess_UserCanAccessEntity + @EntityType nvarchar(100), + @EntityID int, + @UserID int +AS +BEGIN + SET NOCOUNT ON; + + DECLARE @ProjectID int; + + IF @EntityType = N'Project' + SET @ProjectID = @EntityID; + ELSE IF @EntityType = N'Book' + SELECT @ProjectID = ProjectID FROM dbo.Books WHERE BookID = @EntityID; + ELSE IF @EntityType = N'Chapter' + SELECT @ProjectID = b.ProjectID FROM dbo.Chapters c INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE c.ChapterID = @EntityID; + ELSE IF @EntityType = N'Scene' + SELECT @ProjectID = b.ProjectID FROM dbo.Scenes s INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE s.SceneID = @EntityID; + ELSE IF @EntityType = N'Character' + SELECT @ProjectID = ProjectID FROM dbo.Characters WHERE CharacterID = @EntityID; + ELSE IF @EntityType = N'StoryAsset' + SELECT @ProjectID = ProjectID FROM dbo.StoryAssets WHERE StoryAssetID = @EntityID; + ELSE IF @EntityType = N'Location' + SELECT @ProjectID = ProjectID FROM dbo.Locations WHERE LocationID = @EntityID; + ELSE IF @EntityType = N'FloorPlan' + SELECT @ProjectID = ProjectID FROM dbo.FloorPlans WHERE FloorPlanID = @EntityID; + ELSE IF @EntityType = N'FloorPlanFloor' + SELECT @ProjectID = fp.ProjectID FROM dbo.FloorPlanFloors fpf INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID WHERE fpf.FloorPlanFloorID = @EntityID; + ELSE IF @EntityType = N'FloorPlanBlock' + SELECT @ProjectID = fp.ProjectID FROM dbo.FloorPlanBlocks fpb INNER JOIN dbo.FloorPlanFloors fpf ON fpf.FloorPlanFloorID = fpb.FloorPlanFloorID INNER JOIN dbo.FloorPlans fp ON fp.FloorPlanID = fpf.FloorPlanID WHERE fpb.FloorPlanBlockID = @EntityID; + ELSE IF @EntityType = N'PlotLine' + SELECT @ProjectID = ProjectID FROM dbo.PlotLines WHERE PlotLineID = @EntityID; + ELSE IF @EntityType = N'PlotThread' + SELECT @ProjectID = pl.ProjectID FROM dbo.PlotThreads pt INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID WHERE pt.PlotThreadID = @EntityID; + ELSE IF @EntityType = N'CharacterRelationship' + SELECT @ProjectID = ProjectID FROM dbo.CharacterRelationships WHERE CharacterRelationshipID = @EntityID; + ELSE IF @EntityType = N'LocationRelationship' + SELECT @ProjectID = l.ProjectID FROM dbo.LocationRelationships lr INNER JOIN dbo.Locations l ON l.LocationID = lr.FromLocationID WHERE lr.LocationRelationshipID = @EntityID; + ELSE IF @EntityType = N'TimelineViewPreset' + SELECT @ProjectID = ProjectID FROM dbo.TimelineViewPresets WHERE TimelineViewPresetID = @EntityID; + ELSE IF @EntityType = N'ProjectRestorePoint' + SELECT @ProjectID = ProjectID FROM dbo.ProjectRestorePoints WHERE ProjectRestorePointID = @EntityID; + ELSE IF @EntityType = N'Scenario' + SELECT @ProjectID = ProjectID FROM dbo.Scenarios WHERE ScenarioID = @EntityID; + ELSE IF @EntityType = N'ThreadEvent' + SELECT @ProjectID = pl.ProjectID FROM dbo.ThreadEvents te INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID WHERE te.ThreadEventID = @EntityID; + ELSE IF @EntityType = N'AssetEvent' + SELECT @ProjectID = sa.ProjectID FROM dbo.AssetEvents ae INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID WHERE ae.AssetEventID = @EntityID; + ELSE IF @EntityType = N'SceneAssetLocation' + SELECT @ProjectID = sa.ProjectID FROM dbo.SceneAssetLocations sal INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = sal.StoryAssetID WHERE sal.SceneAssetLocationID = @EntityID; + ELSE IF @EntityType = N'AssetCustodyEvent' + SELECT @ProjectID = sa.ProjectID FROM dbo.AssetCustodyEvents ace INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID WHERE ace.AssetCustodyEventID = @EntityID; + ELSE IF @EntityType = N'SceneCharacter' + SELECT @ProjectID = ch.ProjectID FROM dbo.SceneCharacters sc INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID WHERE sc.SceneCharacterID = @EntityID; + ELSE IF @EntityType = N'SceneDependency' + SELECT @ProjectID = ProjectID FROM dbo.SceneDependencies WHERE SceneDependencyID = @EntityID; + ELSE IF @EntityType = N'CharacterKnowledge' + SELECT @ProjectID = c.ProjectID FROM dbo.CharacterKnowledge ck INNER JOIN dbo.Characters c ON c.CharacterID = ck.CharacterID WHERE ck.CharacterKnowledgeID = @EntityID; + ELSE IF @EntityType = N'RelationshipEvent' + SELECT @ProjectID = cr.ProjectID FROM dbo.RelationshipEvents re INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID WHERE re.RelationshipEventID = @EntityID; + ELSE IF @EntityType = N'SceneNote' + SELECT @ProjectID = b.ProjectID FROM dbo.SceneNotes sn INNER JOIN dbo.Scenes s ON s.SceneID = sn.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE sn.SceneNoteID = @EntityID; + ELSE IF @EntityType = N'SceneChecklistItem' + SELECT @ProjectID = b.ProjectID FROM dbo.SceneChecklistItems sci INNER JOIN dbo.Scenes s ON s.SceneID = sci.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE sci.SceneChecklistItemID = @EntityID; + ELSE IF @EntityType = N'SceneAttachment' + SELECT @ProjectID = b.ProjectID FROM dbo.SceneAttachments sa INNER JOIN dbo.Scenes s ON s.SceneID = sa.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE sa.SceneAttachmentID = @EntityID; + + SELECT CONVERT(bit, CASE WHEN EXISTS + ( + SELECT 1 + FROM dbo.ProjectUserAccess + WHERE ProjectID = @ProjectID + AND UserID = @UserID + AND IsActive = 1 + ) THEN 1 ELSE 0 END); +END; +GO diff --git a/PlotLine/ViewModels/CoreViewModels.cs b/PlotLine/ViewModels/CoreViewModels.cs index 3ae8756..d8e74aa 100644 --- a/PlotLine/ViewModels/CoreViewModels.cs +++ b/PlotLine/ViewModels/CoreViewModels.cs @@ -1816,6 +1816,86 @@ public sealed class LocationRelationshipEditViewModel public string? Notes { get; set; } } +public sealed class FloorPlanListViewModel +{ + public Project Project { get; set; } = new(); + public IReadOnlyList FloorPlans { get; set; } = []; + public FloorPlanEditViewModel NewFloorPlan { get; set; } = new(); +} + +public sealed class FloorPlanEditViewModel +{ + public int FloorPlanID { get; set; } + public int ProjectID { get; set; } + + [Required, StringLength(200)] + public string Name { get; set; } = string.Empty; + + public string? Description { get; set; } +} + +public sealed class FloorPlanEditorViewModel +{ + public Project Project { get; set; } = new(); + public FloorPlanEditViewModel FloorPlan { get; set; } = new(); + public List Floors { get; set; } = []; + public List Blocks { get; set; } = []; + public FloorPlanFloorEditViewModel NewFloor { get; set; } = new(); + public FloorPlanBlockEditViewModel NewBlock { get; set; } = new(); + public IReadOnlyList LocationOptions { get; set; } = []; + public IReadOnlyList BlockTypeOptions { get; set; } = []; +} + +public sealed class FloorPlanFloorEditViewModel +{ + public int FloorPlanFloorID { get; set; } + public int FloorPlanID { get; set; } + + [Required, StringLength(100)] + public string Name { get; set; } = string.Empty; + + public int SortOrder { get; set; } + + [Range(8, 80)] + public int GridWidth { get; set; } = 24; + + [Range(8, 80)] + public int GridHeight { get; set; } = 16; +} + +public sealed class FloorPlanBlockEditViewModel +{ + public int FloorPlanBlockID { get; set; } + public int FloorPlanFloorID { get; set; } + + [Range(1, int.MaxValue, ErrorMessage = "Choose a location.")] + public int LocationID { get; set; } + + public string LocationName { get; set; } = string.Empty; + public string LocationPath { get; set; } = string.Empty; + + [Range(0, 79)] + public int X { get; set; } + + [Range(0, 79)] + public int Y { get; set; } + + [Range(1, 80)] + public int WidthCells { get; set; } = 2; + + [Range(1, 80)] + public int HeightCells { get; set; } = 1; + + [Required, StringLength(40)] + public string BlockType { get; set; } = FloorPlanBlockTypes.Room; + + [StringLength(200)] + public string? LabelOverride { get; set; } + + public string? Notes { get; set; } + public string DisplayLabel => string.IsNullOrWhiteSpace(LabelOverride) ? LocationName : LabelOverride; +} + public sealed class WarningDashboardViewModel { public Project Project { get; set; } = new(); diff --git a/PlotLine/Views/FloorPlans/Edit.cshtml b/PlotLine/Views/FloorPlans/Edit.cshtml new file mode 100644 index 0000000..7952bd7 --- /dev/null +++ b/PlotLine/Views/FloorPlans/Edit.cshtml @@ -0,0 +1,457 @@ +@model FloorPlanEditorViewModel +@{ + ViewData["Title"] = Model.FloorPlan.FloorPlanID == 0 ? "New Floor Plan" : Model.FloorPlan.Name; + ViewData["ProjectSection"] = "Floor Plans"; + var activeFloor = Model.Floors.OrderBy(x => x.SortOrder).ThenBy(x => x.Name).FirstOrDefault(); + var blocksByFloor = Model.Blocks.GroupBy(x => x.FloorPlanFloorID).ToDictionary(x => x.Key, x => x.ToList()); + string BlockClass(string blockType) => $"floor-plan-block floor-plan-block--{blockType.ToLowerInvariant()}"; +} + + + + + +
+
+

Location floor plan

+

@Model.FloorPlan.Name

+
+ Back to floor plans +
+ +@if (TempData["FloorPlanMessage"] is string message) +{ +
@message
+} +@if (TempData["FloorPlanError"] is string error) +{ +
@error
+} + +
+
+ + +
+ + + +
+
+ + +
+
+ +
+
+
+ +
+
+ + +
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + @if (Model.Floors.Any() && Model.LocationOptions.Any()) + { +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ } +
+ +@if (!Model.Floors.Any()) +{ +
+

No floors yet

+

Add a floor to start arranging linked locations.

+
+} +else +{ +
+ + + + + + +
+ @foreach (var floor in Model.Floors.OrderBy(x => x.SortOrder).ThenBy(x => x.Name)) + { + var isActive = floor.FloorPlanFloorID == activeFloor?.FloorPlanFloorID; + + } +
+ +
+
+ @for (var i = 0; i < Model.Floors.Count; i++) + { + var floor = Model.Floors[i]; + var isActive = floor.FloorPlanFloorID == activeFloor?.FloorPlanFloorID; + var floorBlocks = blocksByFloor.GetValueOrDefault(floor.FloorPlanFloorID) ?? []; +
+
+
+ + + + + +
+
+ + +
+
+ + +
+ +
+ +
+
+ @foreach (var block in floorBlocks) + { + var blockIndex = Model.Blocks.FindIndex(x => x.FloorPlanBlockID == block.FloorPlanBlockID); + + } +
+
+
+ } +
+ + +
+ +
+ +
+
+} + +@section Scripts { + + +} diff --git a/PlotLine/Views/FloorPlans/Index.cshtml b/PlotLine/Views/FloorPlans/Index.cshtml new file mode 100644 index 0000000..977da99 --- /dev/null +++ b/PlotLine/Views/FloorPlans/Index.cshtml @@ -0,0 +1,79 @@ +@model FloorPlanListViewModel +@{ + ViewData["Title"] = "Floor Plans"; + ViewData["ProjectSection"] = "Floor Plans"; +} + + + + + +
+
+

Location plans

+

@Model.Project.ProjectName

+
+
+ +@if (TempData["FloorPlanMessage"] is string message) +{ +
@message
+} + +
+

Create floor plan

+
+ +
+ + +
+
+ + +
+
+ +
+
+
+ +@if (!Model.FloorPlans.Any()) +{ +
+

No floor plans yet

+

Create a plan, then arrange existing locations as rooms, corridors, halls, gardens, or other spaces.

+
+} +else +{ +
+
+ @foreach (var floorPlan in Model.FloorPlans) + { +
+
+

@floorPlan.Name

+ @if (!string.IsNullOrWhiteSpace(floorPlan.Description)) + { +

@floorPlan.Description

+ } +

@floorPlan.FloorCount floor(s), @floorPlan.BlockCount placed block(s)

+
+
+ Open editor +
+ + + +
+
+
+ } +
+
+} diff --git a/PlotLine/Views/Shared/_ProjectSectionNav.cshtml b/PlotLine/Views/Shared/_ProjectSectionNav.cshtml index 72b5a05..03224ba 100644 --- a/PlotLine/Views/Shared/_ProjectSectionNav.cshtml +++ b/PlotLine/Views/Shared/_ProjectSectionNav.cshtml @@ -16,6 +16,7 @@ Relationships Assets Locations + Floor Plans Threads Metrics Scenarios diff --git a/PlotLine/wwwroot/css/site.css b/PlotLine/wwwroot/css/site.css index 10a72a3..42e8f6d 100644 --- a/PlotLine/wwwroot/css/site.css +++ b/PlotLine/wwwroot/css/site.css @@ -4449,3 +4449,234 @@ body.dragging-location [data-drag-type="location"] { border-right: 0; } } + +.floor-plan-list { + display: grid; + gap: 12px; +} + +.floor-plan-list-item { + align-items: center; + border: 1px solid var(--border-color, #d8ded9); + border-radius: 8px; + display: flex; + justify-content: space-between; + gap: 16px; + padding: 14px; +} + +.floor-plan-list-item h2 { + font-size: 1.05rem; + margin: 0 0 4px; +} + +.floor-plan-list-item p { + margin: 0; +} + +.floor-plan-tools { + display: grid; + gap: 14px; +} + +.floor-plan-tabs { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 12px; +} + +.floor-plan-tab { + background: var(--panel-bg, #fff); + border: 1px solid var(--border-color, #d8ded9); + border-radius: 999px; + color: inherit; + padding: 6px 12px; +} + +.floor-plan-tab.active { + background: var(--bs-primary, #2f6f63); + border-color: var(--bs-primary, #2f6f63); + color: #fff; +} + +.floor-plan-layout { + display: grid; + gap: 16px; + grid-template-columns: minmax(0, 1fr) 320px; +} + +.floor-plan-floor { + display: none; +} + +.floor-plan-floor.active { + display: block; +} + +.floor-plan-floor-header { + align-items: end; + display: grid; + gap: 10px; + grid-template-columns: minmax(180px, 1fr) 120px 120px auto; + margin-bottom: 10px; +} + +.floor-plan-grid-wrap { + overflow: auto; + padding-bottom: 8px; +} + +.floor-plan-grid { + --cell-size: 34px; + background-color: var(--panel-bg, #fff); + background-image: + linear-gradient(to right, rgba(93, 111, 101, .18) 1px, transparent 1px), + linear-gradient(to bottom, rgba(93, 111, 101, .18) 1px, transparent 1px); + background-size: var(--cell-size) var(--cell-size); + border: 1px solid var(--border-color, #d8ded9); + border-radius: 8px; + height: calc(var(--grid-height) * var(--cell-size)); + min-width: calc(var(--grid-width) * var(--cell-size)); + position: relative; + width: calc(var(--grid-width) * var(--cell-size)); +} + +.floor-plan-block { + align-items: center; + border: 2px solid rgba(44, 84, 75, .55); + border-radius: 6px; + color: #12332c; + cursor: grab; + display: flex; + font-weight: 650; + height: calc(var(--h) * var(--cell-size)); + justify-content: center; + left: calc(var(--x) * var(--cell-size)); + line-height: 1.2; + overflow: hidden; + padding: 6px; + position: absolute; + text-align: center; + top: calc(var(--y) * var(--cell-size)); + touch-action: none; + width: calc(var(--w) * var(--cell-size)); +} + +.floor-plan-block span:first-child { + overflow-wrap: anywhere; +} + +.floor-plan-block.selected { + box-shadow: 0 0 0 3px rgba(47, 111, 99, .28); + outline: 2px solid var(--bs-primary, #2f6f63); + z-index: 3; +} + +.floor-plan-block.overlap { + border-color: #b45544; + box-shadow: inset 0 0 0 2px rgba(180, 85, 68, .18); +} + +.floor-plan-block--room { + background: #d9eee7; +} + +.floor-plan-block--corridor { + background: #e7e2f4; +} + +.floor-plan-block--openspace { + background: #e5efd4; +} + +.floor-plan-block--exterior { + background: #d9ecf3; +} + +.floor-plan-block--other { + background: #ece9e1; +} + +.floor-plan-resize-handle { + border-bottom: 10px solid rgba(18, 51, 44, .45); + border-left: 10px solid transparent; + bottom: 4px; + height: 0; + position: absolute; + right: 4px; + width: 0; +} + +.floor-plan-side-panel { + align-self: start; + background: var(--panel-bg, #fff); + border: 1px solid var(--border-color, #d8ded9); + border-radius: 8px; + padding: 14px; + position: sticky; + top: 12px; +} + +.floor-plan-side-panel h2 { + font-size: 1.05rem; + margin-bottom: 8px; +} + +.floor-plan-overlap-warning { + background: rgba(180, 85, 68, .12); + border: 1px solid rgba(180, 85, 68, .35); + border-radius: 6px; + color: #8f3f31; + margin-bottom: 10px; + padding: 8px 10px; +} + +[data-bs-theme="dark"] .floor-plan-grid, +.dark .floor-plan-grid { + background-color: #15201d; + background-image: + linear-gradient(to right, rgba(214, 224, 218, .12) 1px, transparent 1px), + linear-gradient(to bottom, rgba(214, 224, 218, .12) 1px, transparent 1px); +} + +[data-bs-theme="dark"] .floor-plan-block, +.dark .floor-plan-block { + color: #f1f6f3; +} + +[data-bs-theme="dark"] .floor-plan-block--room, +.dark .floor-plan-block--room { + background: #29584f; +} + +[data-bs-theme="dark"] .floor-plan-block--corridor, +.dark .floor-plan-block--corridor { + background: #514876; +} + +[data-bs-theme="dark"] .floor-plan-block--openspace, +.dark .floor-plan-block--openspace { + background: #4f6133; +} + +[data-bs-theme="dark"] .floor-plan-block--exterior, +.dark .floor-plan-block--exterior { + background: #315d6f; +} + +[data-bs-theme="dark"] .floor-plan-block--other, +.dark .floor-plan-block--other { + background: #5b564c; +} + +@media (max-width: 1100px) { + .floor-plan-layout, + .floor-plan-floor-header { + grid-template-columns: 1fr; + } + + .floor-plan-side-panel { + position: static; + } +} diff --git a/PlotLine/wwwroot/css/site.min.css b/PlotLine/wwwroot/css/site.min.css index 97e989b..6eacae6 100644 --- a/PlotLine/wwwroot/css/site.min.css +++ b/PlotLine/wwwroot/css/site.min.css @@ -1 +1 @@ -html{font-size:15px;min-height:100%}@media (min-width:768px){html{font-size:16px}}:root{--plotline-ink:#1e252b;--plotline-muted:#66717b;--plotline-paper:#fbfaf7;--plotline-panel:#ffffff;--plotline-line:#d8ddd8;--plotline-accent:#2f6f63;--plotline-accent-dark:#22534a;--plotline-soft:#eef5f2;--timeline-label-width:180px;--timeline-scene-width:220px;--timeline-grid-gap:10px}body{min-height:100%;margin-bottom:0;color:var(--plotline-ink);background:var(--plotline-paper)}a{color:var(--plotline-accent-dark)}.btn-primary{--bs-btn-bg:var(--plotline-accent);--bs-btn-border-color:var(--plotline-accent);--bs-btn-hover-bg:var(--plotline-accent-dark);--bs-btn-hover-border-color:var(--plotline-accent-dark)}.btn-outline-primary{--bs-btn-color:var(--plotline-accent-dark);--bs-btn-border-color:var(--plotline-accent);--bs-btn-hover-bg:var(--plotline-accent);--bs-btn-hover-border-color:var(--plotline-accent)}.plotline-nav{background:rgba(255,255,255,.94);backdrop-filter:blur(8px)}.plotline-shell{max-width:1480px;padding-left:24px;padding-right:24px}.plotline-shell-wide{max-width:none;padding-left:clamp(18px,2vw,32px);padding-right:clamp(18px,2vw,32px)}.page-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:24px;margin:18px 0 24px}.workspace-heading{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;margin:14px 0 18px}.workspace-heading h1{margin:0;font-size:clamp(1.8rem, 3vw, 2.7rem);line-height:1.05}.page-heading.compact{max-width:980px}.page-heading h1{margin:0;font-size:clamp(2rem, 4vw, 3.2rem);line-height:1.05;letter-spacing:0}.eyebrow{margin:0 0 6px;color:var(--plotline-accent-dark);font-size:.78rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.lead-text{max-width:760px;margin:10px 0 0;color:var(--plotline-muted);font-size:1.05rem}.button-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.asset-card-footer form,.button-row form,.compact-buttons form,form[data-confirm-message],td.text-end form{display:inline-flex;align-items:center;margin:0}td.text-end{white-space:nowrap}.content-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}.edit-panel,.empty-panel,.list-section,.plain-card{background:var(--plotline-panel);border:1px solid var(--plotline-line);border-radius:8px;padding:20px;box-shadow:0 10px 24px rgba(30,37,43,.04)}.empty-panel h2,.list-section h2,.plain-card h2{margin-top:0;font-size:1.35rem}.empty-panel p,.muted,.plain-card p{color:var(--plotline-muted)}.edit-panel{max-width:980px}.trial-dashboard-banner,.trial-status-panel{border:1px solid var(--plotline-line);border-radius:8px;padding:18px 20px;background:var(--plotline-panel)}.trial-status-panel{display:grid;gap:12px;margin-bottom:18px}.trial-status-panel--signup{grid-template-columns:minmax(0,1fr) auto;align-items:center;max-width:980px}.trial-dashboard-banner h2,.trial-status-panel h2,.trial-status-panel h3{margin:0}.trial-dashboard-banner p,.trial-status-panel p{margin-bottom:0}.trial-dashboard-banner,.trial-status-panel__heading{display:flex;gap:16px;align-items:center;justify-content:space-between}.trial-dashboard-banner{margin-bottom:18px}.trial-dashboard-banner__body{min-width:0}.trial-dashboard-banner__actions{display:flex;flex:0 0 auto;flex-wrap:wrap;gap:8px}.trial-progress{height:10px;overflow:hidden;border-radius:999px;background:rgba(84,68,48,.12)}.trial-progress__bar{height:100%;border-radius:inherit}.trial-dashboard-banner[hidden],.trial-status-panel[hidden]{display:none}.location-page .drag-drop-panel,.location-page .edit-panel,.location-page .empty-panel,.location-page .page-heading.compact{max-width:none}.import-panel,.import-preview-section{max-width:none}.import-json-input{font-family:Consolas,"Liberation Mono",Menlo,monospace;font-size:.88rem;line-height:1.45;min-height:34rem;max-height:64vh;overflow:auto;resize:vertical;white-space:pre}.import-counts,.import-preview-heading{display:flex;align-items:flex-start;gap:12px}.import-preview-heading{justify-content:space-between;margin-bottom:16px}.import-counts{flex-wrap:wrap}.import-counts span{display:inline-flex;align-items:baseline;gap:5px;border:1px solid var(--plotline-line);border-radius:999px;padding:5px 11px;background:var(--plotline-soft);color:var(--plotline-muted);font-weight:700}.import-counts strong{color:var(--plotline-ink)}.import-tree{display:grid;gap:12px}.import-tree-book{border:1px solid var(--plotline-line);border-radius:8px;padding:14px;background:rgba(255,255,255,.54)}.import-tree-book>summary,.import-tree-chapter>summary{display:flex;align-items:center;justify-content:space-between;gap:12px;cursor:pointer}.import-tree-book>summary span,.import-tree-chapter>summary span{color:var(--plotline-muted);font-size:.84rem;font-weight:700;white-space:nowrap}.import-tree-book h3{margin:0 0 10px;font-size:1.08rem}.import-tree-chapter{border-top:1px solid var(--plotline-line);padding:10px 0 0;margin-top:10px}.import-tree-chapter ul{margin:8px 0 0;color:var(--plotline-muted)}.import-scene-meta{display:block;margin-top:2px;color:var(--plotline-muted);font-size:.82rem}.import-mapping-summary{display:grid;gap:6px;border:1px solid rgba(184,118,43,.28);border-radius:8px;padding:10px 12px;margin-bottom:14px;background:rgba(251,236,211,.5);color:var(--plotline-muted);font-size:.9rem}.import-duplicate-confirm{border:1px solid rgba(184,118,43,.34);border-radius:8px;padding:12px 12px 12px 38px;background:rgba(251,236,211,.72)}.import-confirmation,.import-docs details{border:1px solid var(--plotline-line);border-radius:8px;padding:16px;background:rgba(255,255,255,.42)}.import-confirmation h3,.import-doc-grid h3{margin-top:0;font-size:1.05rem}.import-docs summary{cursor:pointer;font-weight:800}.import-doc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;margin-top:14px}.import-message-list ul{max-height:16rem;overflow:auto}.scene-editor{max-width:1180px}.table{margin-bottom:0}.table th{color:var(--plotline-muted);font-size:.78rem;letter-spacing:.06em;text-transform:uppercase}.narrow{width:90px;white-space:nowrap}.status-pill{display:inline-flex;align-items:center;width:fit-content;border:1px solid #c8d8d2;border-radius:999px;padding:2px 8px;color:var(--plotline-accent-dark);background:var(--plotline-soft);font-size:.78rem;font-weight:700;white-space:nowrap}.dashboard-summary-card{border-color:var(--plotline-line)}.dashboard-summary-body{padding:10px 12px}.dashboard-summary-label{margin-bottom:4px;font-size:.7rem;line-height:1.15}.dashboard-summary-value{display:block;font-size:1.45rem;line-height:1}.dashboard-summary-calm{border-color:rgba(47,111,99,.28);background:rgba(238,245,242,.62)}.dashboard-summary-mild{border-color:rgba(197,139,43,.32);background:rgba(255,248,232,.68)}.dashboard-summary-attention{border-color:rgba(142,80,103,.32);background:rgba(252,241,246,.7)}.dashboard-progress-card{border-color:rgba(47,111,99,.18);background:linear-gradient(180deg,#fff 0,rgba(238,245,242,.64) 100%)}.dashboard-progress-body{padding:12px 14px}.dashboard-progress-heading{margin-bottom:8px}.dashboard-progress-title{margin:0 0 2px;font-size:1.05rem;line-height:1.2}.dashboard-series-progress{height:7px;background:rgba(47,111,99,.1)}.dashboard-series-progress .progress-bar{background:linear-gradient(90deg,var(--plotline-accent),#6fa99b)}.dashboard-progress-source{border-color:rgba(47,111,99,.2);color:var(--plotline-accent-dark);background:rgba(238,245,242,.9)}.dashboard-book-title-block h3{line-height:1.16}.dashboard-book-title-block a{color:var(--plotline-ink);text-decoration-color:rgba(47,111,99,0.35)}.dashboard-book-subtitle{color:var(--plotline-muted);font-size:.95rem;line-height:1.25}.dashboard-status-pill{border-color:var(--dashboard-status-border,#c8d8d2);color:var(--dashboard-status-ink,var(--plotline-accent-dark));background:var(--dashboard-status-bg,var(--plotline-soft))}.dashboard-status-published{--dashboard-status-border:rgba(47, 111, 99, 0.32);--dashboard-status-bg:#e7f2ed;--dashboard-status-ink:#22534a}.dashboard-status-drafting{--dashboard-status-border:rgba(184, 118, 43, 0.35);--dashboard-status-bg:#fff2dd;--dashboard-status-ink:#7b4b12}.dashboard-status-planning{--dashboard-status-border:rgba(51, 117, 155, 0.3);--dashboard-status-bg:#e9f3f8;--dashboard-status-ink:#245b78}.dashboard-status-outlining,.dashboard-status-research{--dashboard-status-border:rgba(103, 80, 164, 0.26);--dashboard-status-bg:#f0edf8;--dashboard-status-ink:#51417c}.dashboard-status-revising{--dashboard-status-border:rgba(123, 82, 148, 0.3);--dashboard-status-bg:#f4eefa;--dashboard-status-ink:#5f3e73}.dashboard-status-editing{--dashboard-status-border:rgba(68, 75, 130, 0.3);--dashboard-status-bg:#eceef8;--dashboard-status-ink:#343b72}.dashboard-status-proofreading{--dashboard-status-border:rgba(43, 130, 123, 0.3);--dashboard-status-bg:#e6f5f2;--dashboard-status-ink:#23625d}.dashboard-status-ready{--dashboard-status-border:rgba(185, 142, 30, 0.34);--dashboard-status-bg:#fff6d8;--dashboard-status-ink:#765a0f}.dashboard-status-paused{--dashboard-status-border:rgba(102, 113, 123, 0.28);--dashboard-status-bg:#f0f2f1;--dashboard-status-ink:#56616b}.dashboard-status-archived{--dashboard-status-border:rgba(139, 80, 86, 0.28);--dashboard-status-bg:#f3eded;--dashboard-status-ink:#76474d}.dashboard-attention-soft{border-color:rgba(102,113,123,.24);color:#56616b;background:#f3f4f2}.order-buttons{display:flex;gap:6px}.purpose-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:8px;max-height:260px;overflow:auto;padding:10px;border:1px solid var(--plotline-line);border-radius:8px;background:#fcfcfb}.purpose-check{display:flex;align-items:center;gap:8px;font-size:.92rem}.timeline-filter{display:flex;align-items:center;gap:8px;min-width:min(420px,100%)}.workspace-layout{display:grid;grid-template-columns:minmax(0,1fr);gap:0;align-items:start;--timeline-inspector-width:min(40vw, 560px)}.workspace-layout.inspector-drawer-open{grid-template-columns:minmax(420px,1fr) 10px minmax(420px,var(--timeline-inspector-width));gap:12px}.timeline-workspace{min-width:0;max-width:100%}.timeline-inspector-resizer{position:sticky;top:76px;z-index:25;width:10px;height:calc(100vh - 96px);border-radius:999px;cursor:col-resize;background:linear-gradient(180deg,rgba(47,111,99,.16),rgba(47,111,99,.04))}.timeline-inspector-resizer::before{content:"";display:block;width:2px;height:48px;margin:18px auto;border-radius:999px;background:var(--plotline-accent);opacity:.55}.timeline-inspector-resizer:focus-visible,.timeline-inspector-resizer:hover{background:rgba(47,111,99,.16)}body.timeline-inspector-resizing{cursor:col-resize;user-select:none}.pacing-panel{margin-top:14px;margin-bottom:0;border:1px solid var(--plotline-line);border-radius:8px;padding:14px;background:#fff;box-shadow:0 10px 24px rgba(30,37,43,.04)}.pacing-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:10px}.pacing-heading h2{margin:0;font-size:1.1rem}.metric-graph-stack{display:grid;gap:10px;max-width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;padding-bottom:4px}.metric-graph-row{display:grid;grid-template-columns:150px minmax(260px,1fr);gap:10px;align-items:center;min-width:max(100%,calc(150px + (var(--scene-count,1) * 34px)))}.metric-graph-label{color:var(--plotline-muted);font-size:.84rem;font-weight:700}.metric-graph-points{display:grid;grid-template-columns:repeat(var(--scene-count),minmax(28px,1fr));gap:5px;align-items:end;min-height:52px;border-radius:8px;padding:6px;background:linear-gradient(180deg,#f6faf8 0,#edf4f1 100%);min-width:calc(var(--scene-count) * 34px)}.metric-point{display:flex;align-items:flex-end;justify-content:center;height:48px;min-width:28px;color:var(--plotline-accent-dark);text-decoration:none}.metric-point::before{content:"";display:block;width:100%;height:max(8px,var(--metric-height));border-radius:5px 5px 2px 2px;background:linear-gradient(180deg,#6fa99b,var(--plotline-accent-dark));box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}.metric-point span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}.timeline-shell{overflow-x:auto;padding:16px;border:1px solid var(--plotline-line);border-radius:8px;background:linear-gradient(180deg,#fff 0,#f7faf8 100%)}.workspace-timeline{min-height:420px}.timeline-series-row{display:grid;grid-template-columns:var(--timeline-label-width) max-content;gap:var(--timeline-grid-gap);align-items:stretch;width:max-content;min-width:100%}.timeline-project-title{position:sticky;left:0;z-index:20;display:grid;align-content:start;gap:4px;width:var(--timeline-label-width);min-height:100%;border-radius:8px;padding:12px;color:#fff;background:var(--plotline-accent-dark);font-weight:700;box-shadow:8px 0 18px rgba(30,37,43,.08)}.timeline-book-title span,.timeline-project-title span{color:rgba(255,255,255,.78);font-size:.72rem;letter-spacing:.08em;text-transform:uppercase}.timeline-book-title strong,.timeline-project-title strong{line-height:1.15}.timeline-books{display:flex;gap:var(--timeline-grid-gap);align-items:stretch}.timeline-book{flex:0 0 auto;width:calc((var(--book-scene-count) * var(--timeline-scene-width)) + ((var(--book-scene-count) - 1) * var(--timeline-grid-gap)));min-width:var(--timeline-scene-width)}.timeline-book-title{display:grid;gap:2px;margin-bottom:var(--timeline-grid-gap);border-radius:8px;padding:10px 12px;color:#fff;background:var(--plotline-accent-dark);font-weight:700}.timeline-chapters{display:flex;gap:var(--timeline-grid-gap);align-items:flex-start}.timeline-chapter{flex:0 0 auto;width:calc((var(--chapter-scene-count) * var(--timeline-scene-width)) + ((var(--chapter-scene-count) - 1) * var(--timeline-grid-gap)));min-width:var(--timeline-scene-width);box-sizing:border-box;border-radius:8px;background:rgba(238,245,242,.7);outline:1px solid #d4ded8;outline-offset:0;overflow:visible}.timeline-chapter-title{display:grid;gap:2px;border-bottom:1px solid #d4ded8;padding:10px 12px;color:var(--plotline-accent-dark);font-weight:700}.timeline-chapter-title span{color:var(--plotline-muted);font-size:.76rem;text-transform:uppercase}.timeline-scenes{display:grid;grid-template-columns:repeat(var(--chapter-scene-count),minmax(0,1fr));gap:var(--timeline-grid-gap);width:100%;box-sizing:border-box;min-height:190px;padding:0}.timeline-empty-scene,.timeline-scene-card{display:flex;flex-direction:column;gap:6px;width:100%;min-width:0;box-sizing:border-box;border:1px solid var(--plotline-line);border-radius:8px;padding:12px;background:#fff;color:var(--plotline-ink);text-decoration:none;box-shadow:0 8px 18px rgba(30,37,43,.05)}.timeline-scene-card:hover{border-color:var(--plotline-accent);color:var(--plotline-ink)}.metric-point-placeholder,.timeline-placeholder-cell,.timeline-placeholder-scene{cursor:default;opacity:.72}.timeline-placeholder-scene{align-items:center;justify-content:center;border-style:dashed;color:var(--plotline-muted);background:rgba(255,255,255,.38);font-size:.86rem;font-weight:800;text-align:center}.timeline-placeholder-cell{border-style:dashed;background:repeating-linear-gradient(-45deg,rgba(216,221,216,.18) 0,rgba(216,221,216,.18) 7px,rgba(255,255,255,.18) 7px,rgba(255,255,255,.18) 14px)}.metric-point-placeholder::before{height:4px;background:rgba(102,113,123,.2);box-shadow:none}.timeline-scene-card.selected{border-color:var(--plotline-accent-dark);box-shadow:0 0 0 3px rgba(47,111,99,.16),0 10px 22px rgba(30,37,43,.08)}.purpose-chip-row,.scene-badge-row{display:flex;flex-wrap:wrap;gap:5px;align-items:center}.warning-placeholder{display:inline-flex;width:fit-content;border:1px dashed #d6b25e;border-radius:999px;padding:2px 8px;color:#755715;background:#fff8e8;font-size:.74rem;font-weight:700}.warning-placeholder.active{border-color:rgba(159,69,69,.36);color:#8a3434;background:rgba(255,232,232,.9);gap:5px;font-weight:800}.dependency-placeholder{display:inline-flex;width:fit-content;border:1px solid rgba(47,111,99,.2);border-radius:999px;padding:2px 8px;color:var(--plotline-accent-dark);background:rgba(47,111,99,.08);font-size:.74rem;font-weight:700}.purpose-chip{display:inline-flex;width:fit-content;border-radius:999px;padding:2px 7px;color:#38453e;background:#edf0eb;font-size:.72rem;font-weight:700}.future-lane-placeholder{border-top:1px dashed #cbd8d2;padding:8px 12px;color:var(--plotline-muted);font-size:.78rem;background:rgba(255,255,255,.45)}.metric-timeline-board{display:grid;gap:8px;width:max-content;min-width:100%;margin-top:14px;border-top:1px solid var(--plotline-line);padding-top:14px}.metric-shape-controls{position:sticky;left:0;z-index:6;display:grid;gap:8px;width:calc(var(--timeline-label-width) + (var(--scene-count) * var(--timeline-scene-width)) + (var(--scene-count) * var(--timeline-grid-gap)));min-width:100%;border-radius:8px;padding:10px;background:rgba(255,255,255,.88)}.metric-timeline-row{display:grid;grid-template-columns:var(--timeline-label-width) calc((var(--scene-count) * var(--timeline-scene-width)) + ((var(--scene-count) - 1) * var(--timeline-grid-gap)));gap:var(--timeline-grid-gap);align-items:stretch}.metric-lane-label{position:sticky;left:0;z-index:12;display:grid;align-content:start;gap:4px;min-height:320px;border:1px solid rgba(47,111,99,.18);border-radius:8px;padding:10px;color:var(--plotline-accent-dark);background:#fff;box-shadow:0 6px 14px rgba(30,37,43,.04)}.metric-lane-label span{color:var(--plotline-muted);font-size:.78rem;font-weight:600}.colour-swatch{display:inline-block;width:18px;height:18px;border:1px solid rgba(30,37,43,.18);border-radius:999px;vertical-align:middle}.plot-lane-board{position:relative;display:grid;gap:8px;width:max-content;min-width:100%;margin-top:14px;border-top:1px solid var(--plotline-line);padding-top:14px}.plot-lane-svg-overlay{position:absolute;inset:0 auto auto 0;z-index:4;pointer-events:none;overflow:visible}.plot-lane-svg-health-overlay{z-index:8}.plot-line-svg-path{fill:none;stroke-width:3.5;stroke-linecap:round;opacity:.72}.plot-line-svg-segment-dormant{stroke-dasharray:9 8;opacity:.48}.plot-line-svg-connector{fill:none;stroke-width:3;stroke-linecap:round;opacity:.58}.plot-line-svg-path.plotline-state-questionable,.plot-line-svg-path.plotline-state-resolved{stroke:#8c9491;opacity:.42}.plot-line-svg-path.plotline-state-abandoned{stroke:#9b2f36;opacity:.5}.plot-event-svg-node.plotline-state-questionable,.plot-event-svg-node.plotline-state-resolved{opacity:.88}.plot-event-svg-node.plotline-state-abandoned{opacity:.9}.plot-event-svg-node.plotline-state-abandoned line{stroke:#ffffff}.plotline-health-marker circle,.plotline-health-marker path{fill:#f4c430;stroke:#8a6712;stroke-width:1.5}.plotline-health-marker{pointer-events:visiblePainted}.plotline-health-marker line,.plotline-health-marker text{stroke:#5e4710;fill:#5e4710;stroke-width:1.6;stroke-linecap:round}.plotline-health-marker text{font-size:11px;font-weight:800;stroke-width:0;pointer-events:none}.plotline-health-count{fill:#ffffff;stroke:#8a6712;stroke-width:2;paint-order:stroke}.plotline-health-marker-dangling circle,.plotline-health-marker-dangling path{fill:#f8df8f}.plotline-health-marker-abandoned circle,.plotline-health-marker-abandoned path{fill:#d85c63;stroke:#8b1e2d}.plotline-health-marker-abandoned line{stroke:#ffffff}.plot-event-svg-node circle,.plot-event-svg-node ellipse,.plot-event-svg-node path{stroke:#ffffff;stroke-width:2}.plot-event-svg-node line,.plot-event-svg-node path[fill=none]{stroke:#8b1e2d;stroke-width:3.4;stroke-linecap:round;stroke-linejoin:round}.plot-event-svg-node-reveal ellipse{stroke-width:2.4}.plot-event-svg-label{font-size:9.5px;font-weight:900;font-family:system-ui,-apple-system,"Segoe UI",sans-serif;paint-order:stroke;stroke:rgba(30,37,43,0.22);stroke-width:.8px;pointer-events:none}.plot-event-svg-node-resolve path[fill=none]{stroke:#ffffff}.lane-board-heading,.metric-lane-board-heading,.plot-lane-board-heading{position:sticky;left:0;z-index:6;display:flex;align-items:center;justify-content:space-between;gap:14px;width:calc(var(--timeline-label-width) + (var(--scene-count) * var(--timeline-scene-width)) + (var(--scene-count) * var(--timeline-grid-gap)));min-width:100%;margin-bottom:2px;border-radius:8px;padding:8px 10px;color:var(--plotline-accent-dark);background:rgba(255,255,255,.92);font-weight:700;box-shadow:0 8px 16px rgba(30,37,43,.05)}.lane-board-heading small,.metric-lane-board-heading small,.plot-lane-board-heading small{color:var(--plotline-muted);font-weight:500;white-space:nowrap}.timeline-heading-with-help{display:inline-flex;align-items:center;gap:6px;min-width:0}.plot-lane-row{display:grid;grid-template-columns:var(--timeline-label-width) repeat(var(--scene-count),var(--timeline-scene-width));gap:var(--timeline-grid-gap);align-items:stretch}.plot-lane-label{position:sticky;left:0;z-index:12;display:flex;align-items:center;gap:8px;min-height:46px;border:1px solid color-mix(in srgb,var(--plot-colour),#fff 55%);border-left:5px solid var(--plot-colour);border-radius:8px;padding:8px 10px;background:#fff;color:var(--plotline-ink);text-decoration:none;box-shadow:0 6px 14px rgba(30,37,43,.04)}.plot-lane-label:hover{color:var(--plotline-ink);border-color:var(--plot-colour)}.plot-lane-slots{display:contents}.plot-lane-slot{display:flex;flex-wrap:wrap;align-content:center;align-items:center;justify-content:center;gap:5px;min-height:46px;border:1px dashed #d8ddd8;border-radius:8px;padding:7px;background:rgba(255,255,255,.62)}.thread-marker{position:relative;z-index:5;display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:34px;height:30px;border:2px solid color-mix(in srgb,var(--plot-colour),#000 20%);border-radius:999px;padding:0 8px;color:var(--plotline-ink);background:color-mix(in srgb,var(--plot-colour),#fff 66%);font-size:.74rem;font-weight:900;text-decoration:none;box-shadow:0 3px 8px rgba(30,37,43,.14)}.plot-lane-board [data-plot-event-node]{opacity:1}.thread-marker:focus-visible,.thread-marker:hover{color:var(--plotline-ink);background:color-mix(in srgb,var(--plot-colour),#fff 48%);outline:3px solid rgba(47,111,99,.22);outline-offset:2px}.thread-marker.resolved{border-color:#5a8c60;background:#e6f4e7;color:#24512b}.thread-marker.contradicted{border-color:#b45c5c;background:#fff0f0;color:#7a2222}.thread-events-section{margin-top:16px}.thread-event-list{display:grid;gap:8px;margin-bottom:12px}.thread-event-item{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;border:1px solid color-mix(in srgb,var(--plot-colour),#fff 55%);border-left:4px solid var(--plot-colour);border-radius:8px;padding:8px;background:#fff}.thread-event-item>div{display:grid;gap:3px}.thread-event-marker{display:inline-flex;align-items:center;justify-content:center;width:fit-content;min-width:24px;height:22px;border-radius:999px;padding:0 7px;background:color-mix(in srgb,var(--plot-colour),#fff 72%);font-size:.72rem;font-weight:800}.thread-event-create{border-top:1px solid var(--plotline-line);padding-top:12px}.asset-lane-board{display:grid;gap:8px;width:max-content;min-width:100%;margin-top:14px;border-top:1px solid var(--plotline-line);padding-top:14px}.asset-lane-row{display:grid;grid-template-columns:var(--timeline-label-width) repeat(var(--scene-count),var(--timeline-scene-width));gap:var(--timeline-grid-gap);align-items:stretch}.asset-lane-label{position:sticky;left:0;z-index:12;display:grid;gap:2px;min-height:46px;border:1px solid #d4d7df;border-left:5px solid #6b5b95;border-radius:8px;padding:8px 10px;background:#fff;color:var(--plotline-ink);text-decoration:none;box-shadow:0 6px 14px rgba(30,37,43,.04)}.asset-lane-label span{color:var(--plotline-muted);font-size:.75rem}.asset-lane-slots{display:contents}.asset-lane-slot{display:flex;flex-wrap:wrap;align-content:flex-start;gap:5px;min-height:46px;border:1px dashed #d8ddd8;border-radius:8px;padding:7px;background:rgba(250,249,255,.72)}.asset-event-marker,.asset-marker{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:24px;border:1px solid #7f70aa;border-radius:7px;padding:0 7px;color:#33284e;background:#eee9fb;font-size:.72rem;font-weight:800;text-decoration:none}.asset-marker:hover{color:#33284e;background:#dfd6f7}.asset-events-section{margin-top:16px}.asset-event-list,.asset-mini-list{display:grid;gap:8px;margin-bottom:12px}.asset-event-item,.asset-mini-item{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;border:1px solid #ded9ea;border-left:4px solid #6b5b95;border-radius:8px;padding:8px;background:#fff}.asset-event-item>div,.asset-mini-item>div{display:grid;gap:3px}.asset-create-form{border-top:1px solid var(--plotline-line);padding-top:12px}.asset-card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px}.asset-card{display:grid;gap:12px;border:1px solid var(--plotline-line);border-radius:8px;padding:14px;background:#fff;box-shadow:0 8px 18px rgba(30,37,43,.04)}.asset-card h2{margin:0;font-size:1.1rem}.asset-card-footer{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.asset-detail-grid{display:grid;grid-template-columns:minmax(260px,0.8fr) minmax(320px,1.2fr);gap:14px}.character-lane-board{display:grid;gap:8px;width:max-content;min-width:100%;margin-top:14px;border-top:1px solid var(--plotline-line);padding-top:14px}.character-lane-row{display:grid;grid-template-columns:var(--timeline-label-width) repeat(var(--scene-count),var(--timeline-scene-width));gap:var(--timeline-grid-gap);align-items:stretch}.character-lane-label{position:sticky;left:0;z-index:12;display:grid;gap:2px;min-height:46px;border:1px solid #d7d4ce;border-left:5px solid #8a6f3d;border-radius:8px;padding:8px 10px;background:#fff;color:var(--plotline-ink);text-decoration:none;box-shadow:0 6px 14px rgba(30,37,43,.04)}.character-lane-label span{color:var(--plotline-muted);font-size:.75rem}.character-lane-slots{display:contents}.character-lane-slot{display:flex;flex-wrap:wrap;align-content:flex-start;gap:5px;min-height:46px;border:1px dashed #d8ddd8;border-radius:8px;padding:7px;background:rgba(255,252,246,.72)}.character-marker{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:24px;border:1px solid #9a7a3f;border-radius:999px;padding:0 7px;color:#4f3810;background:#fff2d7;font-size:.72rem;font-weight:800;text-decoration:none}.character-card{border-left:4px solid #8a6f3d}.character-section{margin-top:16px}.character-item{border-left-color:#8a6f3d}.character-appearance-card{display:grid;gap:8px;border:1px solid #d7d4ce;border-left:4px solid #8a6f3d;border-radius:8px;padding:10px;background:#fff}.character-appearance-summary{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.character-appearance-summary>div:first-child{display:grid;gap:3px}.character-appearance-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}.character-appearance-edit .asset-create-form{margin-top:8px;border-top:1px solid var(--plotline-line)}.form-helper-message{margin:0 0 8px;font-size:.86rem;font-weight:700}.scene-number,.time-label{color:var(--plotline-muted);font-size:.82rem}.location-badge,.location-pill{display:inline-flex;align-items:center;width:fit-content;border:1px solid rgba(47,111,99,.18);border-radius:999px;padding:2px 7px;color:var(--plotline-accent-dark);background:rgba(47,111,99,.08);font-size:.74rem;font-weight:700}.scene-summary{display:-webkit-box;overflow:hidden;color:var(--plotline-muted);font-size:.88rem;-webkit-box-orient:vertical;-webkit-line-clamp:4}.timeline-empty-scene{justify-content:center;color:var(--plotline-muted);background:rgba(255,255,255,.58)}.form-check-input:focus,.form-control:focus,.form-select:focus{border-color:var(--plotline-accent);box-shadow:0 0 0 .2rem rgba(47,111,99,.16)}.scene-inspector-panel{position:sticky;top:76px;display:flex;flex-direction:column;max-height:calc(100vh - 96px);min-width:0;overflow:hidden;border:1px solid var(--plotline-line);border-radius:8px;background:#fff;box-shadow:0 14px 32px rgba(30,37,43,.08)}.inspector-header{position:sticky;top:0;z-index:5;display:flex;align-items:flex-start;justify-content:space-between;gap:12px;border-bottom:1px solid var(--plotline-line);padding:16px;background:#fff}.scene-inspector-body{flex:1 1 auto;min-height:0;overflow:auto;padding:16px}.inspector-header h2{margin:0;font-size:1.25rem}.scene-inspector-form{display:grid;gap:14px}.inspector-section{border-top:1px solid var(--plotline-line);padding-top:12px}.inspector-section:first-of-type{border-top:0;padding-top:0}.inspector-section h3{margin:0 0 10px;font-size:.95rem;color:var(--plotline-accent-dark)}.location-helper{border:1px solid rgba(138,111,61,.22);border-radius:8px;padding:10px;background:rgba(255,248,232,.74)}.location-warning-list{margin:0;padding-left:18px;color:#6f5526;font-size:.86rem}.location-tree{display:grid;gap:8px}.location-tree-row{display:flex;align-items:center;justify-content:space-between;gap:12px;border-bottom:1px solid var(--plotline-line);padding:10px 0 10px calc(var(--location-depth,0) * 22px)}.location-tree-row:last-child{border-bottom:0}.location-tree-row>div:first-child{display:grid;gap:3px}.compact-buttons{flex-wrap:nowrap}.location-chip-list{display:flex;flex-wrap:wrap;gap:8px}.location-chip{border:1px solid var(--plotline-line);border-radius:8px;padding:7px 10px;background:#fff;text-decoration:none}.location-relationship-list{display:grid;gap:8px}.location-relationship-card{display:flex;justify-content:space-between;gap:12px;border:1px solid var(--plotline-line);border-radius:8px;padding:10px;background:#fff}.location-flags{display:flex;flex-wrap:wrap;gap:10px 16px;color:var(--plotline-muted);font-size:.86rem}.validation-summary,.warning-filter-panel{border:1px solid var(--plotline-line);border-radius:8px;margin-bottom:16px;padding:12px;background:#fff}.validation-summary{display:flex;flex-wrap:wrap;gap:12px;color:var(--plotline-muted)}.warning-dashboard-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin-bottom:18px}.inspector-section-heading,.warning-card-title,.warning-count-row{display:flex;align-items:center;justify-content:space-between;gap:10px}.warning-list,.warning-mini-list{display:grid;gap:10px}.warning-card,.warning-mini-card{border:1px solid var(--plotline-line);border-radius:8px;padding:12px;background:#fff}.warning-card{display:grid;grid-template-columns:1fr auto;gap:12px}.warning-card p,.warning-mini-card p{margin:6px 0}.warning-actions{display:flex;align-items:flex-start;flex-wrap:wrap;gap:6px}.severity-pill{display:inline-flex;align-items:center;border-radius:999px;padding:2px 8px;font-size:.74rem;font-weight:800}.severity-pill.info{color:#365f7c;background:rgba(107,153,184,.14)}.severity-pill.warning{color:#7a5527;background:rgba(205,154,72,.17)}.severity-pill.error{color:#8a3434;background:rgba(190,83,83,.15)}.warning-mini-card{background:rgba(255,255,255,.76)}.dependency-columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.dependency-card{display:grid;gap:3px;border:1px solid var(--plotline-line);border-radius:8px;margin-bottom:8px;padding:9px;background:rgba(255,255,255,.78)}.move-preview-panel{max-width:900px}.focused-validation-summary{display:flex;flex-wrap:wrap;gap:10px;border:1px solid rgba(47,111,99,.16);border-radius:8px;margin-top:12px;padding:10px;color:var(--plotline-muted);background:rgba(47,111,99,.05)}.inspector-purpose-list{grid-template-columns:1fr;max-height:210px}.metric-slider-stack{display:grid;gap:12px}.metric-slider{display:grid;gap:4px}.metric-slider-label{display:flex;justify-content:space-between;gap:10px;color:var(--plotline-muted);font-size:.86rem;font-weight:700}.metric-slider-label output{color:var(--plotline-accent-dark)}.metric-slider .form-range{accent-color:var(--plotline-accent)}.asset-importance-slider{border:1px solid var(--plotline-line);border-radius:8px;padding:10px 12px;background:rgba(47,111,99,.05)}.story-asset-checkbox-field{margin-top:1.95rem}.story-asset-event-item{justify-content:flex-start;gap:8px}.story-asset-event-item .asset-event-marker{flex:0 0 auto;min-width:1.55rem;width:auto}.story-asset-event-details{min-width:0}.scene-inspector-root{display:grid;gap:12px}.scene-overview-card{display:grid;gap:10px;border:1px solid rgba(47,111,99,.18);border-radius:10px;padding:12px;background:linear-gradient(180deg,rgba(47,111,99,.08),rgba(255,255,255,.88))}.scene-overview-card h3{margin:0;font-size:1.05rem}.scene-overview-card .muted{display:flex;flex-wrap:wrap;gap:7px;margin:0}.breadcrumb-trail,.context-nav,.inspector-nav-links,.overview-chip-grid{display:flex;flex-wrap:wrap;gap:7px}.breadcrumb-trail a,.breadcrumb-trail span,.context-nav a,.overview-chip{display:inline-flex;align-items:center;border:1px solid var(--plotline-line);border-radius:999px;padding:4px 9px;color:var(--plotline-muted);font-size:.78rem;font-weight:800;text-decoration:none;background:rgba(255,255,255,.72)}.overview-chip.attention{color:#7a5527;border-color:rgba(205,154,72,.35);background:rgba(205,154,72,.14)}.overview-outcome{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;margin:0;color:var(--plotline-muted)}.inspector-quick-nav{position:sticky;top:0;z-index:5;display:grid;gap:8px;border:1px solid var(--plotline-line);border-radius:10px;padding:10px;background:rgba(255,255,255,.94);box-shadow:0 10px 24px rgba(55,48,39,.08);backdrop-filter:blur(8px)}.inspector-nav-links a{border:1px solid var(--plotline-line);border-radius:999px;padding:4px 8px;color:var(--plotline-muted);font-size:.76rem;font-weight:800;text-decoration:none}.inspector-nav-links a.active,.inspector-nav-links a:hover{color:var(--plotline-accent-dark);border-color:rgba(47,111,99,.35);background:rgba(47,111,99,.08)}.inspector-accordion-toggle{display:flex;align-items:center;justify-content:space-between;width:100%;border:0;padding:0;color:var(--plotline-ink);font-size:.95rem;font-weight:900;text-align:left;background:0 0}.inspector-accordion-toggle::before{content:"+";display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:999px;margin-right:7px;color:var(--plotline-accent-dark);background:rgba(47,111,99,.09)}.inspector-section.is-open>.inspector-accordion-toggle::before{content:"-"}.inspector-accordion-toggle span:first-child{margin-right:auto}.accordion-count{color:var(--plotline-muted);font-size:.75rem}.inspector-subheading{margin:14px 0 8px;color:var(--plotline-muted);font-size:.88rem;font-weight:900}.inspector-accordion-body{display:none;margin-top:12px}.inspector-section.is-open>.inspector-accordion-body{display:block}.inspector-section.is-filtered-out{display:none}.breadcrumb-trail{margin-bottom:12px}.breadcrumb-trail a:hover,.context-nav a:hover{color:var(--plotline-accent-dark);border-color:rgba(47,111,99,.35);background:rgba(47,111,99,.08)}.context-header,.working-context-banner{display:flex;align-items:center;justify-content:space-between;gap:14px;border:1px solid var(--plotline-line);border-radius:10px;margin-bottom:16px;padding:10px 12px;background:rgba(255,255,255,.84);box-shadow:0 10px 24px rgba(55,48,39,.05)}.context-title{display:grid;gap:2px}.context-title strong{color:var(--plotline-ink)}.analytics-grid-three,.analytics-grid-two,.health-kpi-grid{display:grid;gap:14px;margin:18px 0}.health-kpi-grid{grid-template-columns:repeat(4,minmax(0,1fr))}.health-kpi-grid .plain-card{display:grid;gap:4px}.health-kpi-grid strong{color:var(--plotline-ink);font-size:2rem;line-height:1}.analytics-grid-two{grid-template-columns:repeat(2,minmax(0,1fr))}.analytics-grid-three{grid-template-columns:repeat(3,minmax(0,1fr))}.analytics-section{border:1px solid var(--plotline-line);border-radius:10px;margin:18px 0;padding:16px;background:rgba(255,255,255,.84);box-shadow:0 14px 34px rgba(55,48,39,.07)}.analytics-section-heading{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:14px}.analytics-section-heading h2,.plain-card h3{margin:0}.analytics-metric-stack{display:grid;gap:12px;overflow-x:auto;padding-bottom:4px}.metric-shape-chart{display:grid;gap:12px}.metric-shape-intro{max-width:760px;margin:0;color:var(--plotline-muted)}.metric-toggle-row{display:flex;flex-wrap:wrap;gap:8px}.metric-toggle-pill{display:inline-flex;align-items:center;gap:6px;border:1px solid rgba(82,75,65,.16);border-radius:999px;padding:5px 9px;color:var(--plotline-muted);font-size:.78rem;font-weight:800;background:rgba(255,255,255,.78);cursor:pointer}.metric-toggle-pill::before{content:"";width:10px;height:10px;border-radius:999px;background:var(--metric-colour)}.metric-toggle-pill input{margin:0;accent-color:var(--metric-colour)}.timeline-metric-edit-toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:end}.timeline-metric-edit-select{display:grid;gap:3px;min-width:180px;color:var(--plotline-muted);font-size:.78rem;font-weight:800}.timeline-metric-edit-status{align-self:center;border:1px solid rgba(47,111,99,.18);border-radius:999px;padding:5px 9px;color:var(--plotline-muted);font-size:.78rem;font-weight:800;background:rgba(255,255,255,.72)}.timeline-metric-edit-hint{font-size:.82rem}.metric-chart-frame canvas.metric-curve-editing{cursor:ns-resize}.metric-chart-frame{position:relative;min-height:320px;border:1px solid var(--plotline-line);border-radius:10px;padding:14px 12px 32px;background:linear-gradient(180deg,rgba(255,255,255,.92),rgba(247,245,240,.76))}.metric-timeline-frame{width:calc((var(--scene-count) * var(--timeline-scene-width)) + ((var(--scene-count) - 1) * var(--timeline-grid-gap)))}.metric-chart-frame canvas{width:100%;height:300px}.metric-timeline-board .metric-graph-stack{width:max-content;min-width:100%}.metric-timeline-board .metric-graph-row{grid-template-columns:var(--timeline-label-width) repeat(var(--scene-count),var(--timeline-scene-width));gap:var(--timeline-grid-gap);min-width:100%}.metric-timeline-board .metric-graph-points{display:contents}.metric-timeline-board .metric-point{border:1px solid rgba(47,111,99,.12);border-radius:8px;background:rgba(255,255,255,.62)}.metric-raw-details{border-top:1px solid var(--plotline-line);padding-top:8px}.metric-raw-details summary{color:var(--plotline-muted);font-size:.82rem;font-weight:800;cursor:pointer}.timeline-control-panel{display:grid;gap:12px;border:1px solid var(--plotline-line);border-radius:10px;margin-bottom:16px;padding:12px;background:rgba(255,255,255,.84);box-shadow:0 10px 24px rgba(55,48,39,.05)}.timeline-filter-drawer summary{color:var(--plotline-ink);font-weight:900;cursor:pointer}.timeline-filter-grid{display:grid;grid-template-columns:repeat(6,minmax(130px,1fr));gap:10px;margin-top:12px}.preset-chip-row,.timeline-filter-actions,.timeline-filter-checks,.timeline-preset-panel,.timeline-visibility-panel{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.timeline-filter-checks{grid-column:span 3;color:var(--plotline-muted);font-size:.82rem;font-weight:700}.timeline-filter-actions{grid-column:span 3;justify-content:flex-end}.preset-chip,.timeline-visibility-panel label{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--plotline-line);border-radius:999px;padding:4px 8px;color:var(--plotline-muted);font-size:.78rem;font-weight:800;background:rgba(255,255,255,.76)}.timeline-preset-panel form{display:inline-flex;align-items:center;gap:6px}.preset-chip a{color:var(--plotline-accent-dark);text-decoration:none}.preset-chip button{border:0;color:var(--plotline-muted);background:0 0;font-weight:900}.timeline-focus-banner{display:flex;align-items:center;justify-content:space-between;gap:12px;border:1px solid rgba(47,111,99,.22);border-radius:10px;margin-bottom:16px;padding:10px 12px;background:rgba(47,111,99,.08)}.thread-marker-icon{display:inline-flex;align-items:center;justify-content:center;min-width:16px;font-size:.78rem;font-weight:900}.thread-marker-type-abandon .thread-marker-icon,.thread-marker-type-resolve .thread-marker-icon,.thread-marker-type-start .thread-marker-icon,.thread-marker-type-twist .thread-marker-icon{font-size:.9rem}.thread-marker-type-start{border-radius:8px 999px 999px 8px}.thread-marker-type-twist{border-color:#b8762b;background:#fff4dc}.thread-marker-type-branch,.thread-marker-type-merge,.thread-marker-type-split{border-color:#6750a4;background:#f0ebff}.thread-marker-type-reveal .thread-marker-icon{border:1px solid currentColor;border-radius:999px;font-size:0}.thread-marker-type-reveal .thread-marker-icon::after{content:"";width:4px;height:4px;border-radius:999px;background:currentColor}.timeline-navigation-card{display:grid;grid-template-areas:"icon copy" "icon actions";grid-template-columns:auto minmax(0,620px);gap:8px 14px;align-items:center;justify-content:center;border:1px solid rgba(47,111,99,.42);border-left:5px solid var(--plotline-accent);border-radius:8px;margin:0 0 12px;padding:14px 16px;max-height:280px;overflow:hidden;color:var(--plotline-ink);background:linear-gradient(135deg,rgba(238,245,242,.98),rgba(255,255,255,.94));box-shadow:0 10px 26px rgba(30,37,43,.08);opacity:1;transform:translateY(0);transition:opacity 1.15s ease,max-height 1.2s ease,margin 1.2s ease,padding 1.2s ease,transform 1.15s ease}.timeline-navigation-card[hidden]{display:none}.timeline-navigation-card.is-dismissing{margin-bottom:0;padding-top:0;padding-bottom:0;max-height:0;opacity:0;transform:translateY(-6px)}.timeline-navigation-card-icon{grid-area:icon;display:grid;place-items:center;width:42px;height:42px;border-radius:50%;color:#fff;background:var(--plotline-accent);font-size:1.25rem;font-weight:900}.timeline-navigation-card-copy{grid-area:copy;text-align:center}.timeline-navigation-card-copy h2{margin:0 0 6px;color:var(--plotline-accent-dark);font-size:1rem;font-weight:900}.timeline-navigation-card-copy p{margin:2px 0;color:var(--plotline-ink);font-size:.9rem}.timeline-navigation-card-actions{grid-area:actions;display:flex;flex-wrap:wrap;gap:8px;justify-content:center}.timeline-horizontal-overflow{transition:box-shadow .18s ease}.timeline-horizontal-overflow.has-scroll-left{box-shadow:inset 18px 0 18px -20px rgba(30,37,43,.55)}.timeline-horizontal-overflow.has-scroll-right{box-shadow:inset -18px 0 18px -20px rgba(30,37,43,.55)}.timeline-horizontal-overflow.has-scroll-left.has-scroll-right{box-shadow:inset 18px 0 18px -20px rgba(30,37,43,.55),inset -18px 0 18px -20px rgba(30,37,43,.55)}[data-theme=dark] .timeline-navigation-card{border-color:rgba(111,169,155,.52);border-left-color:#6fa99b;color:#f4eadc;background:linear-gradient(135deg,rgba(34,83,74,.72),rgba(32,31,34,.96));box-shadow:0 12px 28px rgba(0,0,0,.28)}[data-theme=dark] .timeline-navigation-card-copy h2,[data-theme=dark] .timeline-navigation-card-copy p{color:#f4eadc}[data-theme=dark] .timeline-horizontal-overflow.has-scroll-left{box-shadow:inset 18px 0 18px -20px rgba(244,234,220,.55)}[data-theme=dark] .timeline-horizontal-overflow.has-scroll-right{box-shadow:inset -18px 0 18px -20px rgba(244,234,220,.55)}[data-theme=dark] .timeline-horizontal-overflow.has-scroll-left.has-scroll-right{box-shadow:inset 18px 0 18px -20px rgba(244,234,220,.55),inset -18px 0 18px -20px rgba(244,234,220,.55)}@media (prefers-reduced-motion:reduce){.timeline-navigation-card{transition:opacity .12s ease}}.lane-dim,.timeline-scene-card.filter-dim,.timeline-scene-card.focus-dim{opacity:.35}.timeline-scene-card.filter-match.focus-match,.timeline-scene-card.focus-match{outline:3px solid rgba(47,111,99,.32);box-shadow:0 12px 26px rgba(47,111,99,.16)}.timeline-section-hidden{display:none!important}.hide-scene-cards .timeline-scene-card{display:none}.timeline-control-panel{position:sticky;top:56px;z-index:18}.timeline-control-select{padding-right:6px}.timeline-control-select .form-select{width:auto;min-width:132px;border:0;padding-top:2px;padding-bottom:2px;background-color:transparent;color:var(--plotline-accent-dark);font-weight:800}.timeline-jump-panel{display:grid;grid-template-columns:auto minmax(180px,1.2fr) minmax(180px,1fr) minmax(170px,0.9fr) minmax(180px,1fr) auto;gap:8px;align-items:center;width:100%}.timeline-shortcut-hint{color:var(--plotline-muted);font-size:.76rem;font-weight:700;white-space:nowrap}.timeline-minimap{display:flex;align-items:stretch;gap:4px;margin:0 0 12px;padding:8px;max-width:100%;min-width:0;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-gutter:stable;border:1px solid rgba(47,111,99,.18);border-radius:8px;background:rgba(255,255,255,.72)}.timeline-minimap button{position:relative;flex:0 0 max(42px,calc(var(--chapter-scenes) * 18px));min-width:42px;min-height:34px;border:1px solid rgba(47,111,99,.18);border-radius:7px;color:var(--plotline-accent-dark);background:linear-gradient(180deg,#f8fbf9 0,#edf4f1 100%);font-size:.72rem;font-weight:800}.timeline-minimap button.is-active{border-color:var(--plotline-accent);color:#fff;background:linear-gradient(180deg,var(--plotline-accent) 0,var(--plotline-accent-dark) 100%);box-shadow:0 0 0 2px rgba(47,111,99,.14)}.timeline-minimap button::after{content:"";position:absolute;right:5px;bottom:5px;width:calc(var(--chapter-warnings) * 4px);max-width:28px;height:4px;border-radius:999px;background:#b45c5c;opacity:.75}.drag-drop-panel,.timeline-drag-palette{border:1px solid rgba(47,111,99,.18);border-radius:10px;margin-bottom:12px;padding:12px;background:rgba(255,255,255,.84);box-shadow:0 10px 24px rgba(55,48,39,.05)}.timeline-drag-palette{display:grid;grid-template-columns:minmax(220px,0.35fr) minmax(0,1fr);gap:12px;align-items:center}.drag-chip-group{display:flex;flex-wrap:wrap;gap:6px;max-height:104px;overflow:auto;padding-right:4px}.drag-chip{display:inline-flex;align-items:center;border:1px solid var(--plotline-line);border-radius:999px;padding:4px 9px;background:#fff;color:var(--plotline-ink);font-size:.78rem;font-weight:800;cursor:grab}.drag-chip.character{border-color:#dacb9e;background:#fff8e8}.drag-chip.asset{border-color:#ded9ea;background:#f4f0ff}.drag-chip.location{border-color:rgba(47,111,99,.18);background:#eef8f4}.is-dragging{opacity:.55}.drop-target-active{outline:3px solid rgba(47,111,99,.28);outline-offset:2px}.timeline-chapter.chapter-drop-active,.timeline-scenes.drop-chapter-end{background:rgba(47,111,99,.06)}.timeline-scenes.drop-chapter-end::after{content:"Drop scene at end of chapter";display:flex;align-items:center;justify-content:center;min-width:var(--timeline-scene-width);min-height:74px;border:2px dashed rgba(197,139,43,.48);border-radius:12px;color:#7a5527;background:rgba(255,248,232,.82);font-size:.8rem;font-weight:900}.timeline-scene-card.drop-before{box-shadow:inset 5px 0 0 #c58b2b,0 12px 26px rgba(197,139,43,.16)}.timeline-scene-card.drop-after{box-shadow:inset -5px 0 0 #c58b2b,0 12px 26px rgba(197,139,43,.16)}.drag-drop-panel{position:sticky;top:132px;z-index:19}.drag-drop-panel-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.drag-drop-panel h2{margin:0;font-size:1.08rem}.drag-drop-form{border-top:1px solid var(--plotline-line);margin-top:10px;padding-top:10px}.scene-move-modal-content{border:1px solid rgba(47,111,99,.18);border-radius:14px;background:var(--plotline-surface);color:var(--plotline-ink);box-shadow:0 24px 70px rgba(55,48,39,.22)}.scene-move-modal-content .modal-footer,.scene-move-modal-content .modal-header{border-color:var(--plotline-line)}.scene-move-modal-content .modal-title{margin:0;color:var(--plotline-ink);font-size:1.2rem;font-weight:900}.scene-move-summary{margin:0 0 12px;color:var(--plotline-ink);font-size:1rem;font-weight:800}[data-theme=dark] .scene-move-modal-content{background:#201f22;color:#f4eadc;border-color:rgba(244,234,220,.16);box-shadow:0 24px 70px rgba(0,0,0,.55)}[data-theme=dark] .scene-move-modal-content .modal-title,[data-theme=dark] .scene-move-summary{color:#f4eadc}[data-theme=dark] .scene-move-modal-content .modal-footer,[data-theme=dark] .scene-move-modal-content .modal-header{border-color:rgba(244,234,220,.14)}[data-theme=dark] .scene-move-modal-content .btn-close{filter:invert(1) grayscale(100%)}.plain-preview{border:1px solid rgba(197,139,43,.28);border-radius:8px;margin:10px 0 0;padding:8px 10px;color:#5e4518;background:#fff8e8;font-size:.86rem;font-weight:800}.drag-check-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px 10px;color:var(--plotline-muted);font-size:.82rem;font-weight:700}.lane-board-heading,.pacing-heading,.timeline-section-header{position:sticky;left:0;z-index:6}.timeline-section-header{display:flex;align-items:center;justify-content:space-between;gap:10px;width:calc(var(--timeline-label-width) + (var(--scene-count) * var(--timeline-scene-width)) + (var(--scene-count) * var(--timeline-grid-gap)));min-width:100%;margin-bottom:10px;border-radius:8px;padding:8px 10px;background:rgba(255,255,255,.92);box-shadow:0 8px 16px rgba(30,37,43,.05)}.lane-board-heading button,.lane-group-heading,.pacing-heading button,.plot-lane-board-heading button,.timeline-section-header button{border:0;padding:0;color:var(--plotline-accent-dark);background:0 0;font:inherit;font-weight:900;text-align:left}.lane-board-heading button::before,.lane-group-heading::before,.pacing-heading button::before,.plot-lane-board-heading button::before,.timeline-section-header button::before{content:"v";display:inline-block;margin-right:6px;color:var(--plotline-muted);font-size:.72rem}.lane-board-heading button.is-collapsed::before,.lane-group-heading.is-collapsed::before,.pacing-heading button.is-collapsed::before,.plot-lane-board-heading button.is-collapsed::before,.timeline-section-header button.is-collapsed::before{content:">"}.lane-group{display:grid;gap:7px}.character-lane-group-body{display:grid;gap:7px}.lane-group-heading{position:sticky;left:0;z-index:6;width:var(--timeline-label-width);border-top:1px solid rgba(47,111,99,.12);padding:7px 8px 2px;color:var(--plotline-muted);background:rgba(247,250,248,.96);font-size:.78rem;text-transform:uppercase}.timeline-section-collapsed{display:none!important}.timeline-chapter-title{position:sticky;top:0;z-index:5;background:rgba(238,245,242,.96);backdrop-filter:blur(6px)}.metric-mini-summary,.scene-pov-label{display:inline-flex;width:fit-content;border-radius:999px;padding:2px 7px;font-size:.72rem;font-weight:800}.scene-pov-label{color:#514018;background:#fff3d3}.metric-mini-summary{color:var(--plotline-accent-dark);background:rgba(47,111,99,.08)}.timeline-scene-card.keyboard-focus,.timeline-scene-card.search-hit{outline:3px solid rgba(197,139,43,.34);box-shadow:0 12px 26px rgba(197,139,43,.16)}.timeline-scene-card.search-dim{opacity:.28}.asset-marker,.character-marker,.thread-marker{transition:transform .12s ease,box-shadow .12s ease}.asset-marker:hover,.character-marker:hover,.thread-marker:hover{transform:translateY(-1px);box-shadow:0 6px 12px rgba(30,37,43,.12)}.timeline-filtered-root.density-compact{--timeline-scene-width:170px;--timeline-grid-gap:6px}.timeline-filtered-root.density-expanded{--timeline-scene-width:280px;--timeline-grid-gap:14px}.timeline-filtered-root.density-compact .timeline-scene-card,.timeline-filtered-root.zoom-book .timeline-scene-card{gap:4px;padding:9px}.timeline-filtered-root.density-compact .metric-mini-summary,.timeline-filtered-root.density-compact .purpose-chip-row,.timeline-filtered-root.density-compact .scene-summary,.timeline-filtered-root.zoom-book .location-pill,.timeline-filtered-root.zoom-book .metric-mini-summary,.timeline-filtered-root.zoom-book .purpose-chip-row,.timeline-filtered-root.zoom-book .scene-pov-label,.timeline-filtered-root.zoom-book .scene-summary{display:none}.timeline-filtered-root.zoom-book{--timeline-scene-width:150px;--timeline-grid-gap:6px}.timeline-filtered-root.zoom-scene-detail{--timeline-scene-width:300px}.timeline-filtered-root.zoom-scene-detail .scene-summary{-webkit-line-clamp:7}.timeline-filtered-root.density-expanded .timeline-scenes{min-height:230px}.timeline-control-panel{position:relative;top:auto;z-index:4;display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:10px;padding:8px;box-shadow:none}.timeline-control-group,.timeline-filter-drawer{border:1px solid var(--plotline-line);border-radius:999px;background:rgba(255,255,255,.82)}.timeline-control-group[open],.timeline-filter-drawer[open]{border-radius:10px;flex-basis:100%}.timeline-control-group summary,.timeline-filter-drawer summary{min-width:max-content;padding:5px 10px;color:var(--plotline-accent-dark);font-size:.82rem;font-weight:900;cursor:pointer;list-style:none}.timeline-control-group summary::-webkit-details-marker,.timeline-drag-palette summary::-webkit-details-marker,.timeline-filter-drawer summary::-webkit-details-marker{display:none}.timeline-control-group summary::after,.timeline-drag-palette summary::after,.timeline-filter-drawer summary::after{content:" v";color:var(--plotline-muted);font-size:.72rem}.timeline-control-group[open] summary::after,.timeline-drag-palette[open] summary::after,.timeline-filter-drawer[open] summary::after{content:" ^"}.timeline-filter-grid{grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:8px;max-height:min(48vh,430px);overflow:auto;padding:0 10px 10px}.timeline-filter-grid .form-label{margin-bottom:2px;font-size:.72rem;font-weight:800}.timeline-filter-actions,.timeline-filter-checks{grid-column:1/-1}.timeline-jump-panel,.timeline-preset-panel,.timeline-visibility-panel{padding:0 10px 10px}.timeline-jump-panel{grid-template-columns:minmax(180px,1.5fr) repeat(3,minmax(150px,1fr))}.timeline-shortcut-hint{grid-column:1/-1}.timeline-preset-panel form{flex-wrap:nowrap}.timeline-preset-panel .form-control{width:150px}.timeline-filter-actions .btn,.timeline-preset-panel .btn{white-space:nowrap}.timeline-drag-palette{display:block;margin-bottom:8px;padding:7px 9px;box-shadow:none}.timeline-drag-palette summary{color:var(--plotline-accent-dark);font-size:.82rem;font-weight:900;cursor:pointer}.timeline-drag-palette .drag-chip-group{margin-top:8px}.drag-drop-panel{position:relative;top:auto;z-index:9;box-shadow:0 12px 28px rgba(30,37,43,.08)}.timeline-filtered-root,.timeline-filtered-root.density-compact,.timeline-filtered-root.zoom-book{--timeline-scene-width:220px;--timeline-grid-gap:10px}.timeline-filtered-root.density-expanded,.timeline-filtered-root.zoom-scene-detail{--timeline-scene-width:290px}.timeline-filtered-root.density-compact.zoom-book{--timeline-scene-width:200px}.timeline-chapter,.timeline-empty-scene,.timeline-scene-card{overflow-wrap:normal;word-break:normal}.asset-lane-label strong,.character-lane-label strong,.plot-lane-label strong,.timeline-chapter-title strong,.timeline-scene-card strong{min-width:0;white-space:normal;word-break:normal;overflow-wrap:break-word}.timeline-scene-card{cursor:grab}.drag-chip:active,.timeline-scene-card:active,[draggable=true]:active{cursor:grabbing}body.drag-active .timeline-scene-card,body.dragging-asset [data-asset-drop-target],body.dragging-location [data-drag-type=location]{transition:outline-color .12s ease,box-shadow .12s ease,opacity .12s ease}body.dragging-asset .timeline-scene-card,body.dragging-asset [data-asset-drop-target],body.dragging-character .timeline-scene-card,body.dragging-location [data-drag-type=location],body.dragging-scene .timeline-scene-card,body.dragging-scene .timeline-scenes[data-chapter-drop-target]{outline:1px dashed rgba(47,111,99,.22);outline-offset:2px}.is-dragging{opacity:.42;transform:scale(.98)}.drop-target-active{outline:3px solid rgba(47,111,99,.42)!important;outline-offset:3px}.analytics-metric-row{display:grid;grid-template-columns:170px minmax(560px,1fr);gap:12px;align-items:stretch}.analytics-metric-label{display:flex;align-items:center;border-right:1px solid var(--plotline-line);color:var(--plotline-muted);font-weight:800}.analytics-sparkline{display:grid;grid-template-columns:repeat(var(--point-count),minmax(28px,1fr));align-items:end;gap:5px;min-height:88px;border-bottom:1px solid rgba(47,111,99,.25);padding-top:10px}.analytics-bar{position:relative;display:flex;align-items:end;justify-content:center;min-height:12px;height:max(12px,var(--bar-height));border-radius:6px 6px 2px 2px;color:var(--plotline-accent-dark);text-decoration:none;background:linear-gradient(180deg,rgba(47,111,99,.58),rgba(47,111,99,.18))}.analytics-bar:hover{background:linear-gradient(180deg,rgba(47,111,99,.82),rgba(47,111,99,.28))}.analytics-bar span{position:absolute;bottom:calc(100% + 3px);color:var(--plotline-muted);font-size:.68rem;font-weight:800}.analytics-count-row,.analytics-link-row,.analytics-list-item,.purpose-stat-row{border-top:1px solid var(--plotline-line);padding:9px 0}.analytics-list-item{display:grid;gap:3px}.analytics-list-item p{margin:0}.analytics-count-row,.analytics-link-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.analytics-link-row{color:inherit;text-decoration:none}.analytics-link-row:hover{color:var(--plotline-accent-dark)}.analytics-link-row span,.analytics-link-row strong{min-width:0}.purpose-stat-row{display:grid;grid-template-columns:minmax(120px,1fr) minmax(90px,1.5fr) 42px;align-items:center;gap:10px}.purpose-stat-bar{overflow:hidden;height:8px;border-radius:999px;background:rgba(47,111,99,.08)}.purpose-stat-bar span{display:block;height:100%;border-radius:inherit;background:var(--plotline-accent)}.inspector-actions{position:sticky;bottom:-16px;display:flex;gap:8px;border-top:1px solid var(--plotline-line);margin:2px -16px -16px;padding:12px 16px;background:rgba(255,255,255,.95);backdrop-filter:blur(8px)}.story-bible-heading{align-items:end}.story-bible-search{display:flex;gap:8px;min-width:min(100%,420px)}.story-bible-search .form-control{min-width:0}.story-bible-nav{position:sticky;top:72px;z-index:4;display:flex;flex-wrap:wrap;gap:8px;margin:0 0 18px;padding:10px;border:1px solid var(--plotline-line);border-radius:12px;background:rgba(250,249,246,.92);backdrop-filter:blur(10px)}.story-bible-actions a,.story-bible-nav a{display:inline-flex;align-items:center;border-radius:999px;padding:6px 10px;background:rgba(47,111,99,.08);color:var(--plotline-ink);font-size:.86rem;font-weight:650;text-decoration:none}.story-bible-actions a:hover,.story-bible-nav a:hover{background:rgba(47,111,99,.16)}.story-bible-section{margin:0 0 28px}.story-bible-section-heading{display:flex;align-items:end;justify-content:space-between;gap:16px;margin-bottom:12px}.story-bible-section-heading h2{margin:0}.soft-count{color:var(--plotline-muted);font-size:.9rem;font-weight:650;white-space:nowrap}.story-bible-grid,.story-bible-search-results{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:14px}.story-bible-grid-compact{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.story-bible-card{border:1px solid var(--plotline-line);border-radius:14px;padding:14px;background:rgba(255,255,255,.86);box-shadow:0 14px 34px rgba(36,32,26,.06)}.story-bible-card h3{margin:0 0 8px;font-size:1.05rem}.story-bible-card h4{margin:14px 0 8px;font-size:.96rem}.story-bible-card p{color:var(--plotline-muted)}.story-bible-card-title{display:flex;align-items:start;justify-content:space-between;gap:10px}.story-bible-card-title span{border:1px solid var(--plotline-line);border-radius:999px;padding:3px 8px;background:rgba(250,249,246,.9);color:var(--plotline-muted);font-size:.78rem;font-weight:700;white-space:nowrap}.story-bible-meta{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0}.story-bible-meta span{border-radius:999px;padding:4px 8px;background:rgba(36,32,26,.05);color:var(--plotline-muted);font-size:.78rem;font-weight:650}.story-bible-facts{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:6px 10px;margin:12px 0}.story-bible-facts dt{color:var(--plotline-muted);font-size:.78rem;font-weight:800;text-transform:uppercase}.story-bible-facts dd{margin:0;min-width:0;overflow-wrap:anywhere}.story-bible-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.story-bible-link-list{display:grid;gap:8px;margin:0;padding:0;list-style:none}.story-bible-link-list li{display:grid;gap:2px;padding:8px 0;border-bottom:1px solid rgba(36,32,26,.08)}.story-bible-link-list li:last-child{border-bottom:0}.story-bible-link-list span{color:var(--plotline-muted);font-size:.88rem}.story-bible-timeline-summary{display:grid;gap:14px}.story-bible-wide-card{max-width:none}.story-bible-chapter-summary{border-top:1px solid var(--plotline-line);padding-top:8px}.story-bible-scene-summary{display:grid;grid-template-columns:minmax(220px,1.5fr) repeat(3,minmax(120px,0.8fr));gap:8px;padding:9px 0;border-top:1px solid rgba(36,32,26,.08)}.story-bible-scene-summary p{grid-column:1/-1;margin:0}.story-bible-scene-summary span{color:var(--plotline-muted);font-size:.88rem}.attention-list{display:grid;gap:10px}.attention-list article{display:grid;grid-template-columns:max-content minmax(0,1fr) auto;align-items:center;gap:10px;border:1px solid var(--plotline-line);border-radius:12px;padding:10px 12px;background:rgba(255,255,255,.82)}.attention-list p{grid-column:2/-1;margin:0;color:var(--plotline-muted)}.chapter-goal-form,.writer-add-form,.writer-edit-form,.writer-workspace-form{display:grid;gap:10px}.writer-checkbox-grid{display:flex;flex-wrap:wrap;gap:8px 14px}.writer-checkbox-grid label{display:inline-flex;align-items:center;gap:6px;color:var(--plotline-muted);font-size:.86rem;font-weight:650}.writer-priority-control{display:grid;grid-template-columns:minmax(0,1fr) max-content;align-items:center;gap:12px}.writer-priority-control .form-range{min-width:0}.writer-note-list,.writer-queue-list{display:grid;gap:10px}.writer-mini-card{border:1px solid var(--plotline-line);border-radius:12px;padding:10px;background:rgba(255,255,255,.72)}.writer-mini-card summary{display:flex;align-items:center;gap:8px;cursor:pointer}.writer-mini-card summary strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.attachment-card-list{display:grid;gap:10px}.attachment-summary{align-items:center}.attachment-kind{border-radius:999px;padding:3px 8px;background:rgba(47,111,99,.1);color:var(--plotline-accent);font-size:.76rem;font-weight:800;text-transform:uppercase}.attachment-card-body{display:grid;gap:6px;margin-top:10px}.attachment-card-body p{margin:0}.attachment-add-card{margin-top:10px}.writer-checklist{display:grid;gap:6px}.writer-add-inline,.writer-checklist-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px}.writer-remove-inline{display:flex;justify-content:end;margin:-2px 0 4px}.writer-dashboard-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin-bottom:26px}.writer-dashboard-panel{border:1px solid var(--plotline-line);border-radius:14px;padding:14px;background:rgba(255,255,255,.7);box-shadow:0 14px 34px rgba(36,32,26,.05)}.writer-queue-card{border:1px solid var(--plotline-line);border-radius:12px;padding:12px;background:rgba(255,255,255,.9)}.writer-queue-card h3{margin:0;font-size:1rem}.writer-queue-card p{margin:8px 0 0;color:var(--plotline-muted)}.writer-queue-title{display:flex;align-items:start;justify-content:space-between;gap:12px}.writer-kpi{display:block;color:var(--plotline-accent);font-size:2.2rem;line-height:1}.export-action-row{margin:-8px 0 18px}.dynamics-timeline{position:relative;min-height:128px;border:1px solid var(--plotline-line);border-radius:14px;background:linear-gradient(180deg,rgba(47,111,99,.07),rgba(255,255,255,.84));overflow:hidden}.dynamics-timeline::before{content:"";position:absolute;left:12px;right:12px;top:64px;height:1px;background:rgba(36,32,26,.18)}.dynamics-marker{position:absolute;bottom:18px;width:12px;height:86px;transform:translateX(-50%);display:flex;align-items:end;justify-content:center}.dynamics-marker span{display:block;width:8px;min-height:12px;max-height:82px;border-radius:999px;background:var(--plotline-accent);box-shadow:0 8px 20px rgba(47,111,99,.22)}.dynamics-marker.pov span{background:#8a4b2f}.dynamics-timeline.relationship .dynamics-marker span{background:#7c5fb0}.scenario-banner{display:flex;justify-content:space-between;gap:18px;align-items:flex-start;border:1px solid rgba(103,80,164,.22);border-radius:12px;padding:16px;margin-bottom:16px;background:linear-gradient(135deg,rgba(103,80,164,.1),rgba(255,255,255,.86));box-shadow:0 12px 28px rgba(36,32,26,.08)}.scenario-banner h1{margin:0 0 4px}.scenario-banner p{margin:0;color:var(--plotline-muted)}.scenario-card{border-color:rgba(103,80,164,.18)}.scenario-warning-strip{display:flex;gap:10px;align-items:center;border:1px solid var(--plotline-line);border-radius:10px;padding:10px 12px;margin-bottom:14px;background:rgba(255,255,255,.76);color:var(--plotline-muted)}.scenario-warning-strip strong{color:var(--plotline-ink)}.scenario-board{border:1px solid var(--plotline-line);border-radius:12px;padding:12px;background:rgba(247,245,240,.74);overflow:hidden;margin-bottom:18px}.scenario-scroll{display:flex;gap:14px;overflow-x:auto;padding-bottom:8px}.scenario-chapter{min-width:360px;max-width:520px;border:1px solid rgba(82,75,65,.14);border-radius:10px;background:rgba(255,255,255,.86)}.scenario-chapter header{display:grid;gap:2px;border-bottom:1px solid var(--plotline-line);padding:10px 12px}.scenario-chapter header span{color:var(--plotline-muted);font-size:.78rem;font-weight:800;text-transform:uppercase}.scenario-scene-row{display:grid;gap:10px;padding:10px}.scenario-scene-card{display:grid;gap:8px;border:1px solid rgba(82,75,65,.12);border-radius:9px;padding:10px;background:#fff}.scenario-scene-title{display:grid;gap:2px}.scenario-mini-data,.scenario-scene-title span{color:var(--plotline-muted);font-size:.78rem}.scenario-mini-data{display:grid;gap:2px}.scenario-chapter-form,.scenario-compare-list article,.scenario-move-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.scenario-chapter-form select{min-width:160px;width:auto}.scenario-compare-list{display:grid;gap:10px}.scenario-compare-list article{justify-content:space-between;border:1px solid var(--plotline-line);border-radius:9px;padding:10px;background:rgba(255,255,255,.82)}.danger-zone{border-color:rgba(163,61,61,.24)}.archive-filter-form{align-items:end}.archive-item-list{display:grid;gap:10px}.archive-item-card{display:flex;justify-content:space-between;gap:14px;align-items:center;border:1px solid var(--plotline-line);border-radius:10px;padding:12px;background:rgba(255,255,255,.82)}.archive-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-items:end}.delete-forever-form{display:inline-flex;margin:0}.archive-item-card h3{margin:4px 0}.archived-badge{display:inline-flex;border:1px solid rgba(82,75,65,.16);border-radius:999px;padding:2px 8px;color:var(--plotline-muted);font-size:.72rem;font-weight:800;text-transform:uppercase;background:rgba(247,245,240,.86)}.restore-button{white-space:nowrap}.archive-button-form{display:inline-flex;align-items:center;margin:0}.acceptance-summary-grid{display:grid;grid-template-columns:repeat(3,minmax(160px,1fr));gap:12px;margin-bottom:16px}.acceptance-summary{display:grid;gap:4px;padding:16px}.acceptance-summary strong{font-size:2rem;line-height:1}.acceptance-summary.pass{border-left:5px solid #4d7654}.acceptance-summary.review{border-left:5px solid #b8762b}.acceptance-summary.blocked{border-left:5px solid #963f36}.acceptance-check-list{display:grid;gap:14px}.acceptance-area{border:1px solid var(--plotline-line);border-radius:10px;padding:12px;background:rgba(255,255,255,.62)}.acceptance-area h3{margin:0 0 10px;font-size:1.05rem}.acceptance-check-items{display:grid;gap:8px}.acceptance-check-item{display:grid;grid-template-columns:92px minmax(0,1fr);gap:10px;align-items:start;border:1px solid rgba(82,75,65,.12);border-radius:8px;padding:10px;background:rgba(255,255,255,.74)}.acceptance-check-item p{margin:2px 0 0;color:var(--plotline-muted)}.acceptance-status{display:inline-flex;justify-content:center;width:fit-content;min-width:74px;border-radius:999px;padding:3px 9px;font-size:.72rem;font-weight:900;text-transform:uppercase}.acceptance-check-item.pass .acceptance-status{color:#24512b;background:#e6f4e7}.acceptance-check-item.review .acceptance-status{color:#7b4b13;background:#fbecd3}.acceptance-check-item.blocked .acceptance-status{color:#7a2222;background:#fff0f0}.plotline-confirm-modal .modal-title{color:var(--plotline-ink)}.plotline-confirm-modal .modal-body p{white-space:pre-line}.delete-confirm-field{border:1px solid rgba(163,61,61,.22);border-radius:8px;padding:12px;background:rgba(255,247,244,.7)}@media print{.breadcrumb-trail,.button-row,.context-header,.inspector-quick-nav,.story-bible-nav,header{display:none!important}.plotline-shell{max-width:none;padding:0}}@media (max-width:760px){.plotline-shell{padding-left:14px;padding-right:14px}.page-heading{display:block}.page-heading .button-row,.timeline-filter{margin-top:14px}.timeline-filter{align-items:stretch;flex-direction:column}.timeline-navigation-card{grid-template-areas:"copy" "actions";grid-template-columns:1fr}.timeline-navigation-card-icon{display:none}.timeline-navigation-card-actions{justify-content:center}.story-bible-search,.story-bible-section-heading{align-items:stretch;flex-direction:column}.attention-list article,.story-bible-scene-summary{grid-template-columns:1fr}.writer-dashboard-grid{grid-template-columns:1fr}.writer-add-inline,.writer-checklist-row{grid-template-columns:1fr}.trial-dashboard-banner,.trial-status-panel--signup,.trial-status-panel__heading{align-items:stretch;flex-direction:column}.trial-dashboard-banner__actions,.trial-dashboard-banner__actions .btn,.trial-status-panel--signup .btn{width:100%}.attention-list p{grid-column:auto}}@media (max-width:900px){.workspace-layout,.workspace-layout.inspector-drawer-open{grid-template-columns:1fr;gap:14px}.timeline-inspector-resizer{display:none}.scene-inspector-panel{position:static;max-height:none;overflow:visible}.scene-inspector-body{max-height:none;overflow:visible}}@media (max-width:1100px){.metric-graph-row{grid-template-columns:1fr}.analytics-grid-three,.analytics-grid-two,.health-kpi-grid{grid-template-columns:1fr}.analytics-metric-row{grid-template-columns:1fr}.analytics-metric-label{border-right:0}} \ No newline at end of file +html{font-size:15px;min-height:100%}@media(min-width:768px){html{font-size:16px}}:root{--plotline-ink:#1e252b;--plotline-muted:#66717b;--plotline-paper:#fbfaf7;--plotline-panel:#fff;--plotline-line:#d8ddd8;--plotline-accent:#2f6f63;--plotline-accent-dark:#22534a;--plotline-soft:#eef5f2;--timeline-label-width:180px;--timeline-scene-width:220px;--timeline-grid-gap:10px}body{min-height:100%;margin-bottom:0;color:var(--plotline-ink);background:var(--plotline-paper)}a{color:var(--plotline-accent-dark)}.btn-primary{--bs-btn-bg:var(--plotline-accent);--bs-btn-border-color:var(--plotline-accent);--bs-btn-hover-bg:var(--plotline-accent-dark);--bs-btn-hover-border-color:var(--plotline-accent-dark)}.btn-outline-primary{--bs-btn-color:var(--plotline-accent-dark);--bs-btn-border-color:var(--plotline-accent);--bs-btn-hover-bg:var(--plotline-accent);--bs-btn-hover-border-color:var(--plotline-accent)}.plotline-nav{background:rgba(255,255,255,.94);backdrop-filter:blur(8px)}.plotline-shell{max-width:1480px;padding-left:24px;padding-right:24px}.plotline-shell-wide{max-width:none;padding-left:clamp(18px,2vw,32px);padding-right:clamp(18px,2vw,32px)}.page-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:24px;margin:18px 0 24px}.workspace-heading{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;margin:14px 0 18px}.workspace-heading h1{margin:0;font-size:clamp(1.8rem,3vw,2.7rem);line-height:1.05}.page-heading.compact{max-width:980px}.page-heading h1{margin:0;font-size:clamp(2rem,4vw,3.2rem);line-height:1.05;letter-spacing:0}.eyebrow{margin:0 0 6px;color:var(--plotline-accent-dark);font-size:.78rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.lead-text{max-width:760px;margin:10px 0 0;color:var(--plotline-muted);font-size:1.05rem}.button-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.button-row form,.compact-buttons form,.asset-card-footer form,td.text-end form,form[data-confirm-message]{display:inline-flex;align-items:center;margin:0}td.text-end{white-space:nowrap}.content-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}.plain-card,.empty-panel,.edit-panel,.list-section{background:var(--plotline-panel);border:1px solid var(--plotline-line);border-radius:8px;padding:20px;box-shadow:0 10px 24px rgba(30,37,43,.04)}.plain-card h2,.empty-panel h2,.list-section h2{margin-top:0;font-size:1.35rem}.plain-card p,.empty-panel p,.muted{color:var(--plotline-muted)}.edit-panel{max-width:980px}.trial-status-panel,.trial-dashboard-banner{border:1px solid var(--plotline-line);border-radius:8px;padding:18px 20px;background:var(--plotline-panel)}.trial-status-panel{display:grid;gap:12px;margin-bottom:18px}.trial-status-panel--signup{grid-template-columns:minmax(0,1fr) auto;align-items:center;max-width:980px}.trial-status-panel h2,.trial-status-panel h3,.trial-dashboard-banner h2{margin:0}.trial-status-panel p,.trial-dashboard-banner p{margin-bottom:0}.trial-status-panel__heading,.trial-dashboard-banner{display:flex;gap:16px;align-items:center;justify-content:space-between}.trial-dashboard-banner{margin-bottom:18px}.trial-dashboard-banner__body{min-width:0}.trial-dashboard-banner__actions{display:flex;flex:0 0 auto;flex-wrap:wrap;gap:8px}.trial-progress{height:10px;overflow:hidden;border-radius:999px;background:rgba(84,68,48,.12)}.trial-progress__bar{height:100%;border-radius:inherit}.trial-status-panel[hidden],.trial-dashboard-banner[hidden]{display:none}.location-page .page-heading.compact,.location-page .edit-panel,.location-page .empty-panel,.location-page .drag-drop-panel{max-width:none}.import-panel,.import-preview-section{max-width:none}.import-json-input{font-family:Consolas,"Liberation Mono",Menlo,monospace;font-size:.88rem;line-height:1.45;min-height:34rem;max-height:64vh;overflow:auto;resize:vertical;white-space:pre}.import-preview-heading,.import-counts{display:flex;align-items:flex-start;gap:12px}.import-preview-heading{justify-content:space-between;margin-bottom:16px}.import-counts{flex-wrap:wrap}.import-counts span{display:inline-flex;align-items:baseline;gap:5px;border:1px solid var(--plotline-line);border-radius:999px;padding:5px 11px;background:var(--plotline-soft);color:var(--plotline-muted);font-weight:700}.import-counts strong{color:var(--plotline-ink)}.import-tree{display:grid;gap:12px}.import-tree-book{border:1px solid var(--plotline-line);border-radius:8px;padding:14px;background:rgba(255,255,255,.54)}.import-tree-book>summary,.import-tree-chapter>summary{display:flex;align-items:center;justify-content:space-between;gap:12px;cursor:pointer}.import-tree-book>summary span,.import-tree-chapter>summary span{color:var(--plotline-muted);font-size:.84rem;font-weight:700;white-space:nowrap}.import-tree-book h3{margin:0 0 10px;font-size:1.08rem}.import-tree-chapter{border-top:1px solid var(--plotline-line);padding:10px 0 0;margin-top:10px}.import-tree-chapter ul{margin:8px 0 0;color:var(--plotline-muted)}.import-scene-meta{display:block;margin-top:2px;color:var(--plotline-muted);font-size:.82rem}.import-mapping-summary{display:grid;gap:6px;border:1px solid rgba(184,118,43,.28);border-radius:8px;padding:10px 12px;margin-bottom:14px;background:rgba(251,236,211,.5);color:var(--plotline-muted);font-size:.9rem}.import-duplicate-confirm{border:1px solid rgba(184,118,43,.34);border-radius:8px;padding:12px 12px 12px 38px;background:rgba(251,236,211,.72)}.import-confirmation,.import-docs details{border:1px solid var(--plotline-line);border-radius:8px;padding:16px;background:rgba(255,255,255,.42)}.import-confirmation h3,.import-doc-grid h3{margin-top:0;font-size:1.05rem}.import-docs summary{cursor:pointer;font-weight:800}.import-doc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:14px;margin-top:14px}.import-message-list ul{max-height:16rem;overflow:auto}.scene-editor{max-width:1180px}.table{margin-bottom:0}.table th{color:var(--plotline-muted);font-size:.78rem;letter-spacing:.06em;text-transform:uppercase}.narrow{width:90px;white-space:nowrap}.status-pill{display:inline-flex;align-items:center;width:fit-content;border:1px solid #c8d8d2;border-radius:999px;padding:2px 8px;color:var(--plotline-accent-dark);background:var(--plotline-soft);font-size:.78rem;font-weight:700;white-space:nowrap}.dashboard-summary-card{border-color:var(--plotline-line)}.dashboard-summary-body{padding:10px 12px}.dashboard-summary-label{margin-bottom:4px;font-size:.7rem;line-height:1.15}.dashboard-summary-value{display:block;font-size:1.45rem;line-height:1}.dashboard-summary-calm{border-color:rgba(47,111,99,.28);background:rgba(238,245,242,.62)}.dashboard-summary-mild{border-color:rgba(197,139,43,.32);background:rgba(255,248,232,.68)}.dashboard-summary-attention{border-color:rgba(142,80,103,.32);background:rgba(252,241,246,.7)}.dashboard-progress-card{border-color:rgba(47,111,99,.18);background:linear-gradient(180deg,#fff 0%,rgba(238,245,242,.64) 100%)}.dashboard-progress-body{padding:12px 14px}.dashboard-progress-heading{margin-bottom:8px}.dashboard-progress-title{margin:0 0 2px;font-size:1.05rem;line-height:1.2}.dashboard-series-progress{height:7px;background:rgba(47,111,99,.1)}.dashboard-series-progress .progress-bar{background:linear-gradient(90deg,var(--plotline-accent),#6fa99b)}.dashboard-progress-source{border-color:rgba(47,111,99,.2);color:var(--plotline-accent-dark);background:rgba(238,245,242,.9)}.dashboard-book-title-block h3{line-height:1.16}.dashboard-book-title-block a{color:var(--plotline-ink);text-decoration-color:rgba(47,111,99,.35)}.dashboard-book-subtitle{color:var(--plotline-muted);font-size:.95rem;line-height:1.25}.dashboard-status-pill{border-color:var(--dashboard-status-border,#c8d8d2);color:var(--dashboard-status-ink,var(--plotline-accent-dark));background:var(--dashboard-status-bg,var(--plotline-soft))}.dashboard-status-published{--dashboard-status-border:rgba(47,111,99,.32);--dashboard-status-bg:#e7f2ed;--dashboard-status-ink:#22534a}.dashboard-status-drafting{--dashboard-status-border:rgba(184,118,43,.35);--dashboard-status-bg:#fff2dd;--dashboard-status-ink:#7b4b12}.dashboard-status-planning{--dashboard-status-border:rgba(51,117,155,.3);--dashboard-status-bg:#e9f3f8;--dashboard-status-ink:#245b78}.dashboard-status-research,.dashboard-status-outlining{--dashboard-status-border:rgba(103,80,164,.26);--dashboard-status-bg:#f0edf8;--dashboard-status-ink:#51417c}.dashboard-status-revising{--dashboard-status-border:rgba(123,82,148,.3);--dashboard-status-bg:#f4eefa;--dashboard-status-ink:#5f3e73}.dashboard-status-editing{--dashboard-status-border:rgba(68,75,130,.3);--dashboard-status-bg:#eceef8;--dashboard-status-ink:#343b72}.dashboard-status-proofreading{--dashboard-status-border:rgba(43,130,123,.3);--dashboard-status-bg:#e6f5f2;--dashboard-status-ink:#23625d}.dashboard-status-ready{--dashboard-status-border:rgba(185,142,30,.34);--dashboard-status-bg:#fff6d8;--dashboard-status-ink:#765a0f}.dashboard-status-paused{--dashboard-status-border:rgba(102,113,123,.28);--dashboard-status-bg:#f0f2f1;--dashboard-status-ink:#56616b}.dashboard-status-archived{--dashboard-status-border:rgba(139,80,86,.28);--dashboard-status-bg:#f3eded;--dashboard-status-ink:#76474d}.dashboard-attention-soft{border-color:rgba(102,113,123,.24);color:#56616b;background:#f3f4f2}.order-buttons{display:flex;gap:6px}.purpose-list{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:8px;max-height:260px;overflow:auto;padding:10px;border:1px solid var(--plotline-line);border-radius:8px;background:#fcfcfb}.purpose-check{display:flex;align-items:center;gap:8px;font-size:.92rem}.timeline-filter{display:flex;align-items:center;gap:8px;min-width:min(420px,100%)}.workspace-layout{display:grid;grid-template-columns:minmax(0,1fr);gap:0;align-items:start;--timeline-inspector-width:min(40vw,560px)}.workspace-layout.inspector-drawer-open{grid-template-columns:minmax(420px,1fr) 10px minmax(420px,var(--timeline-inspector-width));gap:12px}.timeline-workspace{min-width:0;max-width:100%}.timeline-inspector-resizer{position:sticky;top:76px;z-index:25;width:10px;height:calc(100vh - 96px);border-radius:999px;cursor:col-resize;background:linear-gradient(180deg,rgba(47,111,99,.16),rgba(47,111,99,.04))}.timeline-inspector-resizer::before{content:"";display:block;width:2px;height:48px;margin:18px auto;border-radius:999px;background:var(--plotline-accent);opacity:.55}.timeline-inspector-resizer:hover,.timeline-inspector-resizer:focus-visible{background:rgba(47,111,99,.16)}body.timeline-inspector-resizing{cursor:col-resize;user-select:none}.pacing-panel{margin-top:14px;margin-bottom:0;border:1px solid var(--plotline-line);border-radius:8px;padding:14px;background:#fff;box-shadow:0 10px 24px rgba(30,37,43,.04)}.pacing-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:10px}.pacing-heading h2{margin:0;font-size:1.1rem}.metric-graph-stack{display:grid;gap:10px;max-width:100%;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;padding-bottom:4px}.metric-graph-row{display:grid;grid-template-columns:150px minmax(260px,1fr);gap:10px;align-items:center;min-width:max(100%,calc(150px + (var(--scene-count,1)*34px)))}.metric-graph-label{color:var(--plotline-muted);font-size:.84rem;font-weight:700}.metric-graph-points{display:grid;grid-template-columns:repeat(var(--scene-count),minmax(28px,1fr));gap:5px;align-items:end;min-height:52px;border-radius:8px;padding:6px;background:linear-gradient(180deg,#f6faf8 0%,#edf4f1 100%);min-width:calc(var(--scene-count)*34px)}.metric-point{display:flex;align-items:flex-end;justify-content:center;height:48px;min-width:28px;color:var(--plotline-accent-dark);text-decoration:none}.metric-point::before{content:"";display:block;width:100%;height:max(8px,var(--metric-height));border-radius:5px 5px 2px 2px;background:linear-gradient(180deg,#6fa99b,var(--plotline-accent-dark));box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}.metric-point span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}.timeline-shell{overflow-x:auto;padding:16px;border:1px solid var(--plotline-line);border-radius:8px;background:linear-gradient(180deg,#fff 0%,#f7faf8 100%)}.workspace-timeline{min-height:420px}.timeline-series-row{display:grid;grid-template-columns:var(--timeline-label-width) max-content;gap:var(--timeline-grid-gap);align-items:stretch;width:max-content;min-width:100%}.timeline-project-title{position:sticky;left:0;z-index:20;display:grid;align-content:start;gap:4px;width:var(--timeline-label-width);min-height:100%;border-radius:8px;padding:12px;color:#fff;background:var(--plotline-accent-dark);font-weight:700;box-shadow:8px 0 18px rgba(30,37,43,.08)}.timeline-project-title span,.timeline-book-title span{color:rgba(255,255,255,.78);font-size:.72rem;letter-spacing:.08em;text-transform:uppercase}.timeline-project-title strong,.timeline-book-title strong{line-height:1.15}.timeline-books{display:flex;gap:var(--timeline-grid-gap);align-items:stretch}.timeline-book{flex:0 0 auto;width:calc((var(--book-scene-count)*var(--timeline-scene-width)) + ((var(--book-scene-count) - 1)*var(--timeline-grid-gap)));min-width:var(--timeline-scene-width)}.timeline-book-title{display:grid;gap:2px;margin-bottom:var(--timeline-grid-gap);border-radius:8px;padding:10px 12px;color:#fff;background:var(--plotline-accent-dark);font-weight:700}.timeline-chapters{display:flex;gap:var(--timeline-grid-gap);align-items:flex-start}.timeline-chapter{flex:0 0 auto;width:calc((var(--chapter-scene-count)*var(--timeline-scene-width)) + ((var(--chapter-scene-count) - 1)*var(--timeline-grid-gap)));min-width:var(--timeline-scene-width);box-sizing:border-box;border-radius:8px;background:rgba(238,245,242,.7);outline:1px solid #d4ded8;outline-offset:0;overflow:visible}.timeline-chapter-title{display:grid;gap:2px;border-bottom:1px solid #d4ded8;padding:10px 12px;color:var(--plotline-accent-dark);font-weight:700}.timeline-chapter-title span{color:var(--plotline-muted);font-size:.76rem;text-transform:uppercase}.timeline-scenes{display:grid;grid-template-columns:repeat(var(--chapter-scene-count),minmax(0,1fr));gap:var(--timeline-grid-gap);width:100%;box-sizing:border-box;min-height:190px;padding:0}.timeline-scene-card,.timeline-empty-scene{display:flex;flex-direction:column;gap:6px;width:100%;min-width:0;box-sizing:border-box;border:1px solid var(--plotline-line);border-radius:8px;padding:12px;background:#fff;color:var(--plotline-ink);text-decoration:none;box-shadow:0 8px 18px rgba(30,37,43,.05)}.timeline-scene-card:hover{border-color:var(--plotline-accent);color:var(--plotline-ink)}.timeline-placeholder-scene,.timeline-placeholder-cell,.metric-point-placeholder{cursor:default;opacity:.72}.timeline-placeholder-scene{align-items:center;justify-content:center;border-style:dashed;color:var(--plotline-muted);background:rgba(255,255,255,.38);font-size:.86rem;font-weight:800;text-align:center}.timeline-placeholder-cell{border-style:dashed;background:repeating-linear-gradient(-45deg,rgba(216,221,216,.18) 0,rgba(216,221,216,.18) 7px,rgba(255,255,255,.18) 7px,rgba(255,255,255,.18) 14px)}.metric-point-placeholder::before{height:4px;background:rgba(102,113,123,.2);box-shadow:none}.timeline-scene-card.selected{border-color:var(--plotline-accent-dark);box-shadow:0 0 0 3px rgba(47,111,99,.16),0 10px 22px rgba(30,37,43,.08)}.scene-badge-row,.purpose-chip-row{display:flex;flex-wrap:wrap;gap:5px;align-items:center}.warning-placeholder{display:inline-flex;width:fit-content;border:1px dashed #d6b25e;border-radius:999px;padding:2px 8px;color:#755715;background:#fff8e8;font-size:.74rem;font-weight:700}.warning-placeholder.active{border-color:rgba(159,69,69,.36);color:#8a3434;background:rgba(255,232,232,.9);gap:5px;font-weight:800}.dependency-placeholder{display:inline-flex;width:fit-content;border:1px solid rgba(47,111,99,.2);border-radius:999px;padding:2px 8px;color:var(--plotline-accent-dark);background:rgba(47,111,99,.08);font-size:.74rem;font-weight:700}.purpose-chip{display:inline-flex;width:fit-content;border-radius:999px;padding:2px 7px;color:#38453e;background:#edf0eb;font-size:.72rem;font-weight:700}.future-lane-placeholder{border-top:1px dashed #cbd8d2;padding:8px 12px;color:var(--plotline-muted);font-size:.78rem;background:rgba(255,255,255,.45)}.metric-timeline-board{display:grid;gap:8px;width:max-content;min-width:100%;margin-top:14px;border-top:1px solid var(--plotline-line);padding-top:14px}.metric-shape-controls{position:sticky;left:0;z-index:6;display:grid;gap:8px;width:calc(var(--timeline-label-width) + (var(--scene-count)*var(--timeline-scene-width)) + (var(--scene-count)*var(--timeline-grid-gap)));min-width:100%;border-radius:8px;padding:10px;background:rgba(255,255,255,.88)}.metric-timeline-row{display:grid;grid-template-columns:var(--timeline-label-width) calc((var(--scene-count)*var(--timeline-scene-width)) + ((var(--scene-count) - 1)*var(--timeline-grid-gap)));gap:var(--timeline-grid-gap);align-items:stretch}.metric-lane-label{position:sticky;left:0;z-index:12;display:grid;align-content:start;gap:4px;min-height:320px;border:1px solid rgba(47,111,99,.18);border-radius:8px;padding:10px;color:var(--plotline-accent-dark);background:#fff;box-shadow:0 6px 14px rgba(30,37,43,.04)}.metric-lane-label span{color:var(--plotline-muted);font-size:.78rem;font-weight:600}.colour-swatch{display:inline-block;width:18px;height:18px;border:1px solid rgba(30,37,43,.18);border-radius:999px;vertical-align:middle}.plot-lane-board{position:relative;display:grid;gap:8px;width:max-content;min-width:100%;margin-top:14px;border-top:1px solid var(--plotline-line);padding-top:14px}.plot-lane-svg-overlay{position:absolute;inset:0 auto auto 0;z-index:4;pointer-events:none;overflow:visible}.plot-lane-svg-health-overlay{z-index:8}.plot-line-svg-path{fill:none;stroke-width:3.5;stroke-linecap:round;opacity:.72}.plot-line-svg-segment-dormant{stroke-dasharray:9 8;opacity:.48}.plot-line-svg-connector{fill:none;stroke-width:3;stroke-linecap:round;opacity:.58}.plot-line-svg-path.plotline-state-resolved,.plot-line-svg-path.plotline-state-questionable{stroke:#8c9491;opacity:.42}.plot-line-svg-path.plotline-state-abandoned{stroke:#9b2f36;opacity:.5}.plot-event-svg-node.plotline-state-resolved,.plot-event-svg-node.plotline-state-questionable{opacity:.88}.plot-event-svg-node.plotline-state-abandoned{opacity:.9}.plot-event-svg-node.plotline-state-abandoned line{stroke:#fff}.plotline-health-marker path,.plotline-health-marker circle{fill:#f4c430;stroke:#8a6712;stroke-width:1.5}.plotline-health-marker{pointer-events:visiblePainted}.plotline-health-marker line,.plotline-health-marker text{stroke:#5e4710;fill:#5e4710;stroke-width:1.6;stroke-linecap:round}.plotline-health-marker text{font-size:11px;font-weight:800;stroke-width:0;pointer-events:none}.plotline-health-count{fill:#fff;stroke:#8a6712;stroke-width:2;paint-order:stroke}.plotline-health-marker-dangling path,.plotline-health-marker-dangling circle{fill:#f8df8f}.plotline-health-marker-abandoned path,.plotline-health-marker-abandoned circle{fill:#d85c63;stroke:#8b1e2d}.plotline-health-marker-abandoned line{stroke:#fff}.plot-event-svg-node circle,.plot-event-svg-node ellipse,.plot-event-svg-node path{stroke:#fff;stroke-width:2}.plot-event-svg-node line,.plot-event-svg-node path[fill="none"]{stroke:#8b1e2d;stroke-width:3.4;stroke-linecap:round;stroke-linejoin:round}.plot-event-svg-node-reveal ellipse{stroke-width:2.4}.plot-event-svg-label{font-size:9.5px;font-weight:900;font-family:system-ui,-apple-system,"Segoe UI",sans-serif;paint-order:stroke;stroke:rgba(30,37,43,.22);stroke-width:.8px;pointer-events:none}.plot-event-svg-node-resolve path[fill="none"]{stroke:#fff}.lane-board-heading,.metric-lane-board-heading,.plot-lane-board-heading{position:sticky;left:0;z-index:6;display:flex;align-items:center;justify-content:space-between;gap:14px;width:calc(var(--timeline-label-width) + (var(--scene-count)*var(--timeline-scene-width)) + (var(--scene-count)*var(--timeline-grid-gap)));min-width:100%;margin-bottom:2px;border-radius:8px;padding:8px 10px;color:var(--plotline-accent-dark);background:rgba(255,255,255,.92);font-weight:700;box-shadow:0 8px 16px rgba(30,37,43,.05)}.lane-board-heading small,.metric-lane-board-heading small,.plot-lane-board-heading small{color:var(--plotline-muted);font-weight:500;white-space:nowrap}.timeline-heading-with-help{display:inline-flex;align-items:center;gap:6px;min-width:0}.plot-lane-row{display:grid;grid-template-columns:var(--timeline-label-width) repeat(var(--scene-count),var(--timeline-scene-width));gap:var(--timeline-grid-gap);align-items:stretch}.plot-lane-label{position:sticky;left:0;z-index:12;display:flex;align-items:center;gap:8px;min-height:46px;border:1px solid color-mix(in srgb,var(--plot-colour),#fff 55%);border-left:5px solid var(--plot-colour);border-radius:8px;padding:8px 10px;background:#fff;color:var(--plotline-ink);text-decoration:none;box-shadow:0 6px 14px rgba(30,37,43,.04)}.plot-lane-label:hover{color:var(--plotline-ink);border-color:var(--plot-colour)}.plot-lane-slots{display:contents}.plot-lane-slot{display:flex;flex-wrap:wrap;align-content:center;align-items:center;justify-content:center;gap:5px;min-height:46px;border:1px dashed #d8ddd8;border-radius:8px;padding:7px;background:rgba(255,255,255,.62)}.thread-marker{position:relative;z-index:5;display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:34px;height:30px;border:2px solid color-mix(in srgb,var(--plot-colour),#000 20%);border-radius:999px;padding:0 8px;color:var(--plotline-ink);background:color-mix(in srgb,var(--plot-colour),#fff 66%);font-size:.74rem;font-weight:900;text-decoration:none;box-shadow:0 3px 8px rgba(30,37,43,.14)}.plot-lane-board [data-plot-event-node]{opacity:1}.thread-marker:hover,.thread-marker:focus-visible{color:var(--plotline-ink);background:color-mix(in srgb,var(--plot-colour),#fff 48%);outline:3px solid rgba(47,111,99,.22);outline-offset:2px}.thread-marker.resolved{border-color:#5a8c60;background:#e6f4e7;color:#24512b}.thread-marker.contradicted{border-color:#b45c5c;background:#fff0f0;color:#7a2222}.thread-events-section{margin-top:16px}.thread-event-list{display:grid;gap:8px;margin-bottom:12px}.thread-event-item{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;border:1px solid color-mix(in srgb,var(--plot-colour),#fff 55%);border-left:4px solid var(--plot-colour);border-radius:8px;padding:8px;background:#fff}.thread-event-item>div{display:grid;gap:3px}.thread-event-marker{display:inline-flex;align-items:center;justify-content:center;width:fit-content;min-width:24px;height:22px;border-radius:999px;padding:0 7px;background:color-mix(in srgb,var(--plot-colour),#fff 72%);font-size:.72rem;font-weight:800}.thread-event-create{border-top:1px solid var(--plotline-line);padding-top:12px}.asset-lane-board{display:grid;gap:8px;width:max-content;min-width:100%;margin-top:14px;border-top:1px solid var(--plotline-line);padding-top:14px}.asset-lane-row{display:grid;grid-template-columns:var(--timeline-label-width) repeat(var(--scene-count),var(--timeline-scene-width));gap:var(--timeline-grid-gap);align-items:stretch}.asset-lane-label{position:sticky;left:0;z-index:12;display:grid;gap:2px;min-height:46px;border:1px solid #d4d7df;border-left:5px solid #6b5b95;border-radius:8px;padding:8px 10px;background:#fff;color:var(--plotline-ink);text-decoration:none;box-shadow:0 6px 14px rgba(30,37,43,.04)}.asset-lane-label span{color:var(--plotline-muted);font-size:.75rem}.asset-lane-slots{display:contents}.asset-lane-slot{display:flex;flex-wrap:wrap;align-content:flex-start;gap:5px;min-height:46px;border:1px dashed #d8ddd8;border-radius:8px;padding:7px;background:rgba(250,249,255,.72)}.asset-marker,.asset-event-marker{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:24px;border:1px solid #7f70aa;border-radius:7px;padding:0 7px;color:#33284e;background:#eee9fb;font-size:.72rem;font-weight:800;text-decoration:none}.asset-marker:hover{color:#33284e;background:#dfd6f7}.asset-marker.timeline-marker-story-turn,.character-marker.timeline-marker-story-turn{border-width:2px}.asset-marker.timeline-marker-important,.character-marker.timeline-marker-important{box-shadow:inset 0 -3px 0 rgba(197,139,43,.36)}.asset-marker.timeline-marker-state-change,.character-marker.timeline-marker-state-change{border-style:double}.asset-marker.timeline-marker-transfer{box-shadow:inset 3px 0 0 rgba(47,111,99,.32)}.character-marker.timeline-marker-exit{opacity:.86;border-style:dashed}.asset-marker.timeline-marker-has-warning,.character-marker.timeline-marker-has-warning{box-shadow:0 0 0 2px rgba(179,95,59,.22)}.asset-events-section{margin-top:16px}.asset-event-list,.asset-mini-list{display:grid;gap:8px;margin-bottom:12px}.asset-event-item,.asset-mini-item{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;border:1px solid #ded9ea;border-left:4px solid #6b5b95;border-radius:8px;padding:8px;background:#fff}.asset-event-item>div,.asset-mini-item>div{display:grid;gap:3px}.asset-create-form{border-top:1px solid var(--plotline-line);padding-top:12px}.asset-card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px}.asset-card{display:grid;gap:12px;border:1px solid var(--plotline-line);border-radius:8px;padding:14px;background:#fff;box-shadow:0 8px 18px rgba(30,37,43,.04)}.asset-card h2{margin:0;font-size:1.1rem}.asset-card-footer{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.asset-detail-grid{display:grid;grid-template-columns:minmax(260px,.8fr) minmax(320px,1.2fr);gap:14px}.character-lane-board{display:grid;gap:8px;width:max-content;min-width:100%;margin-top:14px;border-top:1px solid var(--plotline-line);padding-top:14px}.character-lane-row{display:grid;grid-template-columns:var(--timeline-label-width) repeat(var(--scene-count),var(--timeline-scene-width));gap:var(--timeline-grid-gap);align-items:stretch}.character-lane-label{position:sticky;left:0;z-index:12;display:grid;gap:2px;min-height:46px;border:1px solid #d7d4ce;border-left:5px solid #8a6f3d;border-radius:8px;padding:8px 10px;background:#fff;color:var(--plotline-ink);text-decoration:none;box-shadow:0 6px 14px rgba(30,37,43,.04)}.character-lane-label span{color:var(--plotline-muted);font-size:.75rem}.character-lane-slots{display:contents}.character-lane-slot{display:flex;flex-wrap:wrap;align-content:flex-start;gap:5px;min-height:46px;border:1px dashed #d8ddd8;border-radius:8px;padding:7px;background:rgba(255,252,246,.72)}.character-marker{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:24px;border:1px solid #9a7a3f;border-radius:999px;padding:0 7px;color:#4f3810;background:#fff2d7;font-size:.72rem;font-weight:800;text-decoration:none}.character-card{border-left:4px solid #8a6f3d}.character-section{margin-top:16px}.character-item{border-left-color:#8a6f3d}.character-appearance-card{display:grid;gap:8px;border:1px solid #d7d4ce;border-left:4px solid #8a6f3d;border-radius:8px;padding:10px;background:#fff}.character-appearance-summary{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.character-appearance-summary>div:first-child{display:grid;gap:3px}.character-appearance-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}.character-appearance-edit .asset-create-form{margin-top:8px;border-top:1px solid var(--plotline-line)}.form-helper-message{margin:0 0 8px;font-size:.86rem;font-weight:700}.scene-number,.time-label{color:var(--plotline-muted);font-size:.82rem}.location-pill,.location-badge{display:inline-flex;align-items:center;width:fit-content;border:1px solid rgba(47,111,99,.18);border-radius:999px;padding:2px 7px;color:var(--plotline-accent-dark);background:rgba(47,111,99,.08);font-size:.74rem;font-weight:700}.scene-summary{display:-webkit-box;overflow:hidden;color:var(--plotline-muted);font-size:.88rem;-webkit-box-orient:vertical;-webkit-line-clamp:4}.timeline-empty-scene{justify-content:center;color:var(--plotline-muted);background:rgba(255,255,255,.58)}.form-control:focus,.form-select:focus,.form-check-input:focus{border-color:var(--plotline-accent);box-shadow:0 0 0 .2rem rgba(47,111,99,.16)}.scene-inspector-panel{position:sticky;top:76px;display:flex;flex-direction:column;max-height:calc(100vh - 96px);min-width:0;overflow:hidden;border:1px solid var(--plotline-line);border-radius:8px;background:#fff;box-shadow:0 14px 32px rgba(30,37,43,.08)}.inspector-header{position:sticky;top:0;z-index:5;display:flex;align-items:flex-start;justify-content:space-between;gap:12px;border-bottom:1px solid var(--plotline-line);padding:16px;background:#fff}.scene-inspector-body{flex:1 1 auto;min-height:0;overflow:auto;padding:16px}.inspector-header h2{margin:0;font-size:1.25rem}.scene-inspector-form{display:grid;gap:14px}.inspector-section{border-top:1px solid var(--plotline-line);padding-top:12px}.inspector-section:first-of-type{border-top:0;padding-top:0}.inspector-section h3{margin:0 0 10px;font-size:.95rem;color:var(--plotline-accent-dark)}.location-helper{border:1px solid rgba(138,111,61,.22);border-radius:8px;padding:10px;background:rgba(255,248,232,.74)}.location-warning-list{margin:0;padding-left:18px;color:#6f5526;font-size:.86rem}.location-tree{display:grid;gap:8px}.location-tree-row{display:flex;align-items:center;justify-content:space-between;gap:12px;border-bottom:1px solid var(--plotline-line);padding:10px 0 10px calc(var(--location-depth,0)*22px)}.location-tree-row:last-child{border-bottom:0}.location-tree-row>div:first-child{display:grid;gap:3px}.compact-buttons{flex-wrap:nowrap}.location-chip-list{display:flex;flex-wrap:wrap;gap:8px}.location-chip{border:1px solid var(--plotline-line);border-radius:8px;padding:7px 10px;background:#fff;text-decoration:none}.location-relationship-list{display:grid;gap:8px}.location-relationship-card{display:flex;justify-content:space-between;gap:12px;border:1px solid var(--plotline-line);border-radius:8px;padding:10px;background:#fff}.location-flags{display:flex;flex-wrap:wrap;gap:10px 16px;color:var(--plotline-muted);font-size:.86rem}.validation-summary,.warning-filter-panel{border:1px solid var(--plotline-line);border-radius:8px;margin-bottom:16px;padding:12px;background:#fff}.validation-summary{display:flex;flex-wrap:wrap;gap:12px;color:var(--plotline-muted)}.warning-dashboard-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;margin-bottom:18px}.warning-count-row,.warning-card-title,.inspector-section-heading{display:flex;align-items:center;justify-content:space-between;gap:10px}.warning-list,.warning-mini-list{display:grid;gap:10px}.warning-card,.warning-mini-card{border:1px solid var(--plotline-line);border-radius:8px;padding:12px;background:#fff}.warning-card{display:grid;grid-template-columns:1fr auto;gap:12px}.warning-card p,.warning-mini-card p{margin:6px 0}.warning-actions{display:flex;align-items:flex-start;flex-wrap:wrap;gap:6px}.severity-pill{display:inline-flex;align-items:center;border-radius:999px;padding:2px 8px;font-size:.74rem;font-weight:800}.severity-pill.info{color:#365f7c;background:rgba(107,153,184,.14)}.severity-pill.warning{color:#7a5527;background:rgba(205,154,72,.17)}.severity-pill.error{color:#8a3434;background:rgba(190,83,83,.15)}.warning-mini-card{background:rgba(255,255,255,.76)}.dependency-columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.dependency-card{display:grid;gap:3px;border:1px solid var(--plotline-line);border-radius:8px;margin-bottom:8px;padding:9px;background:rgba(255,255,255,.78)}.move-preview-panel{max-width:900px}.focused-validation-summary{display:flex;flex-wrap:wrap;gap:10px;border:1px solid rgba(47,111,99,.16);border-radius:8px;margin-top:12px;padding:10px;color:var(--plotline-muted);background:rgba(47,111,99,.05)}.inspector-purpose-list{grid-template-columns:1fr;max-height:210px}.metric-slider-stack{display:grid;gap:12px}.metric-slider{display:grid;gap:4px}.metric-slider-label{display:flex;justify-content:space-between;gap:10px;color:var(--plotline-muted);font-size:.86rem;font-weight:700}.metric-slider-label output{color:var(--plotline-accent-dark)}.metric-slider .form-range{accent-color:var(--plotline-accent)}.asset-importance-slider{border:1px solid var(--plotline-line);border-radius:8px;padding:10px 12px;background:rgba(47,111,99,.05)}.story-asset-checkbox-field{margin-top:1.95rem}.story-asset-event-item{justify-content:flex-start;gap:8px}.story-asset-event-item .asset-event-marker{flex:0 0 auto;min-width:1.55rem;width:auto}.story-asset-event-details{min-width:0}.scene-inspector-root{display:grid;gap:12px}.scene-overview-card{display:grid;gap:10px;border:1px solid rgba(47,111,99,.18);border-radius:10px;padding:12px;background:linear-gradient(180deg,rgba(47,111,99,.08),rgba(255,255,255,.88))}.scene-overview-card h3{margin:0;font-size:1.05rem}.scene-overview-card .muted{display:flex;flex-wrap:wrap;gap:7px;margin:0}.overview-chip-grid,.context-nav,.breadcrumb-trail,.inspector-nav-links{display:flex;flex-wrap:wrap;gap:7px}.overview-chip,.context-nav a,.breadcrumb-trail a,.breadcrumb-trail span{display:inline-flex;align-items:center;border:1px solid var(--plotline-line);border-radius:999px;padding:4px 9px;color:var(--plotline-muted);font-size:.78rem;font-weight:800;text-decoration:none;background:rgba(255,255,255,.72)}.overview-chip.attention{color:#7a5527;border-color:rgba(205,154,72,.35);background:rgba(205,154,72,.14)}.overview-outcome{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;margin:0;color:var(--plotline-muted)}.inspector-quick-nav{position:sticky;top:0;z-index:5;display:grid;gap:8px;border:1px solid var(--plotline-line);border-radius:10px;padding:10px;background:rgba(255,255,255,.94);box-shadow:0 10px 24px rgba(55,48,39,.08);backdrop-filter:blur(8px)}.inspector-nav-links a{border:1px solid var(--plotline-line);border-radius:999px;padding:4px 8px;color:var(--plotline-muted);font-size:.76rem;font-weight:800;text-decoration:none}.inspector-nav-links a:hover,.inspector-nav-links a.active{color:var(--plotline-accent-dark);border-color:rgba(47,111,99,.35);background:rgba(47,111,99,.08)}.inspector-accordion-toggle{display:flex;align-items:center;justify-content:space-between;width:100%;border:0;padding:0;color:var(--plotline-ink);font-size:.95rem;font-weight:900;text-align:left;background:transparent}.inspector-accordion-toggle::before{content:"+";display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:999px;margin-right:7px;color:var(--plotline-accent-dark);background:rgba(47,111,99,.09)}.inspector-section.is-open>.inspector-accordion-toggle::before{content:"-"}.inspector-accordion-toggle span:first-child{margin-right:auto}.accordion-count{color:var(--plotline-muted);font-size:.75rem}.inspector-subheading{margin:14px 0 8px;color:var(--plotline-muted);font-size:.88rem;font-weight:900}.inspector-accordion-body{display:none;margin-top:12px}.inspector-section.is-open>.inspector-accordion-body{display:block}.inspector-section.is-filtered-out{display:none}.breadcrumb-trail{margin-bottom:12px}.breadcrumb-trail a:hover,.context-nav a:hover{color:var(--plotline-accent-dark);border-color:rgba(47,111,99,.35);background:rgba(47,111,99,.08)}.context-header,.working-context-banner{display:flex;align-items:center;justify-content:space-between;gap:14px;border:1px solid var(--plotline-line);border-radius:10px;margin-bottom:16px;padding:10px 12px;background:rgba(255,255,255,.84);box-shadow:0 10px 24px rgba(55,48,39,.05)}.context-title{display:grid;gap:2px}.context-title strong{color:var(--plotline-ink)}.health-kpi-grid,.analytics-grid-two,.analytics-grid-three{display:grid;gap:14px;margin:18px 0}.health-kpi-grid{grid-template-columns:repeat(4,minmax(0,1fr))}.health-kpi-grid .plain-card{display:grid;gap:4px}.health-kpi-grid strong{color:var(--plotline-ink);font-size:2rem;line-height:1}.analytics-grid-two{grid-template-columns:repeat(2,minmax(0,1fr))}.analytics-grid-three{grid-template-columns:repeat(3,minmax(0,1fr))}.analytics-section{border:1px solid var(--plotline-line);border-radius:10px;margin:18px 0;padding:16px;background:rgba(255,255,255,.84);box-shadow:0 14px 34px rgba(55,48,39,.07)}.analytics-section-heading{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin-bottom:14px}.analytics-section-heading h2,.plain-card h3{margin:0}.analytics-metric-stack{display:grid;gap:12px;overflow-x:auto;padding-bottom:4px}.metric-shape-chart{display:grid;gap:12px}.metric-shape-intro{max-width:760px;margin:0;color:var(--plotline-muted)}.metric-toggle-row{display:flex;flex-wrap:wrap;gap:8px}.metric-toggle-pill{display:inline-flex;align-items:center;gap:6px;border:1px solid rgba(82,75,65,.16);border-radius:999px;padding:5px 9px;color:var(--plotline-muted);font-size:.78rem;font-weight:800;background:rgba(255,255,255,.78);cursor:pointer}.metric-toggle-pill::before{content:"";width:10px;height:10px;border-radius:999px;background:var(--metric-colour)}.metric-toggle-pill input{margin:0;accent-color:var(--metric-colour)}.timeline-metric-edit-toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:end}.timeline-metric-edit-select{display:grid;gap:3px;min-width:180px;color:var(--plotline-muted);font-size:.78rem;font-weight:800}.timeline-metric-edit-status{align-self:center;border:1px solid rgba(47,111,99,.18);border-radius:999px;padding:5px 9px;color:var(--plotline-muted);font-size:.78rem;font-weight:800;background:rgba(255,255,255,.72)}.timeline-metric-edit-hint{font-size:.82rem}.metric-chart-frame canvas.metric-curve-editing{cursor:ns-resize}.metric-chart-frame{position:relative;min-height:320px;border:1px solid var(--plotline-line);border-radius:10px;padding:14px 12px 32px;background:linear-gradient(180deg,rgba(255,255,255,.92),rgba(247,245,240,.76))}.metric-timeline-frame{width:calc((var(--scene-count)*var(--timeline-scene-width)) + ((var(--scene-count) - 1)*var(--timeline-grid-gap)))}.metric-chart-frame canvas{width:100%;height:300px}.metric-timeline-board .metric-graph-stack{width:max-content;min-width:100%}.metric-timeline-board .metric-graph-row{grid-template-columns:var(--timeline-label-width) repeat(var(--scene-count),var(--timeline-scene-width));gap:var(--timeline-grid-gap);min-width:100%}.metric-timeline-board .metric-graph-points{display:contents}.metric-timeline-board .metric-point{border:1px solid rgba(47,111,99,.12);border-radius:8px;background:rgba(255,255,255,.62)}.metric-raw-details{border-top:1px solid var(--plotline-line);padding-top:8px}.metric-raw-details summary{color:var(--plotline-muted);font-size:.82rem;font-weight:800;cursor:pointer}.timeline-control-panel{display:grid;gap:12px;border:1px solid var(--plotline-line);border-radius:10px;margin-bottom:16px;padding:12px;background:rgba(255,255,255,.84);box-shadow:0 10px 24px rgba(55,48,39,.05)}.timeline-filter-drawer summary{color:var(--plotline-ink);font-weight:900;cursor:pointer}.timeline-filter-grid{display:grid;grid-template-columns:repeat(6,minmax(130px,1fr));gap:10px;margin-top:12px}.timeline-filter-checks,.timeline-filter-actions,.timeline-visibility-panel,.timeline-preset-panel,.preset-chip-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.timeline-filter-checks{grid-column:span 3;color:var(--plotline-muted);font-size:.82rem;font-weight:700}.timeline-filter-actions{grid-column:span 3;justify-content:flex-end}.timeline-visibility-panel label,.preset-chip{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--plotline-line);border-radius:999px;padding:4px 8px;color:var(--plotline-muted);font-size:.78rem;font-weight:800;background:rgba(255,255,255,.76)}.timeline-preset-panel form{display:inline-flex;align-items:center;gap:6px}.preset-chip a{color:var(--plotline-accent-dark);text-decoration:none}.preset-chip button{border:0;color:var(--plotline-muted);background:transparent;font-weight:900}.timeline-focus-banner{display:flex;align-items:center;justify-content:space-between;gap:12px;border:1px solid rgba(47,111,99,.22);border-radius:10px;margin-bottom:16px;padding:10px 12px;background:rgba(47,111,99,.08)}.thread-marker-icon{display:inline-flex;align-items:center;justify-content:center;min-width:16px;font-size:.78rem;font-weight:900}.thread-marker-type-start .thread-marker-icon,.thread-marker-type-resolve .thread-marker-icon,.thread-marker-type-abandon .thread-marker-icon,.thread-marker-type-twist .thread-marker-icon{font-size:.9rem}.thread-marker-type-start{border-radius:8px 999px 999px 8px}.thread-marker-type-twist{border-color:#b8762b;background:#fff4dc}.thread-marker-type-branch,.thread-marker-type-split,.thread-marker-type-merge{border-color:#6750a4;background:#f0ebff}.thread-marker-type-reveal .thread-marker-icon{border:1px solid currentColor;border-radius:999px;font-size:0}.thread-marker-type-reveal .thread-marker-icon::after{content:"";width:4px;height:4px;border-radius:999px;background:currentColor}.timeline-navigation-card{display:grid;grid-template-areas:"icon copy" "icon actions";grid-template-columns:auto minmax(0,620px);gap:8px 14px;align-items:center;justify-content:center;border:1px solid rgba(47,111,99,.42);border-left:5px solid var(--plotline-accent);border-radius:8px;margin:0 0 12px;padding:14px 16px;max-height:280px;overflow:hidden;color:var(--plotline-ink);background:linear-gradient(135deg,rgba(238,245,242,.98),rgba(255,255,255,.94));box-shadow:0 10px 26px rgba(30,37,43,.08);opacity:1;transform:translateY(0);transition:opacity 1.15s ease,max-height 1.2s ease,margin 1.2s ease,padding 1.2s ease,transform 1.15s ease}.timeline-navigation-card[hidden]{display:none}.timeline-navigation-card.is-dismissing{margin-bottom:0;padding-top:0;padding-bottom:0;max-height:0;opacity:0;transform:translateY(-6px)}.timeline-navigation-card-icon{grid-area:icon;display:grid;place-items:center;width:42px;height:42px;border-radius:50%;color:#fff;background:var(--plotline-accent);font-size:1.25rem;font-weight:900}.timeline-navigation-card-copy{grid-area:copy;text-align:center}.timeline-navigation-card-copy h2{margin:0 0 6px;color:var(--plotline-accent-dark);font-size:1rem;font-weight:900}.timeline-navigation-card-copy p{margin:2px 0;color:var(--plotline-ink);font-size:.9rem}.timeline-navigation-card-actions{grid-area:actions;display:flex;flex-wrap:wrap;gap:8px;justify-content:center}.timeline-horizontal-overflow{transition:box-shadow .18s ease}.timeline-horizontal-overflow.has-scroll-left{box-shadow:inset 18px 0 18px -20px rgba(30,37,43,.55)}.timeline-horizontal-overflow.has-scroll-right{box-shadow:inset -18px 0 18px -20px rgba(30,37,43,.55)}.timeline-horizontal-overflow.has-scroll-left.has-scroll-right{box-shadow:inset 18px 0 18px -20px rgba(30,37,43,.55),inset -18px 0 18px -20px rgba(30,37,43,.55)}[data-theme="dark"] .timeline-navigation-card{border-color:rgba(111,169,155,.52);border-left-color:#6fa99b;color:#f4eadc;background:linear-gradient(135deg,rgba(34,83,74,.72),rgba(32,31,34,.96));box-shadow:0 12px 28px rgba(0,0,0,.28)}[data-theme="dark"] .timeline-navigation-card-copy h2,[data-theme="dark"] .timeline-navigation-card-copy p{color:#f4eadc}[data-theme="dark"] .timeline-horizontal-overflow.has-scroll-left{box-shadow:inset 18px 0 18px -20px rgba(244,234,220,.55)}[data-theme="dark"] .timeline-horizontal-overflow.has-scroll-right{box-shadow:inset -18px 0 18px -20px rgba(244,234,220,.55)}[data-theme="dark"] .timeline-horizontal-overflow.has-scroll-left.has-scroll-right{box-shadow:inset 18px 0 18px -20px rgba(244,234,220,.55),inset -18px 0 18px -20px rgba(244,234,220,.55)}@media(prefers-reduced-motion:reduce){.timeline-navigation-card{transition:opacity .12s ease}}.timeline-scene-card.filter-dim,.timeline-scene-card.focus-dim,.lane-dim{opacity:.35}.timeline-scene-card.focus-match,.timeline-scene-card.filter-match.focus-match{outline:3px solid rgba(47,111,99,.32);box-shadow:0 12px 26px rgba(47,111,99,.16)}.timeline-section-hidden{display:none!important}.hide-scene-cards .timeline-scene-card{display:none}.timeline-control-panel{position:sticky;top:56px;z-index:18}.timeline-control-select{padding-right:6px}.timeline-control-select .form-select{width:auto;min-width:132px;border:0;padding-top:2px;padding-bottom:2px;background-color:transparent;color:var(--plotline-accent-dark);font-weight:800}.timeline-jump-panel{display:grid;grid-template-columns:auto minmax(180px,1.2fr) minmax(180px,1fr) minmax(170px,.9fr) minmax(180px,1fr) auto;gap:8px;align-items:center;width:100%}.timeline-shortcut-hint{color:var(--plotline-muted);font-size:.76rem;font-weight:700;white-space:nowrap}.timeline-minimap{display:flex;align-items:stretch;gap:4px;margin:0 0 12px;padding:8px;max-width:100%;min-width:0;overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-gutter:stable;border:1px solid rgba(47,111,99,.18);border-radius:8px;background:rgba(255,255,255,.72)}.timeline-minimap button{position:relative;flex:0 0 max(42px,calc(var(--chapter-scenes)*18px));min-width:42px;min-height:34px;border:1px solid rgba(47,111,99,.18);border-radius:7px;color:var(--plotline-accent-dark);background:linear-gradient(180deg,#f8fbf9 0%,#edf4f1 100%);font-size:.72rem;font-weight:800}.timeline-minimap button.is-active{border-color:var(--plotline-accent);color:#fff;background:linear-gradient(180deg,var(--plotline-accent) 0%,var(--plotline-accent-dark) 100%);box-shadow:0 0 0 2px rgba(47,111,99,.14)}.timeline-minimap button::after{content:"";position:absolute;right:5px;bottom:5px;width:calc(var(--chapter-warnings)*4px);max-width:28px;height:4px;border-radius:999px;background:#b45c5c;opacity:.75}.timeline-drag-palette,.drag-drop-panel{border:1px solid rgba(47,111,99,.18);border-radius:10px;margin-bottom:12px;padding:12px;background:rgba(255,255,255,.84);box-shadow:0 10px 24px rgba(55,48,39,.05)}.timeline-drag-palette{display:grid;grid-template-columns:minmax(220px,.35fr) minmax(0,1fr);gap:12px;align-items:center}.drag-chip-group{display:flex;flex-wrap:wrap;gap:6px;max-height:104px;overflow:auto;padding-right:4px}.drag-chip{display:inline-flex;align-items:center;border:1px solid var(--plotline-line);border-radius:999px;padding:4px 9px;background:#fff;color:var(--plotline-ink);font-size:.78rem;font-weight:800;cursor:grab}.drag-chip.character{border-color:#dacb9e;background:#fff8e8}.drag-chip.asset{border-color:#ded9ea;background:#f4f0ff}.drag-chip.location{border-color:rgba(47,111,99,.18);background:#eef8f4}.is-dragging{opacity:.55}.drop-target-active{outline:3px solid rgba(47,111,99,.28);outline-offset:2px}.timeline-chapter.chapter-drop-active,.timeline-scenes.drop-chapter-end{background:rgba(47,111,99,.06)}.timeline-scenes.drop-chapter-end::after{content:"Drop scene at end of chapter";display:flex;align-items:center;justify-content:center;min-width:var(--timeline-scene-width);min-height:74px;border:2px dashed rgba(197,139,43,.48);border-radius:12px;color:#7a5527;background:rgba(255,248,232,.82);font-size:.8rem;font-weight:900}.timeline-scene-card.drop-before{box-shadow:inset 5px 0 0 #c58b2b,0 12px 26px rgba(197,139,43,.16)}.timeline-scene-card.drop-after{box-shadow:inset -5px 0 0 #c58b2b,0 12px 26px rgba(197,139,43,.16)}.drag-drop-panel{position:sticky;top:132px;z-index:19}.drag-drop-panel-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.drag-drop-panel h2{margin:0;font-size:1.08rem}.drag-drop-form{border-top:1px solid var(--plotline-line);margin-top:10px;padding-top:10px}.scene-move-modal-content{border:1px solid rgba(47,111,99,.18);border-radius:14px;background:var(--plotline-surface);color:var(--plotline-ink);box-shadow:0 24px 70px rgba(55,48,39,.22)}.scene-move-modal-content .modal-header,.scene-move-modal-content .modal-footer{border-color:var(--plotline-line)}.scene-move-modal-content .modal-title{margin:0;color:var(--plotline-ink);font-size:1.2rem;font-weight:900}.scene-move-summary{margin:0 0 12px;color:var(--plotline-ink);font-size:1rem;font-weight:800}[data-theme="dark"] .scene-move-modal-content{background:#201f22;color:#f4eadc;border-color:rgba(244,234,220,.16);box-shadow:0 24px 70px rgba(0,0,0,.55)}[data-theme="dark"] .scene-move-modal-content .modal-title,[data-theme="dark"] .scene-move-summary{color:#f4eadc}[data-theme="dark"] .scene-move-modal-content .modal-header,[data-theme="dark"] .scene-move-modal-content .modal-footer{border-color:rgba(244,234,220,.14)}[data-theme="dark"] .scene-move-modal-content .btn-close{filter:invert(1) grayscale(100%)}.plain-preview{border:1px solid rgba(197,139,43,.28);border-radius:8px;margin:10px 0 0;padding:8px 10px;color:#5e4518;background:#fff8e8;font-size:.86rem;font-weight:800}.drag-check-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px 10px;color:var(--plotline-muted);font-size:.82rem;font-weight:700}.timeline-section-header,.lane-board-heading,.pacing-heading{position:sticky;left:0;z-index:6}.timeline-section-header{display:flex;align-items:center;justify-content:space-between;gap:10px;width:calc(var(--timeline-label-width) + (var(--scene-count)*var(--timeline-scene-width)) + (var(--scene-count)*var(--timeline-grid-gap)));min-width:100%;margin-bottom:10px;border-radius:8px;padding:8px 10px;background:rgba(255,255,255,.92);box-shadow:0 8px 16px rgba(30,37,43,.05)}.timeline-section-header button,.lane-board-heading button,.plot-lane-board-heading button,.pacing-heading button,.lane-group-heading{border:0;padding:0;color:var(--plotline-accent-dark);background:transparent;font:inherit;font-weight:900;text-align:left}.timeline-section-header button::before,.lane-board-heading button::before,.plot-lane-board-heading button::before,.pacing-heading button::before,.lane-group-heading::before{content:"v";display:inline-block;margin-right:6px;color:var(--plotline-muted);font-size:.72rem}.timeline-section-header button.is-collapsed::before,.lane-board-heading button.is-collapsed::before,.plot-lane-board-heading button.is-collapsed::before,.pacing-heading button.is-collapsed::before,.lane-group-heading.is-collapsed::before{content:">"}.lane-group{display:grid;gap:7px}.character-lane-group-body{display:grid;gap:7px}.lane-group-heading{position:sticky;left:0;z-index:6;width:var(--timeline-label-width);border-top:1px solid rgba(47,111,99,.12);padding:7px 8px 2px;color:var(--plotline-muted);background:rgba(247,250,248,.96);font-size:.78rem;text-transform:uppercase}.timeline-section-collapsed{display:none!important}.timeline-chapter-title{position:sticky;top:0;z-index:5;background:rgba(238,245,242,.96);backdrop-filter:blur(6px)}.scene-pov-label,.metric-mini-summary{display:inline-flex;width:fit-content;border-radius:999px;padding:2px 7px;font-size:.72rem;font-weight:800}.scene-pov-label{color:#514018;background:#fff3d3}.metric-mini-summary{color:var(--plotline-accent-dark);background:rgba(47,111,99,.08)}.scene-overview-panel{display:none;gap:8px;margin-top:4px}.scene-overview-section{display:grid;gap:4px}.scene-overview-label{color:var(--plotline-muted);font-size:.68rem;font-weight:900;letter-spacing:.06em;text-transform:uppercase}.scene-overview-chip-row{display:flex;flex-wrap:wrap;gap:5px;min-width:0}.scene-overview-chip,.scene-overview-empty,.scene-overview-brief{display:inline-flex;width:fit-content;max-width:100%;border-radius:999px;padding:3px 8px;background:rgba(47,111,99,.08);color:var(--plotline-ink);font-size:.72rem;font-weight:750;line-height:1.25;overflow-wrap:anywhere}.scene-overview-chip.is-pov{border:1px solid rgba(197,139,43,.38);background:#fff3d3}.scene-overview-chip.is-important{border:1px solid rgba(103,80,164,.3);background:#f0edf8}.scene-overview-chip.is-more,.scene-overview-empty{color:var(--plotline-muted);background:rgba(102,113,123,.08)}.scene-overview-brief{display:block;width:auto;border-radius:8px;background:rgba(255,255,255,.62);color:var(--plotline-muted);font-weight:600}.timeline-scene-card.search-hit,.timeline-scene-card.keyboard-focus{outline:3px solid rgba(197,139,43,.34);box-shadow:0 12px 26px rgba(197,139,43,.16)}.timeline-scene-card.search-dim{opacity:.28}.thread-marker,.asset-marker,.character-marker{transition:transform .12s ease,box-shadow .12s ease}.thread-marker:hover,.asset-marker:hover,.character-marker:hover{transform:translateY(-1px);box-shadow:0 6px 12px rgba(30,37,43,.12)}.timeline-filtered-root.density-compact{--timeline-scene-width:170px;--timeline-grid-gap:6px}.timeline-filtered-root.density-expanded{--timeline-scene-width:280px;--timeline-grid-gap:14px}.timeline-filtered-root.density-compact .timeline-scene-card,.timeline-filtered-root.zoom-book .timeline-scene-card{gap:4px;padding:9px}.timeline-filtered-root.density-compact .scene-summary,.timeline-filtered-root.density-compact .purpose-chip-row,.timeline-filtered-root.density-compact .metric-mini-summary,.timeline-filtered-root.zoom-book .scene-summary,.timeline-filtered-root.zoom-book .purpose-chip-row,.timeline-filtered-root.zoom-book .metric-mini-summary,.timeline-filtered-root.zoom-book .location-pill,.timeline-filtered-root.zoom-book .scene-pov-label{display:none}.timeline-filtered-root.zoom-book{--timeline-scene-width:150px;--timeline-grid-gap:6px}.timeline-filtered-root.zoom-scene-detail{--timeline-scene-width:300px}.timeline-filtered-root.zoom-scene-detail .scene-summary{-webkit-line-clamp:7}.timeline-filtered-root.zoom-scene-overview{--timeline-scene-width:380px}.timeline-filtered-root.zoom-scene-overview .timeline-scenes{min-height:320px}.timeline-filtered-root.zoom-scene-overview .timeline-scene-card{gap:8px;padding:14px}.timeline-filtered-root.zoom-scene-overview .scene-summary{display:none}.timeline-filtered-root.zoom-scene-overview .scene-overview-panel{display:grid}.timeline-filtered-root.density-expanded .timeline-scenes{min-height:230px}.timeline-control-panel{position:relative;top:auto;z-index:4;display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:10px;padding:8px;box-shadow:none}.timeline-filter-drawer,.timeline-control-group{border:1px solid var(--plotline-line);border-radius:999px;background:rgba(255,255,255,.82)}.timeline-filter-drawer[open],.timeline-control-group[open]{border-radius:10px;flex-basis:100%}.timeline-filter-drawer summary,.timeline-control-group summary{min-width:max-content;padding:5px 10px;color:var(--plotline-accent-dark);font-size:.82rem;font-weight:900;cursor:pointer;list-style:none}.timeline-filter-drawer summary::-webkit-details-marker,.timeline-control-group summary::-webkit-details-marker,.timeline-drag-palette summary::-webkit-details-marker{display:none}.timeline-filter-drawer summary::after,.timeline-control-group summary::after,.timeline-drag-palette summary::after{content:" v";color:var(--plotline-muted);font-size:.72rem}.timeline-filter-drawer[open] summary::after,.timeline-control-group[open] summary::after,.timeline-drag-palette[open] summary::after{content:" ^"}.timeline-filter-grid{grid-template-columns:repeat(auto-fit,minmax(145px,1fr));gap:8px;max-height:min(48vh,430px);overflow:auto;padding:0 10px 10px}.timeline-filter-grid .form-label{margin-bottom:2px;font-size:.72rem;font-weight:800}.timeline-filter-checks,.timeline-filter-actions{grid-column:1/-1}.timeline-visibility-panel,.timeline-jump-panel,.timeline-preset-panel{padding:0 10px 10px}.timeline-jump-panel{grid-template-columns:minmax(180px,1.5fr) repeat(3,minmax(150px,1fr))}.timeline-shortcut-hint{grid-column:1/-1}.timeline-preset-panel form{flex-wrap:nowrap}.timeline-preset-panel .form-control{width:150px}.timeline-preset-panel .btn,.timeline-filter-actions .btn{white-space:nowrap}.timeline-drag-palette{display:block;margin-bottom:8px;padding:7px 9px;box-shadow:none}.timeline-drag-palette summary{color:var(--plotline-accent-dark);font-size:.82rem;font-weight:900;cursor:pointer}.timeline-drag-palette .drag-chip-group{margin-top:8px}.drag-drop-panel{position:relative;top:auto;z-index:9;box-shadow:0 12px 28px rgba(30,37,43,.08)}.timeline-filtered-root,.timeline-filtered-root.density-compact,.timeline-filtered-root.zoom-book{--timeline-scene-width:220px;--timeline-grid-gap:10px}.timeline-filtered-root.density-expanded,.timeline-filtered-root.zoom-scene-detail{--timeline-scene-width:290px}.timeline-filtered-root.density-compact.zoom-book{--timeline-scene-width:200px}.timeline-chapter,.timeline-scene-card,.timeline-empty-scene{overflow-wrap:normal;word-break:normal}.timeline-scene-card strong,.timeline-chapter-title strong,.plot-lane-label strong,.asset-lane-label strong,.character-lane-label strong{min-width:0;white-space:normal;word-break:normal;overflow-wrap:break-word}.timeline-scene-card{cursor:grab}.timeline-scene-card:active,.drag-chip:active,[draggable="true"]:active{cursor:grabbing}body.drag-active .timeline-scene-card,body.dragging-asset [data-asset-drop-target],body.dragging-location [data-drag-type="location"]{transition:outline-color .12s ease,box-shadow .12s ease,opacity .12s ease}body.dragging-character .timeline-scene-card,body.dragging-asset .timeline-scene-card,body.dragging-scene .timeline-scene-card,body.dragging-scene .timeline-scenes[data-chapter-drop-target],body.dragging-asset [data-asset-drop-target],body.dragging-location [data-drag-type="location"]{outline:1px dashed rgba(47,111,99,.22);outline-offset:2px}.is-dragging{opacity:.42;transform:scale(.98)}.drop-target-active{outline:3px solid rgba(47,111,99,.42)!important;outline-offset:3px}.analytics-metric-row{display:grid;grid-template-columns:170px minmax(560px,1fr);gap:12px;align-items:stretch}.analytics-metric-label{display:flex;align-items:center;border-right:1px solid var(--plotline-line);color:var(--plotline-muted);font-weight:800}.analytics-sparkline{display:grid;grid-template-columns:repeat(var(--point-count),minmax(28px,1fr));align-items:end;gap:5px;min-height:88px;border-bottom:1px solid rgba(47,111,99,.25);padding-top:10px}.analytics-bar{position:relative;display:flex;align-items:end;justify-content:center;min-height:12px;height:max(12px,var(--bar-height));border-radius:6px 6px 2px 2px;color:var(--plotline-accent-dark);text-decoration:none;background:linear-gradient(180deg,rgba(47,111,99,.58),rgba(47,111,99,.18))}.analytics-bar:hover{background:linear-gradient(180deg,rgba(47,111,99,.82),rgba(47,111,99,.28))}.analytics-bar span{position:absolute;bottom:calc(100% + 3px);color:var(--plotline-muted);font-size:.68rem;font-weight:800}.analytics-list-item,.analytics-link-row,.analytics-count-row,.purpose-stat-row{border-top:1px solid var(--plotline-line);padding:9px 0}.analytics-list-item{display:grid;gap:3px}.analytics-list-item p{margin:0}.analytics-link-row,.analytics-count-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.analytics-link-row{color:inherit;text-decoration:none}.analytics-link-row:hover{color:var(--plotline-accent-dark)}.analytics-link-row span,.analytics-link-row strong{min-width:0}.purpose-stat-row{display:grid;grid-template-columns:minmax(120px,1fr) minmax(90px,1.5fr) 42px;align-items:center;gap:10px}.purpose-stat-bar{overflow:hidden;height:8px;border-radius:999px;background:rgba(47,111,99,.08)}.purpose-stat-bar span{display:block;height:100%;border-radius:inherit;background:var(--plotline-accent)}.inspector-actions{position:sticky;bottom:-16px;display:flex;gap:8px;border-top:1px solid var(--plotline-line);margin:2px -16px -16px;padding:12px 16px;background:rgba(255,255,255,.95);backdrop-filter:blur(8px)}.story-bible-heading{align-items:end}.story-bible-search{display:flex;gap:8px;min-width:min(100%,420px)}.story-bible-search .form-control{min-width:0}.story-bible-nav{position:sticky;top:72px;z-index:4;display:flex;flex-wrap:wrap;gap:8px;margin:0 0 18px;padding:10px;border:1px solid var(--plotline-line);border-radius:12px;background:rgba(250,249,246,.92);backdrop-filter:blur(10px)}.story-bible-nav a,.story-bible-actions a{display:inline-flex;align-items:center;border-radius:999px;padding:6px 10px;background:rgba(47,111,99,.08);color:var(--plotline-ink);font-size:.86rem;font-weight:650;text-decoration:none}.story-bible-nav a:hover,.story-bible-actions a:hover{background:rgba(47,111,99,.16)}.story-bible-section{margin:0 0 28px}.story-bible-section-heading{display:flex;align-items:end;justify-content:space-between;gap:16px;margin-bottom:12px}.story-bible-section-heading h2{margin:0}.soft-count{color:var(--plotline-muted);font-size:.9rem;font-weight:650;white-space:nowrap}.story-bible-grid,.story-bible-search-results{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:14px}.story-bible-grid-compact{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.story-bible-card{border:1px solid var(--plotline-line);border-radius:14px;padding:14px;background:rgba(255,255,255,.86);box-shadow:0 14px 34px rgba(36,32,26,.06)}.story-bible-card h3{margin:0 0 8px;font-size:1.05rem}.story-bible-card h4{margin:14px 0 8px;font-size:.96rem}.story-bible-card p{color:var(--plotline-muted)}.story-bible-card-title{display:flex;align-items:start;justify-content:space-between;gap:10px}.story-bible-card-title span{border:1px solid var(--plotline-line);border-radius:999px;padding:3px 8px;background:rgba(250,249,246,.9);color:var(--plotline-muted);font-size:.78rem;font-weight:700;white-space:nowrap}.story-bible-meta{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0}.story-bible-meta span{border-radius:999px;padding:4px 8px;background:rgba(36,32,26,.05);color:var(--plotline-muted);font-size:.78rem;font-weight:650}.story-bible-facts{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:6px 10px;margin:12px 0}.story-bible-facts dt{color:var(--plotline-muted);font-size:.78rem;font-weight:800;text-transform:uppercase}.story-bible-facts dd{margin:0;min-width:0;overflow-wrap:anywhere}.story-bible-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.story-bible-link-list{display:grid;gap:8px;margin:0;padding:0;list-style:none}.story-bible-link-list li{display:grid;gap:2px;padding:8px 0;border-bottom:1px solid rgba(36,32,26,.08)}.story-bible-link-list li:last-child{border-bottom:0}.story-bible-link-list span{color:var(--plotline-muted);font-size:.88rem}.story-bible-timeline-summary{display:grid;gap:14px}.story-bible-wide-card{max-width:none}.story-bible-chapter-summary{border-top:1px solid var(--plotline-line);padding-top:8px}.story-bible-scene-summary{display:grid;grid-template-columns:minmax(220px,1.5fr) repeat(3,minmax(120px,.8fr));gap:8px;padding:9px 0;border-top:1px solid rgba(36,32,26,.08)}.story-bible-scene-summary p{grid-column:1/-1;margin:0}.story-bible-scene-summary span{color:var(--plotline-muted);font-size:.88rem}.attention-list{display:grid;gap:10px}.attention-list article{display:grid;grid-template-columns:max-content minmax(0,1fr) auto;align-items:center;gap:10px;border:1px solid var(--plotline-line);border-radius:12px;padding:10px 12px;background:rgba(255,255,255,.82)}.attention-list p{grid-column:2/-1;margin:0;color:var(--plotline-muted)}.writer-workspace-form,.writer-add-form,.writer-edit-form,.chapter-goal-form{display:grid;gap:10px}.writer-checkbox-grid{display:flex;flex-wrap:wrap;gap:8px 14px}.writer-checkbox-grid label{display:inline-flex;align-items:center;gap:6px;color:var(--plotline-muted);font-size:.86rem;font-weight:650}.writer-priority-control{display:grid;grid-template-columns:minmax(0,1fr) max-content;align-items:center;gap:12px}.writer-priority-control .form-range{min-width:0}.writer-note-list,.writer-queue-list{display:grid;gap:10px}.writer-mini-card{border:1px solid var(--plotline-line);border-radius:12px;padding:10px;background:rgba(255,255,255,.72)}.writer-mini-card summary{display:flex;align-items:center;gap:8px;cursor:pointer}.writer-mini-card summary strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.attachment-card-list{display:grid;gap:10px}.attachment-summary{align-items:center}.attachment-kind{border-radius:999px;padding:3px 8px;background:rgba(47,111,99,.1);color:var(--plotline-accent);font-size:.76rem;font-weight:800;text-transform:uppercase}.attachment-card-body{display:grid;gap:6px;margin-top:10px}.attachment-card-body p{margin:0}.attachment-add-card{margin-top:10px}.writer-checklist{display:grid;gap:6px}.writer-checklist-row,.writer-add-inline{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px}.writer-remove-inline{display:flex;justify-content:end;margin:-2px 0 4px}.writer-dashboard-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin-bottom:26px}.writer-dashboard-panel{border:1px solid var(--plotline-line);border-radius:14px;padding:14px;background:rgba(255,255,255,.7);box-shadow:0 14px 34px rgba(36,32,26,.05)}.writer-queue-card{border:1px solid var(--plotline-line);border-radius:12px;padding:12px;background:rgba(255,255,255,.9)}.writer-queue-card h3{margin:0;font-size:1rem}.writer-queue-card p{margin:8px 0 0;color:var(--plotline-muted)}.writer-queue-title{display:flex;align-items:start;justify-content:space-between;gap:12px}.writer-kpi{display:block;color:var(--plotline-accent);font-size:2.2rem;line-height:1}.export-action-row{margin:-8px 0 18px}.dynamics-timeline{position:relative;min-height:128px;border:1px solid var(--plotline-line);border-radius:14px;background:linear-gradient(180deg,rgba(47,111,99,.07),rgba(255,255,255,.84));overflow:hidden}.dynamics-timeline::before{content:"";position:absolute;left:12px;right:12px;top:64px;height:1px;background:rgba(36,32,26,.18)}.dynamics-marker{position:absolute;bottom:18px;width:12px;height:86px;transform:translateX(-50%);display:flex;align-items:end;justify-content:center}.dynamics-marker span{display:block;width:8px;min-height:12px;max-height:82px;border-radius:999px;background:var(--plotline-accent);box-shadow:0 8px 20px rgba(47,111,99,.22)}.dynamics-marker.pov span{background:#8a4b2f}.dynamics-timeline.relationship .dynamics-marker span{background:#7c5fb0}.scenario-banner{display:flex;justify-content:space-between;gap:18px;align-items:flex-start;border:1px solid rgba(103,80,164,.22);border-radius:12px;padding:16px;margin-bottom:16px;background:linear-gradient(135deg,rgba(103,80,164,.1),rgba(255,255,255,.86));box-shadow:0 12px 28px rgba(36,32,26,.08)}.scenario-banner h1{margin:0 0 4px}.scenario-banner p{margin:0;color:var(--plotline-muted)}.scenario-card{border-color:rgba(103,80,164,.18)}.scenario-warning-strip{display:flex;gap:10px;align-items:center;border:1px solid var(--plotline-line);border-radius:10px;padding:10px 12px;margin-bottom:14px;background:rgba(255,255,255,.76);color:var(--plotline-muted)}.scenario-warning-strip strong{color:var(--plotline-ink)}.scenario-board{border:1px solid var(--plotline-line);border-radius:12px;padding:12px;background:rgba(247,245,240,.74);overflow:hidden;margin-bottom:18px}.scenario-scroll{display:flex;gap:14px;overflow-x:auto;padding-bottom:8px}.scenario-chapter{min-width:360px;max-width:520px;border:1px solid rgba(82,75,65,.14);border-radius:10px;background:rgba(255,255,255,.86)}.scenario-chapter header{display:grid;gap:2px;border-bottom:1px solid var(--plotline-line);padding:10px 12px}.scenario-chapter header span{color:var(--plotline-muted);font-size:.78rem;font-weight:800;text-transform:uppercase}.scenario-scene-row{display:grid;gap:10px;padding:10px}.scenario-scene-card{display:grid;gap:8px;border:1px solid rgba(82,75,65,.12);border-radius:9px;padding:10px;background:#fff}.scenario-scene-title{display:grid;gap:2px}.scenario-scene-title span,.scenario-mini-data{color:var(--plotline-muted);font-size:.78rem}.scenario-mini-data{display:grid;gap:2px}.scenario-move-actions,.scenario-chapter-form,.scenario-compare-list article{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.scenario-chapter-form select{min-width:160px;width:auto}.scenario-compare-list{display:grid;gap:10px}.scenario-compare-list article{justify-content:space-between;border:1px solid var(--plotline-line);border-radius:9px;padding:10px;background:rgba(255,255,255,.82)}.danger-zone{border-color:rgba(163,61,61,.24)}.archive-filter-form{align-items:end}.archive-item-list{display:grid;gap:10px}.archive-item-card{display:flex;justify-content:space-between;gap:14px;align-items:center;border:1px solid var(--plotline-line);border-radius:10px;padding:12px;background:rgba(255,255,255,.82)}.archive-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-items:end}.delete-forever-form{display:inline-flex;margin:0}.archive-item-card h3{margin:4px 0}.archived-badge{display:inline-flex;border:1px solid rgba(82,75,65,.16);border-radius:999px;padding:2px 8px;color:var(--plotline-muted);font-size:.72rem;font-weight:800;text-transform:uppercase;background:rgba(247,245,240,.86)}.restore-button{white-space:nowrap}.archive-button-form{display:inline-flex;align-items:center;margin:0}.acceptance-summary-grid{display:grid;grid-template-columns:repeat(3,minmax(160px,1fr));gap:12px;margin-bottom:16px}.acceptance-summary{display:grid;gap:4px;padding:16px}.acceptance-summary strong{font-size:2rem;line-height:1}.acceptance-summary.pass{border-left:5px solid #4d7654}.acceptance-summary.review{border-left:5px solid #b8762b}.acceptance-summary.blocked{border-left:5px solid #963f36}.acceptance-check-list{display:grid;gap:14px}.acceptance-area{border:1px solid var(--plotline-line);border-radius:10px;padding:12px;background:rgba(255,255,255,.62)}.acceptance-area h3{margin:0 0 10px;font-size:1.05rem}.acceptance-check-items{display:grid;gap:8px}.acceptance-check-item{display:grid;grid-template-columns:92px minmax(0,1fr);gap:10px;align-items:start;border:1px solid rgba(82,75,65,.12);border-radius:8px;padding:10px;background:rgba(255,255,255,.74)}.acceptance-check-item p{margin:2px 0 0;color:var(--plotline-muted)}.acceptance-status{display:inline-flex;justify-content:center;width:fit-content;min-width:74px;border-radius:999px;padding:3px 9px;font-size:.72rem;font-weight:900;text-transform:uppercase}.acceptance-check-item.pass .acceptance-status{color:#24512b;background:#e6f4e7}.acceptance-check-item.review .acceptance-status{color:#7b4b13;background:#fbecd3}.acceptance-check-item.blocked .acceptance-status{color:#7a2222;background:#fff0f0}.plotline-confirm-modal .modal-title{color:var(--plotline-ink)}.plotline-confirm-modal .modal-body p{white-space:pre-line}.delete-confirm-field{border:1px solid rgba(163,61,61,.22);border-radius:8px;padding:12px;background:rgba(255,247,244,.7)}@media print{header,.breadcrumb-trail,.context-header,.button-row,.story-bible-nav,.inspector-quick-nav{display:none!important}.plotline-shell{max-width:none;padding:0}}@media(max-width:760px){.plotline-shell{padding-left:14px;padding-right:14px}.page-heading{display:block}.page-heading .button-row,.timeline-filter{margin-top:14px}.timeline-filter{align-items:stretch;flex-direction:column}.timeline-navigation-card{grid-template-areas:"copy" "actions";grid-template-columns:1fr}.timeline-navigation-card-icon{display:none}.timeline-navigation-card-actions{justify-content:center}.story-bible-search,.story-bible-section-heading{align-items:stretch;flex-direction:column}.story-bible-scene-summary,.attention-list article{grid-template-columns:1fr}.writer-dashboard-grid{grid-template-columns:1fr}.writer-checklist-row,.writer-add-inline{grid-template-columns:1fr}.trial-status-panel--signup,.trial-status-panel__heading,.trial-dashboard-banner{align-items:stretch;flex-direction:column}.trial-dashboard-banner__actions,.trial-dashboard-banner__actions .btn,.trial-status-panel--signup .btn{width:100%}.attention-list p{grid-column:auto}}@media(max-width:900px){.workspace-layout,.workspace-layout.inspector-drawer-open{grid-template-columns:1fr;gap:14px}.timeline-inspector-resizer{display:none}.scene-inspector-panel{position:static;max-height:none;overflow:visible}.scene-inspector-body{max-height:none;overflow:visible}}@media(max-width:1100px){.metric-graph-row{grid-template-columns:1fr}.health-kpi-grid,.analytics-grid-two,.analytics-grid-three{grid-template-columns:1fr}.analytics-metric-row{grid-template-columns:1fr}.analytics-metric-label{border-right:0}}.floor-plan-list{display:grid;gap:12px}.floor-plan-list-item{align-items:center;border:1px solid var(--border-color,#d8ded9);border-radius:8px;display:flex;justify-content:space-between;gap:16px;padding:14px}.floor-plan-list-item h2{font-size:1.05rem;margin:0 0 4px}.floor-plan-list-item p{margin:0}.floor-plan-tools{display:grid;gap:14px}.floor-plan-tabs{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:12px}.floor-plan-tab{background:var(--panel-bg,#fff);border:1px solid var(--border-color,#d8ded9);border-radius:999px;color:inherit;padding:6px 12px}.floor-plan-tab.active{background:var(--bs-primary,#2f6f63);border-color:var(--bs-primary,#2f6f63);color:#fff}.floor-plan-layout{display:grid;gap:16px;grid-template-columns:minmax(0,1fr) 320px}.floor-plan-floor{display:none}.floor-plan-floor.active{display:block}.floor-plan-floor-header{align-items:end;display:grid;gap:10px;grid-template-columns:minmax(180px,1fr) 120px 120px auto;margin-bottom:10px}.floor-plan-grid-wrap{overflow:auto;padding-bottom:8px}.floor-plan-grid{--cell-size:34px;background-color:var(--panel-bg,#fff);background-image:linear-gradient(to right,rgba(93,111,101,.18) 1px,transparent 1px),linear-gradient(to bottom,rgba(93,111,101,.18) 1px,transparent 1px);background-size:var(--cell-size) var(--cell-size);border:1px solid var(--border-color,#d8ded9);border-radius:8px;height:calc(var(--grid-height)*var(--cell-size));min-width:calc(var(--grid-width)*var(--cell-size));position:relative;width:calc(var(--grid-width)*var(--cell-size))}.floor-plan-block{align-items:center;border:2px solid rgba(44,84,75,.55);border-radius:6px;color:#12332c;cursor:grab;display:flex;font-weight:650;height:calc(var(--h)*var(--cell-size));justify-content:center;left:calc(var(--x)*var(--cell-size));line-height:1.2;overflow:hidden;padding:6px;position:absolute;text-align:center;top:calc(var(--y)*var(--cell-size));touch-action:none;width:calc(var(--w)*var(--cell-size))}.floor-plan-block span:first-child{overflow-wrap:anywhere}.floor-plan-block.selected{box-shadow:0 0 0 3px rgba(47,111,99,.28);outline:2px solid var(--bs-primary,#2f6f63);z-index:3}.floor-plan-block.overlap{border-color:#b45544;box-shadow:inset 0 0 0 2px rgba(180,85,68,.18)}.floor-plan-block--room{background:#d9eee7}.floor-plan-block--corridor{background:#e7e2f4}.floor-plan-block--openspace{background:#e5efd4}.floor-plan-block--exterior{background:#d9ecf3}.floor-plan-block--other{background:#ece9e1}.floor-plan-resize-handle{border-bottom:10px solid rgba(18,51,44,.45);border-left:10px solid transparent;bottom:4px;height:0;position:absolute;right:4px;width:0}.floor-plan-side-panel{align-self:start;background:var(--panel-bg,#fff);border:1px solid var(--border-color,#d8ded9);border-radius:8px;padding:14px;position:sticky;top:12px}.floor-plan-side-panel h2{font-size:1.05rem;margin-bottom:8px}.floor-plan-overlap-warning{background:rgba(180,85,68,.12);border:1px solid rgba(180,85,68,.35);border-radius:6px;color:#8f3f31;margin-bottom:10px;padding:8px 10px}[data-bs-theme="dark"] .floor-plan-grid,.dark .floor-plan-grid{background-color:#15201d;background-image:linear-gradient(to right,rgba(214,224,218,.12) 1px,transparent 1px),linear-gradient(to bottom,rgba(214,224,218,.12) 1px,transparent 1px)}[data-bs-theme="dark"] .floor-plan-block,.dark .floor-plan-block{color:#f1f6f3}[data-bs-theme="dark"] .floor-plan-block--room,.dark .floor-plan-block--room{background:#29584f}[data-bs-theme="dark"] .floor-plan-block--corridor,.dark .floor-plan-block--corridor{background:#514876}[data-bs-theme="dark"] .floor-plan-block--openspace,.dark .floor-plan-block--openspace{background:#4f6133}[data-bs-theme="dark"] .floor-plan-block--exterior,.dark .floor-plan-block--exterior{background:#315d6f}[data-bs-theme="dark"] .floor-plan-block--other,.dark .floor-plan-block--other{background:#5b564c}@media(max-width:1100px){.floor-plan-layout,.floor-plan-floor-header{grid-template-columns:1fr}.floor-plan-side-panel{position:static}} \ No newline at end of file