/* Marque Talent Desk — the only data in the product. One record, one fixture:
   every screen reads these arrays, and no screen writes a second literal to
   match. Money that two records describe is written once and derived at read
   time — an invoice's amount and a payout's amount are both the deal's fee run
   through metrics.js, never copied here.

   The awkward states are deliberate and each appears exactly once:
     · Marisol Duarte is exclusive to Lumen Beauty in beauty until 31 Jan 2027,
       and Sable & Salt's brief is beauty — the conflict that gets agencies sued.
     · Ferro's "One-pan Sunday" came back with revisions and the revised cut is
       a day over.
     · Tessellate Home is 22 days late on $31,000, which is why Juno Park has
       not been paid.
     · Cindr's brief fits nobody: wrong category, and the budget is below what
       the cheapest creator on the books quotes for the work. */

const MARQUE_NOW = Date.UTC(2026, 7, 21, 10, 20); /* Friday 21 August 2026 */
const at = (y, m, d, h, min) => Date.UTC(y, m - 1, d, h === undefined ? 9 : h, min || 0);

const MARQUE_AGENCY = { name: "Marque", desk: "Talent desk", book: "London · New York", billingEmail: "billing@marque.example" };
const MARQUE_USER = { name: "Priya Raghunathan", role: "Talent manager", email: "priya@marque.example", avatar: "../../assets/img/avatar-40-01.jpg" };
const MARQUE_FINANCE = { name: "Otis Delacroix", role: "Business affairs" };

/* ── The desk ────────────────────────────────────────────────────────────
   Who works here and who owns which creator. Ownership is a fact about a
   creator, so `managerId` sits on the creator above and this array holds only
   the people. MARQUE_USER and MARQUE_FINANCE are the same two people read once,
   never retyped. */
const MANAGERS = [
  { id: "priya", name: MARQUE_USER.name, role: MARQUE_USER.role, email: MARQUE_USER.email, avatar: MARQUE_USER.avatar, desk: "London", joinedAt: at(2021, 3, 1) },
  { id: "noa", name: "Noa Ferreira", role: "Talent manager", email: "noa@marque.example", avatar: "../../assets/img/avatar.jpg", desk: "New York", joinedAt: at(2024, 2, 5) },
  { id: "otis", name: MARQUE_FINANCE.name, role: MARQUE_FINANCE.role, email: "otis@marque.example", avatar: "../../assets/img/avatar-40-04.jpg", desk: "London", joinedAt: at(2020, 9, 14) },
];

/* ── Brands ──────────────────────────────────────────────────────────────
   A brand's NAME is written here and nowhere else: briefs, deals, bookings and
   exclusivities carry `brandId` and the name is derived at the bottom of this
   file, so renaming a brand moves every screen at once.
   Nothing about how they behave is stored — when we first heard from them, what
   they have spent, how long they take to pay and what we are exclusive to them
   for are all read off the briefs, deals and invoices that name them.
   `lumen`, `quill` and `verdant` hold no brief on this desk: they are the
   counterparties on an exclusivity or an outside booking, and a brand we owe
   something to is still a brand we have to be able to open. */
const BRANDS = [
  { id: "cindr", name: "Cindr", sector: "dating", hq: "Berlin", note: "Cold inbound. Nobody on the book works in dating and the budget is under our floor." },
  { id: "sable", name: "Sable & Salt", sector: "beauty", hq: "Copenhagen", note: "The biggest brief on the desk. Whitelisting is the whole negotiation." },
  { id: "halvard", name: "Halvard Athletic", sector: "fitness", hq: "Oslo", note: "Slow decision-makers, generous on usage. Pitched and waiting." },
  { id: "nook", name: "Nook Living", sector: "home", hq: "London", note: "Their legal takes a fortnight and always has — price the delay into the dates." },
  { id: "ferro", name: "Ferro Kitchenware", sector: "food", hq: "Milan", note: "Hands-on brand lead who gives notes in his own words. Worth the revisions." },
  { id: "vanta", name: "Vanta Motors", sector: "tech", hq: "Stockholm", note: "Full buyouts, big money, and we had to turn the last one down on a conflict." },
  { id: "pell", name: "Pell & Co", sector: "travel", hq: "Edinburgh", note: "Took the last one in-house. Worth one more brief, not two." },
  { id: "tessellate", name: "Tessellate Home", sector: "home", hq: "Lisbon", note: "Founder-led, pays late, and underdelivered against the reach we promised." },
  { id: "corriente", name: "Corriente Swim", sector: "fashion", hq: "Barcelona", note: "Best-run campaign of the year. Their paid social rights are nearly up." },
  { id: "ardent", name: "Ardent Sport", sector: "fitness", hq: "Manchester", note: "Small budgets, fast paper, pays on time. The easiest brand on the book." },
  { id: "verdant", name: "Verdant Nutrition", sector: "fitness", hq: "Los Angeles", note: "Booked Sunny direct before this desk existed, so the campaign has no deal here." },
  { id: "quill", name: "Quill Audio", sector: "tech", hq: "Berlin", note: "Holds Theo in technology to 15 December, which is why Vanta was declined." },
  { id: "lumen", name: "Lumen Beauty", sector: "beauty", hq: "Mexico City", note: "Holds Marisol in beauty to 31 January 2027. Read it before pitching her." },
];

/* ── The roster ──────────────────────────────────────────────────────────
   reach and engagement are NOT stored: both are computed from `platforms` in
   metrics.js, so a follower count changed here moves every figure that
   mentions it. `rates` is the rate card, in cents, per deliverable kind — a
   kind the creator has no rate for is work they do not take. */
const CREATORS = [
  {
    id: "wren", name: "Wren Adeyemi", managerId: "priya", handle: "wrenmakes", based: "Margate",
    portrait: "../../assets/img/avatar-80-06.jpg",
    categories: ["home", "food"], status: "available",
    signedAt: at(2023, 4, 11), note: "Slow-build interiors with a joinery bent. Brands buy the hands, not the face.",
    platforms: [
      { platform: "instagram", followers: 412000, engagementRate: 0.038 },
      { platform: "tiktok", followers: 188000, engagementRate: 0.051 },
    ],
    rates: { ig_reel: 950000, ig_story: 180000, tiktok: 650000, ugc_pack: 400000 },
    exclusivities: [],
  },
  {
    id: "sunny", name: "Sunny Falk", managerId: "noa", handle: "sunnyfalk", based: "Los Angeles",
    portrait: "../../assets/img/avatar-80-01.jpg",
    categories: ["fitness"], status: "booked",
    signedAt: at(2021, 9, 2), note: "Endurance, not aesthetics. The only creator on the book with a real YouTube audience.",
    /* An external booking: the campaign predates this desk, so there is no deal
       record for it and the brand is the booking's own fact. */
    booking: { brandId: "verdant", until: at(2026, 10, 3), dealId: null },
    bookedUntil: at(2026, 10, 3),
    platforms: [
      { platform: "instagram", followers: 890000, engagementRate: 0.024 },
      { platform: "tiktok", followers: 1200000, engagementRate: 0.062 },
      { platform: "youtube", followers: 340000, engagementRate: 0.044 },
    ],
    rates: { ig_reel: 2800000, ig_story: 550000, tiktok: 2400000, yt_integration: 4600000, ugc_pack: 1200000 },
    exclusivities: [],
  },
  {
    id: "marisol", name: "Marisol Duarte", managerId: "priya", handle: "marisolduarte", based: "Mexico City",
    portrait: "../../assets/img/avatar-80-05.jpg",
    categories: ["beauty", "fashion"], status: "available",
    signedAt: at(2022, 2, 28), note: "The biggest audience on the book and the tightest contract. Read the exclusivity before you pitch her.",
    platforms: [
      { platform: "instagram", followers: 1400000, engagementRate: 0.019 },
      { platform: "tiktok", followers: 2100000, engagementRate: 0.074 },
    ],
    rates: { ig_reel: 3800000, ig_story: 750000, tiktok: 3400000, ugc_pack: 1600000 },
    /* The one that matters. Category-scoped, brand-scoped, dated. */
    exclusivities: [{ brandId: "lumen", category: "beauty", until: at(2027, 1, 31), signedAt: at(2026, 2, 1) }],
  },
  {
    id: "theo", name: "Theo Brandt-Lowe", managerId: "noa", handle: "brandtlowe", based: "Berlin",
    portrait: "../../assets/img/avatar-40-02.jpg",
    categories: ["tech"], status: "booked",
    signedAt: at(2024, 1, 15), note: "Long-form teardowns. Comes off the Quill campaign at the end of the month.",
    booking: { brandId: "quill", until: at(2026, 8, 29), dealId: null },
    bookedUntil: at(2026, 8, 29),
    platforms: [
      { platform: "youtube", followers: 720000, engagementRate: 0.056 },
      { platform: "tiktok", followers: 310000, engagementRate: 0.048 },
    ],
    rates: { yt_integration: 4200000, tiktok: 1100000, ugc_pack: 800000 },
    exclusivities: [{ brandId: "quill", category: "tech", until: at(2026, 12, 15), signedAt: at(2026, 5, 30) }],
  },
  {
    id: "imani", name: "Imani Castellanos", managerId: "noa", handle: "imanicast", based: "New York",
    portrait: "../../assets/img/avatar-40-03.jpg",
    categories: ["fashion", "beauty"], status: "available",
    signedAt: at(2022, 11, 7), note: "Styling-led, shoots her own stills. Corriente ran 26% over on reach.",
    platforms: [
      { platform: "instagram", followers: 640000, engagementRate: 0.031 },
      { platform: "tiktok", followers: 480000, engagementRate: 0.059 },
    ],
    rates: { ig_reel: 1600000, ig_story: 320000, tiktok: 1300000, ugc_pack: 650000 },
    exclusivities: [],
  },
  {
    id: "rafa", name: "Rafa Nkemelu", managerId: "noa", handle: "rafacooks", based: "Lagos",
    portrait: "../../assets/img/avatar-80-03.jpg",
    categories: ["food"], status: "booked",
    signedAt: at(2023, 8, 19), note: "Highest engagement on the book by a distance. One-take cooking, no cuts.",
    booking: { brandId: null, until: at(2026, 9, 12), dealId: "DEAL-26-019" },
    bookedUntil: at(2026, 9, 12),
    platforms: [
      { platform: "tiktok", followers: 980000, engagementRate: 0.081 },
      { platform: "instagram", followers: 220000, engagementRate: 0.042 },
    ],
    rates: { tiktok: 1500000, ig_reel: 900000, ig_story: 200000, ugc_pack: 500000 },
    exclusivities: [],
  },
  {
    id: "juno", name: "Juno Park", managerId: "priya", handle: "junoparkstudio", based: "Seoul",
    portrait: "../../assets/img/avatar-80-02.jpg",
    categories: ["home"], status: "available",
    signedAt: at(2024, 6, 3), note: "Small, expensive, architectural. Tessellate underran on reach and we owe her an answer.",
    platforms: [
      { platform: "instagram", followers: 310000, engagementRate: 0.046 },
      { platform: "youtube", followers: 96000, engagementRate: 0.06 },
    ],
    rates: { ig_reel: 750000, ig_story: 150000, yt_integration: 1400000, ugc_pack: 350000 },
    exclusivities: [],
  },
  {
    id: "della", name: "Della Voss", managerId: "priya", handle: "dellavoss", based: "Lisbon",
    portrait: "../../assets/img/avatar-80-04.jpg",
    categories: ["travel"], status: "paused",
    signedAt: at(2020, 5, 26), note: "On sabbatical to 2 November at her own request. Do not pitch, do not hold.",
    pausedUntil: at(2026, 11, 2),
    platforms: [
      { platform: "instagram", followers: 1100000, engagementRate: 0.022 },
      { platform: "youtube", followers: 210000, engagementRate: 0.039 },
    ],
    rates: { ig_reel: 2600000, ig_story: 500000, yt_integration: 3000000, ugc_pack: 1000000 },
    exclusivities: [],
  },
  {
    id: "kit", name: "Kit Oyelaran", managerId: "priya", handle: "kitoyelaran", based: "Manchester",
    portrait: "../../assets/img/avatar-40-05.jpg",
    categories: ["fitness", "fashion"], status: "available",
    signedAt: at(2025, 10, 14), note: "Newest signing and the cheapest rate on the book. Ardent came in 10% over promise.",
    platforms: [
      { platform: "tiktok", followers: 540000, engagementRate: 0.068 },
      { platform: "instagram", followers: 175000, engagementRate: 0.049 },
    ],
    rates: { tiktok: 800000, ig_reel: 550000, ig_story: 120000, ugc_pack: 300000 },
    exclusivities: [],
  },
];

/* ── Inbound briefs ──────────────────────────────────────────────────────
   `deliverables` is what the brand asked for; the quote a creator gives for it
   is computed in metrics.js from their rate card and the usage uplift, never
   stored. `budgetCents` is the whole campaign budget for one creator. */
const BRIEFS = [
  {
    id: "BR-26-047", brandId: "cindr", title: "Real talk, real matches",
    category: "dating", budgetCents: 900000,
    deliverables: [{ kind: "tiktok", quantity: 4 }],
    usage: "organic", audienceMin: 1000000,
    liveFromAt: at(2026, 9, 22), liveToAt: at(2026, 10, 20),
    receivedAt: at(2026, 8, 20, 16, 40), respondByAt: at(2026, 8, 26),
    contact: { name: "Dev Shanmugam", role: "Brand partnerships" },
    ask: "Four unscripted TikToks from a creator with a million-plus audience, talking honestly about dating in their twenties.",
    status: "to_triage",
  },
  {
    id: "BR-26-038", brandId: "sable", title: "Winter barrier repair",
    category: "beauty", budgetCents: 22500000,
    deliverables: [{ kind: "ig_reel", quantity: 2 }, { kind: "ig_story", quantity: 3 }, { kind: "tiktok", quantity: 1 }],
    usage: "whitelisting_6m", audienceMin: 750000,
    liveFromAt: at(2026, 10, 6), liveToAt: at(2026, 11, 14),
    receivedAt: at(2026, 8, 17, 11, 2), respondByAt: at(2026, 8, 25),
    contact: { name: "Ilse Brandão", role: "Head of brand" },
    ask: "A routine-led launch for the winter barrier cream, whitelisted to their paid social for six months.",
    status: "to_triage",
  },
  {
    id: "BR-26-041", brandId: "halvard", title: "Winter run series",
    category: "fitness", budgetCents: 11000000,
    deliverables: [{ kind: "yt_integration", quantity: 1 }, { kind: "tiktok", quantity: 2 }],
    usage: "organic", audienceMin: 800000,
    liveFromAt: at(2026, 10, 20), liveToAt: at(2026, 12, 1),
    receivedAt: at(2026, 8, 11, 9, 30), respondByAt: at(2026, 9, 1),
    contact: { name: "Marek Toft", role: "Marketing director" },
    ask: "One long-form YouTube integration across a six-week winter training block, cut down into two TikToks.",
    status: "pitching", pitchedAt: at(2026, 8, 14, 15, 10), pitched: ["sunny", "kit"],
  },
  {
    id: "BR-26-045", brandId: "nook", title: "First flat",
    category: "home", budgetCents: 5200000,
    deliverables: [{ kind: "ig_reel", quantity: 3 }, { kind: "ig_story", quantity: 6 }],
    usage: "organic", audienceMin: 300000,
    liveFromAt: at(2026, 9, 8), liveToAt: at(2026, 10, 5),
    receivedAt: at(2026, 7, 27, 10, 0), respondByAt: at(2026, 8, 8),
    contact: { name: "Bea Lindqvist", role: "Head of growth" },
    ask: "Furnishing a first rented flat on a budget, over four weeks, without it looking like a haul.",
    status: "won", pitchedAt: at(2026, 7, 29), pitched: ["wren", "juno"], dealId: "DEAL-26-021",
  },
  {
    id: "BR-26-044", brandId: "ferro", title: "Slow Sunday",
    category: "food", budgetCents: 9500000,
    deliverables: [{ kind: "tiktok", quantity: 4 }, { kind: "ig_reel", quantity: 1 }],
    usage: "paid_social_3m", audienceMin: 600000,
    liveFromAt: at(2026, 8, 10), liveToAt: at(2026, 9, 20),
    receivedAt: at(2026, 7, 14, 14, 20), respondByAt: at(2026, 7, 24),
    contact: { name: "Hal Winterbourne", role: "Brand lead" },
    ask: "Five pieces built around one cast-iron pan, shot the way he already cooks.",
    status: "won", pitchedAt: at(2026, 7, 16), pitched: ["rafa", "wren"], dealId: "DEAL-26-019",
  },
  {
    id: "BR-26-036", brandId: "vanta", title: "City EV, one week",
    category: "tech", budgetCents: 15000000,
    deliverables: [{ kind: "yt_integration", quantity: 1 }, { kind: "ugc_pack", quantity: 1 }],
    usage: "buyout_12m", audienceMin: 500000,
    liveFromAt: at(2026, 9, 1), liveToAt: at(2026, 9, 30),
    receivedAt: at(2026, 6, 30, 8, 45), respondByAt: at(2026, 7, 10),
    contact: { name: "Sunniva Ek", role: "Global partnerships" },
    ask: "A week of city driving on the new compact, bought out in full for twelve months.",
    status: "passed", passReason: "brand_conflict", passedAt: at(2026, 7, 3),
    passNote: "Theo is the only creator on the book in technology and he is exclusive to Quill Audio in the category until 15 December. Declined rather than risk it.",
  },
  {
    id: "BR-26-030", brandId: "pell", title: "Slow roads",
    category: "travel", budgetCents: 12000000,
    deliverables: [{ kind: "yt_integration", quantity: 1 }, { kind: "ig_reel", quantity: 2 }],
    usage: "paid_social_3m", audienceMin: 900000,
    liveFromAt: at(2026, 8, 1), liveToAt: at(2026, 9, 15),
    receivedAt: at(2026, 6, 9, 12, 0), respondByAt: at(2026, 6, 26),
    contact: { name: "Rowan Teague", role: "Creative director" },
    ask: "A two-week drive through northern Portugal, no itinerary, no voiceover.",
    status: "lost", pitchedAt: at(2026, 6, 12), pitched: ["della"], lostAt: at(2026, 7, 22),
    lostNote: "Brand took it in-house after Della asked to move the shoot dates.",
  },
  {
    id: "BR-26-028", brandId: "tessellate", title: "Tile stories",
    category: "home", budgetCents: 3400000,
    deliverables: [{ kind: "ig_reel", quantity: 2 }, { kind: "yt_integration", quantity: 1 }],
    usage: "organic", audienceMin: 250000,
    liveFromAt: at(2026, 6, 10), liveToAt: at(2026, 7, 10),
    receivedAt: at(2026, 5, 6, 9, 15), respondByAt: at(2026, 5, 20),
    contact: { name: "Gil Marchetti", role: "Founder" },
    ask: "Three pieces on the new terracotta range, shot in a real kitchen rather than a set.",
    status: "won", pitchedAt: at(2026, 5, 8), pitched: ["juno", "wren"], dealId: "DEAL-26-012",
  },
  {
    id: "BR-26-022", brandId: "corriente", title: "Salt season",
    category: "fashion", budgetCents: 6200000,
    deliverables: [{ kind: "ig_reel", quantity: 2 }, { kind: "tiktok", quantity: 1 }],
    usage: "paid_social_3m", audienceMin: 700000,
    liveFromAt: at(2026, 6, 1), liveToAt: at(2026, 7, 1),
    receivedAt: at(2026, 4, 28, 11, 30), respondByAt: at(2026, 5, 12),
    contact: { name: "Noor Haddad", role: "Head of marketing" },
    ask: "The summer capsule, worn in, on real coastline rather than a studio.",
    status: "won", pitchedAt: at(2026, 5, 1), pitched: ["imani", "marisol"], dealId: "DEAL-26-009",
  },
  {
    id: "BR-26-014", brandId: "ardent", title: "Couch to five",
    category: "fitness", budgetCents: 1800000,
    deliverables: [{ kind: "tiktok", quantity: 2 }],
    usage: "organic", audienceMin: 400000,
    liveFromAt: at(2026, 7, 1), liveToAt: at(2026, 7, 31),
    receivedAt: at(2026, 5, 20, 13, 0), respondByAt: at(2026, 6, 5),
    contact: { name: "Fenn Okoro", role: "Brand manager" },
    ask: "Two TikToks for the beginners' programme, from someone who was a beginner last year.",
    status: "won", pitchedAt: at(2026, 5, 22), pitched: ["kit"], dealId: "DEAL-26-004",
  },
];

/* ── Deals ───────────────────────────────────────────────────────────────
   `feeCents` is what the brand pays and it is written once. The commission,
   the creator's net, the invoice amount and the payout amount are all derived
   from it in metrics.js — none of them appears as a literal below.
   `promise` is what we told the brand in the pitch; performance is measured
   against it. */
const DEALS = [
  {
    id: "DEAL-26-004", briefId: "BR-26-014", creatorId: "kit", brandId: "ardent", title: "Couch to five",
    agreedAt: at(2026, 6, 8), feeCents: 1500000, commissionRate: 0.2,
    contract: { status: "signed", draftedAt: at(2026, 6, 9), sentAt: at(2026, 6, 10), signedAt: at(2026, 6, 15) },
    usage: "organic", usageStartAt: at(2026, 7, 6),
    promise: { reach: 700000, engagementRate: 0.05 },
    deliverables: [
      { id: "DLV-04-1", kind: "tiktok", quantity: 1, title: "Week one, honestly", dueAt: at(2026, 7, 6), status: "published", submittedAt: at(2026, 7, 2), publishedAt: at(2026, 7, 6, 18, 0), url: "tiktok.com/@kitoyelaran/7401", metrics: { reach: 412000, engagements: 31900, saves: 4100 } },
      { id: "DLV-04-2", kind: "tiktok", quantity: 1, title: "The five, and what it cost", dueAt: at(2026, 7, 15), status: "published", submittedAt: at(2026, 7, 11), publishedAt: at(2026, 7, 15, 19, 30), url: "tiktok.com/@kitoyelaran/7412", metrics: { reach: 355000, engagements: 24850, saves: 3050 } },
    ],
    invoice: { id: "INV-26-0164", status: "paid", issuedAt: at(2026, 7, 20), paidAt: at(2026, 8, 17, 14, 5) },
    payout: { id: "PAY-26-0091", status: "due", reference: null, paidAt: null },
  },
  {
    id: "DEAL-26-009", briefId: "BR-26-022", creatorId: "imani", brandId: "corriente", title: "Salt season",
    agreedAt: at(2026, 5, 15), feeCents: 5950000, commissionRate: 0.2,
    contract: { status: "signed", draftedAt: at(2026, 5, 16), sentAt: at(2026, 5, 18), signedAt: at(2026, 5, 20) },
    usage: "paid_social_3m", usageStartAt: at(2026, 6, 2),
    promise: { reach: 900000, engagementRate: 0.042 },
    deliverables: [
      { id: "DLV-09-1", kind: "ig_reel", quantity: 1, title: "Capsule, first wear", dueAt: at(2026, 6, 2), status: "published", submittedAt: at(2026, 5, 29), publishedAt: at(2026, 6, 2, 17, 0), url: "instagram.com/p/salt-1", metrics: { reach: 388000, engagements: 15900, saves: 6200 } },
      { id: "DLV-09-2", kind: "ig_reel", quantity: 1, title: "Three ways off the beach", dueAt: at(2026, 6, 12), status: "published", submittedAt: at(2026, 6, 9), publishedAt: at(2026, 6, 12, 16, 30), url: "instagram.com/p/salt-2", metrics: { reach: 341000, engagements: 12400, saves: 4900 } },
      { id: "DLV-09-3", kind: "tiktok", quantity: 1, title: "What actually survives salt", dueAt: at(2026, 6, 24), status: "published", submittedAt: at(2026, 6, 20), publishedAt: at(2026, 6, 24, 20, 0), url: "tiktok.com/@imanicast/7288", metrics: { reach: 402000, engagements: 26800, saves: 5400 } },
    ],
    invoice: { id: "INV-26-0138", status: "paid", issuedAt: at(2026, 6, 26), paidAt: at(2026, 7, 21, 10, 40) },
    payout: { id: "PAY-26-0071", status: "paid", reference: "SEPA-0071", paidAt: at(2026, 8, 3, 11, 0) },
  },
  {
    id: "DEAL-26-012", briefId: "BR-26-028", creatorId: "juno", brandId: "tessellate", title: "Tile stories",
    agreedAt: at(2026, 5, 29), feeCents: 3100000, commissionRate: 0.2,
    contract: { status: "signed", draftedAt: at(2026, 5, 30), sentAt: at(2026, 6, 1), signedAt: at(2026, 6, 2) },
    usage: "organic", usageStartAt: at(2026, 6, 12),
    promise: { reach: 380000, engagementRate: 0.048 },
    deliverables: [
      { id: "DLV-12-1", kind: "ig_reel", quantity: 1, title: "Terracotta, in a real kitchen", dueAt: at(2026, 6, 12), status: "published", submittedAt: at(2026, 6, 9), publishedAt: at(2026, 6, 12, 12, 0), url: "instagram.com/p/tile-1", metrics: { reach: 118000, engagements: 6100, saves: 2900 } },
      { id: "DLV-12-2", kind: "ig_reel", quantity: 1, title: "Grout is a colour decision", dueAt: at(2026, 6, 20), status: "published", submittedAt: at(2026, 6, 17), publishedAt: at(2026, 6, 20, 11, 0), url: "instagram.com/p/tile-2", metrics: { reach: 96000, engagements: 4400, saves: 2100 } },
      { id: "DLV-12-3", kind: "yt_integration", quantity: 1, title: "Renovating one wall", dueAt: at(2026, 6, 28), status: "published", submittedAt: at(2026, 6, 25), publishedAt: at(2026, 6, 28, 15, 0), url: "youtube.com/watch?v=tile3", metrics: { reach: 74000, engagements: 5200, saves: 1180 } },
    ],
    /* Issued the day after the last post; net-30 fell on 30 July and nothing
       has landed. This is why Juno has not been paid. */
    invoice: { id: "INV-26-0158", status: "issued", issuedAt: at(2026, 6, 30), paidAt: null, chasedAt: at(2026, 8, 12, 9, 0) },
    payout: { id: "PAY-26-0079", status: "awaiting_funds", reference: null, paidAt: null },
  },
  {
    id: "DEAL-26-019", briefId: "BR-26-044", creatorId: "rafa", brandId: "ferro", title: "Slow Sunday",
    agreedAt: at(2026, 8, 3), feeCents: 8800000, commissionRate: 0.18,
    contract: { status: "signed", draftedAt: at(2026, 8, 3), sentAt: at(2026, 8, 3, 16, 0), signedAt: at(2026, 8, 4, 10, 15) },
    usage: "paid_social_3m", usageStartAt: at(2026, 8, 13),
    promise: { reach: 1400000, engagementRate: 0.06 },
    deliverables: [
      { id: "DLV-19-1", kind: "tiktok", quantity: 1, title: "Cast-iron sear", dueAt: at(2026, 8, 12), status: "published", submittedAt: at(2026, 8, 9), publishedAt: at(2026, 8, 13, 18, 0), url: "tiktok.com/@rafacooks/7455", metrics: { reach: 486000, engagements: 39400, saves: 11200 } },
      /* The revision the brand asked for, and it is a day over. */
      { id: "DLV-19-2", kind: "tiktok", quantity: 1, title: "One-pan Sunday", dueAt: at(2026, 8, 20), status: "revisions_requested", submittedAt: at(2026, 8, 17, 14, 0), revisionAt: at(2026, 8, 18, 9, 20), revisionNote: "The apron shot crops the wordmark. Reshoot that beat or cut it — everything else is approved.", revisionBy: "Hal Winterbourne" },
      { id: "DLV-19-3", kind: "ig_reel", quantity: 1, title: "Ferro care ritual", dueAt: at(2026, 8, 26), status: "submitted", submittedAt: at(2026, 8, 20, 16, 45) },
      { id: "DLV-19-4", kind: "tiktok", quantity: 1, title: "Sunday leftovers", dueAt: at(2026, 8, 25), status: "scheduled" },
      { id: "DLV-19-5", kind: "tiktok", quantity: 1, title: "Braise night", dueAt: at(2026, 9, 2), status: "scheduled" },
    ],
    invoice: { id: "INV-26-0171", status: "issued", issuedAt: at(2026, 8, 14), paidAt: null },
    payout: { id: "PAY-26-0084", status: "awaiting_funds", reference: null, paidAt: null },
  },
  {
    id: "DEAL-26-021", briefId: "BR-26-045", creatorId: "wren", brandId: "nook", title: "First flat",
    agreedAt: at(2026, 8, 10), feeCents: 4150000, commissionRate: 0.2,
    /* Eight days out and nothing can start until it comes back. */
    contract: { status: "out_for_signature", draftedAt: at(2026, 8, 10), sentAt: at(2026, 8, 13, 11, 0), signedAt: null },
    usage: "organic", usageStartAt: at(2026, 9, 8),
    promise: { reach: 520000, engagementRate: 0.04 },
    deliverables: [
      { id: "DLV-21-1", kind: "ig_reel", quantity: 1, title: "Empty room tour", dueAt: at(2026, 9, 8), status: "scheduled" },
      { id: "DLV-21-2", kind: "ig_story", quantity: 6, title: "Move-in day frames", dueAt: at(2026, 9, 9), status: "scheduled" },
      { id: "DLV-21-3", kind: "ig_reel", quantity: 1, title: "One shelf, three ways", dueAt: at(2026, 9, 18), status: "scheduled" },
      { id: "DLV-21-4", kind: "ig_reel", quantity: 1, title: "The finished flat", dueAt: at(2026, 9, 29), status: "scheduled" },
    ],
    invoice: { id: "INV-26-0175", status: "draft", issuedAt: null, paidAt: null, holdReason: "Held until the contract is countersigned." },
    payout: { id: "PAY-26-0088", status: "awaiting_funds", reference: null, paidAt: null },
  },
];

/* Authored against the entities above: every one of these agrees with the
   record it names, at the timestamp it carries. */
const NOTIFICATIONS = [
  { id: "n1", at: at(2026, 8, 21, 8, 5), title: "Tessellate Home is past due", body: "INV-26-0158 was due on 30 July. Juno Park's payout is held until it lands.", route: "/money" },
  { id: "n2", at: at(2026, 8, 20, 9, 15), title: "Nook Living haven't countersigned", body: "Out for signature since 13 August. Wren's first post is due 8 September.", route: "/deals/DEAL-26-021" },
  { id: "n3", at: at(2026, 8, 18, 9, 20), title: "Ferro sent revisions on One-pan Sunday", body: "Hal Winterbourne wants the apron beat reshot or cut. The revised cut was due yesterday.", route: "/deliverables" },
  { id: "n4", at: at(2026, 8, 17, 11, 2), title: "Sable & Salt want an answer by Tuesday", body: "$225,000 in beauty, whitelisted six months. Read Marisol's exclusivity before you reply.", route: "/briefs/BR-26-038" },
  /* Both of these are true of the fixture above at this timestamp: Corriente's
     three-month paid social window opened on 2 June, and Theo's Quill booking
     ends on 29 August. */
  { id: "n5", at: at(2026, 8, 16, 7, 30), title: "Corriente's paid social rights end on 2 September", body: "Three months from 2 June. Renew the window or the ads have to come down.", route: "/performance/DEAL-26-009" },
  { id: "n6", at: at(2026, 8, 14, 8, 0), title: "Theo comes free on 29 August", body: "Off the Quill campaign in eight days, but exclusive to them in technology until 15 December.", route: "/talent/theo" },
];

/* The brand name, derived once. Every record above carries an id; this is the
   only place a name is attached to one, so no screen renders a second literal
   and a rename in BRANDS moves the roster, the ledgers and the palette
   together. A record pointing at a brand that does not exist is a bug, and it
   says so rather than printing an id at a reader. */
const brandNameOf = (id) => {
  const brand = BRANDS.find((b) => b.id === id);
  if (!brand) { console.warn("[fixtures] no brand with id", id); return String(id); }
  return brand.name;
};
BRIEFS.forEach((brief) => { brief.brand = brandNameOf(brief.brandId); });
DEALS.forEach((deal) => { deal.brand = brandNameOf(deal.brandId); });
CREATORS.forEach((creator) => {
  /* A booking with a deal on this desk takes its brand from the deal, so
     bookingBrandOf() stays the one answer; only an outside booking names one. */
  if (creator.booking && creator.booking.brandId) creator.booking.brand = brandNameOf(creator.booking.brandId);
  (creator.exclusivities || []).forEach((e) => { e.brand = brandNameOf(e.brandId); });
});

Object.assign(window, { MARQUE_NOW, MARQUE_AGENCY, MARQUE_USER, MARQUE_FINANCE, MANAGERS, BRANDS, CREATORS, BRIEFS, DEALS, NOTIFICATIONS, brandNameOf });
