// Room layout canvas - Phase B.2.8b.

const ROOM_LAYOUT_EMBEDDED_APP_SHELL = new URLSearchParams(window.location.search).get("embed") === "shell";

const ROOM_DND = {
  furnitureKind: "application/x-scrubstack-furniture-kind",
  furnitureId: "application/x-scrubstack-furniture-id",
  furnitureOffset: "application/x-scrubstack-furniture-offset",
  itemId: "application/x-scrubstack-item-id",
};

const ROOM_FURNITURE = {
  anesthesia_station: { label: "Anesthesia", icon: "A", w: 0.28, h: 0.13, bg: "#E3E8DE", bd: "#8DA18D", fg: "#3F523F", fixedLabel: "Default widget" },
  nurses_workstation: { label: "Nurses Station", icon: "NS", w: 0.28, h: 0.13, bg: "#E3E8DE", bd: "#7A8E7B", fg: "#3F523F", fixedLabel: "Default widget" },
  or_bed: { label: "OR bed", icon: "BED", w: 0.30, h: 0.14, bg: "#E8EBEA", bd: "#6E6962", fg: "#3A3833", paletteNote: "Optional; record removal reason" },
  boom: { label: "Boom", icon: "BM", w: 0.16, h: 0.16, bg: "#E7EFE9", bd: "#789B86", fg: "#3F523F", paletteNote: "Optional equipment target" },
  back_table: { label: "Back table", icon: "BT", glyph: "table_wheels", w: 0.24, h: 0.11, bg: "#FAF7F2", bd: "#C7765A", fg: "#7A3F2A" },
  mayo_stand: { label: "Mayo", icon: "M", glyph: "mayo_stand", w: 0.10, h: 0.10, bg: "#F1DDD2", bd: "#C7765A", fg: "#7A3F2A" },
  equipment_cart: { label: "Equip cart", icon: "EC", glyph: "equipment_cart", w: 0.14, h: 0.11, bg: "#DCE2E6", bd: "#6B7A85", fg: "#3A4651", paletteNote: "Optional equipment target" },
  bovie_tower: { label: "Bovie", icon: "B", glyph: "bovie_pencil", w: 0.10, h: 0.13, bg: "#F4E6CC", bd: "#D9A14A", fg: "#7A5615" },
  suction_tower: { label: "Suction", icon: "S", glyph: "suction_canister", w: 0.10, h: 0.13, bg: "#EEE7DA", bd: "#9A8C66", fg: "#5F563F" },
  c_arm: { label: "C-arm", icon: "C", glyph: "xray", w: 0.18, h: 0.16, bg: "#E2E2DA", bd: "#6E6962", fg: "#3A3833" },
  prep_station: { label: "Prep", icon: "P", glyph: "prep_stick", w: 0.16, h: 0.11, bg: "#F1DDD2", bd: "#A05F47", fg: "#7A3F2A" },
  custom: { label: "Custom", icon: "+", w: 0.13, h: 0.10, bg: "#FAF7F2", bd: "#ABA59C", fg: "#3A3833", paletteNote: "GUDID lookup" },
};

const ROOM_PALETTE = [
  "or_bed",
  "boom",
  "back_table",
  "mayo_stand",
  "equipment_cart",
  "bovie_tower",
  "suction_tower",
  "c_arm",
  "prep_station",
  "custom",
];

const ROOM_DEFAULT_RULES = [
  {
    id: "esu",
    label: "ESU",
    defaultLabel: "ESU assumed in room",
    reasonLabel: "Bovie pencil/tip listed",
    accessoryPattern: /\b(bovie|cautery|electrosurgical)\b.*\b(pencil|tip|cord|pad|blade)\b|\b(pencil|tip)\b.*\b(bovie|cautery|electrosurgical)\b/i,
    equipmentPattern: /\b(esu|electrosurgical unit|cautery generator|bovie generator|valleylab|force triad)\b/i,
    targetKinds: ["boom", "equipment_cart", "bovie_tower"],
  },
  {
    id: "suction",
    label: "Suction",
    defaultLabel: "Suction assumed in room",
    reasonLabel: "Suction accessory listed",
    accessoryPattern: /\b(yankauer|poole|suction tubing|suction tip|suction canister|suction liner)\b/i,
    equipmentPattern: /\b(suction machine|suction pump|suction tower|suction regulator|wall suction)\b/i,
    targetKinds: ["boom", "equipment_cart", "suction_tower"],
  },
];

const ROOM_STORAGE_PREFIX = "scrubstack-room-layout";

const ROOM_SEED = [
  { furniture_kind: "anesthesia_station", zone_label: "anesthesia", position_x: 0.15, position_y: 0.30, rotation: 0, is_fixed: true, width_pct: 0.28, height_pct: 0.13, item_count: 0 },
  { furniture_kind: "nurses_workstation", zone_label: "nurses-station", position_x: 0.85, position_y: 0.30, rotation: 0, is_fixed: true, width_pct: 0.28, height_pct: 0.13, item_count: 0 },
];

function RoomLayoutView({ mobile = false, viewTabs = null }) {
  const cardId = window.PREFCARD_CONFIG?.cardId || 3;
  const compact = useRoomCompactLayout(mobile);
  const [cardSnapshot] = React.useState(window.PREFCARD);
  const [furniture, setFurniture] = React.useState(ROOM_SEED);
  const [items, setItems] = React.useState([]);
  const [assignments, setAssignments] = React.useState({});
  const [selectedZone, setSelectedZone] = React.useState(null);
  const [loadState, setLoadState] = React.useState({ phase: "loading", message: "" });
  const [saveState, setSaveState] = React.useState({ phase: "idle", message: "" });
  const [toast, setToast] = React.useState("");
  const [layoutSide, setLayoutSide] = React.useState(() => readRoomSetting(cardId, "side", "right") || "right");
  const [layoutNotes, setLayoutNotes] = React.useState(() => readRoomSetting(cardId, "notes", { bedRemovalReason: "" }) || { bedRemovalReason: "" });

  React.useEffect(() => {
    let alive = true;
    fetch(window.prefcardApiPath(`/api/cards/${cardId}/room-layout`), {
      credentials: "same-origin",
      headers: { "Accept": "application/json" },
    })
      .then((response) => {
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        return response.json();
      })
      .then((payload) => {
        if (!alive) return;
        applyRoomPayload(payload, setFurniture, setItems, setAssignments);
        setLoadState({ phase: "done", message: "" });
      })
      .catch((err) => {
        if (!alive) return;
        setItems(fallbackItems());
        setLoadState({ phase: "error", message: err.message || "Room layout unavailable" });
      });
    return () => { alive = false; };
  }, [cardId]);

  const selectedFurniture = React.useMemo(
    () => furniture.find((row) => row.zone_label === selectedZone) || null,
    [furniture, selectedZone]
  );
  const itemCounts = React.useMemo(() => countAssignments(assignments), [assignments]);
  const assignedItems = selectedFurniture ? itemsForZone(items, assignments, selectedFurniture.zone_label) : [];
  const unassignedItems = React.useMemo(
    () => items.filter((item) => !assignments[String(item.id)]),
    [items, assignments]
  );
  const detailOpen = Boolean(selectedFurniture);
  const bedPlaced = furniture.some((row) => row.furniture_kind === "or_bed");
  const boomPlaced = furniture.some((row) => row.furniture_kind === "boom");
  const cartPlaced = furniture.some((row) => row.furniture_kind === "equipment_cart");
  const roomDefaults = React.useMemo(() => inferredRoomDefaults(items), [items]);
  const highlightItemId = new URLSearchParams(window.location.search).get("highlight_item");

  React.useEffect(() => {
    writeRoomSetting(cardId, "side", layoutSide);
  }, [cardId, layoutSide]);

  React.useEffect(() => {
    writeRoomSetting(cardId, "notes", layoutNotes);
  }, [cardId, layoutNotes]);

  function addFurniture(kind, position) {
    const meta = ROOM_FURNITURE[kind];
    const zoneLabel = nextZoneLabel(furniture, kind);
    const row = {
      furniture_kind: kind,
      zone_label: zoneLabel,
      position_x: position.x,
      position_y: position.y,
      rotation: 0,
      is_fixed: false,
      width_pct: meta.w,
      height_pct: meta.h,
      item_count: 0,
    };
    setFurniture((prev) => [...prev, row]);
    setSelectedZone(zoneLabel);
    setSaveState({ phase: "idle", message: "" });
  }

  function moveFurniture(zoneLabel, position) {
    setFurniture((prev) => prev.map((row) => row.zone_label === zoneLabel ? {
      ...row,
      position_x: position.x,
      position_y: position.y,
    } : row));
    setSaveState({ phase: "idle", message: "" });
  }

  function rotateFurniture(zoneLabel, delta) {
    setFurniture((prev) => prev.map((row) => row.zone_label === zoneLabel ? {
      ...row,
      rotation: (Number(row.rotation || 0) + delta + 360) % 360,
    } : row));
  }

  function deleteFurniture(zoneLabel) {
    const row = furniture.find((candidate) => candidate.zone_label === zoneLabel);
    if (!row || row.is_fixed) return;
    setFurniture((prev) => prev.filter((candidate) => candidate.zone_label !== zoneLabel));
    setAssignments((prev) => clearZone(prev, zoneLabel));
    setSelectedZone(null);
  }

  function setBedRemovalReason(reason) {
    setLayoutNotes((prev) => ({ ...prev, bedRemovalReason: reason }));
  }

  function assignItem(itemId, zoneLabel) {
    setAssignments((prev) => ({ ...prev, [String(itemId)]: zoneLabel }));
    setSelectedZone(zoneLabel);
    setSaveState({ phase: "idle", message: "" });
  }

  function removeItem(itemId) {
    setAssignments((prev) => ({ ...prev, [String(itemId)]: null }));
    setSaveState({ phase: "idle", message: "" });
  }

  async function saveLayout() {
    setSaveState({ phase: "saving", message: "" });
    try {
      const response = await fetch(window.prefcardApiPath(`/api/cards/${cardId}/room-layout`), {
        method: "PUT",
        credentials: "same-origin",
        headers: { "Content-Type": "application/json", "Accept": "application/json" },
        body: JSON.stringify({
          furniture: furniture.map((row) => ({
            zone_label: row.zone_label,
            furniture_kind: row.furniture_kind,
            position_x: Number(row.position_x),
            position_y: Number(row.position_y),
            rotation: Number(row.rotation || 0),
            is_fixed: Boolean(row.is_fixed),
            width_pct: Number(row.width_pct),
            height_pct: Number(row.height_pct),
          })),
          item_zone_assignments: items.map((item) => ({
            item_id: numericItemId(item.id),
            zone_label: assignments[String(item.id)] || null,
          })).filter((row) => Number.isFinite(row.item_id)),
        }),
      });
      const payload = await response.json();
      if (!response.ok) throw new Error(payload.detail || `HTTP ${response.status}`);
      applyRoomPayload(payload, setFurniture, setItems, setAssignments);
      setSaveState({ phase: "done", message: "" });
      setToast("Layout saved");
      setTimeout(() => setToast(""), 1400);
    } catch (err) {
      setSaveState({ phase: "error", message: err.message || "Save failed" });
    }
  }

  return (
    <div className="ss-app" style={{ background: "var(--ss-cream)", minHeight: "100vh", position: "relative" }}>
      {!ROOM_LAYOUT_EMBEDDED_APP_SHELL && (mobile ? <RoomMobileHeader card={cardSnapshot} /> : <window.AppHeader />)}
      <RoomPageHeader
        card={cardSnapshot}
        mobile={mobile}
        loadState={loadState}
        saveState={saveState}
        onSave={saveLayout}
        side={layoutSide}
        onSideChange={setLayoutSide}
      />
      {viewTabs}
      {saveState.phase === "error" && <RoomBanner tone="error" message={saveState.message} onRetry={saveLayout} />}
      {loadState.phase === "error" && <RoomBanner tone="warn" message={`Using offline layout fallback: ${loadState.message}`} />}
      <main style={{
        padding: mobile ? "12px 0 30px" : "18px 32px 42px",
        display: "grid",
        gridTemplateColumns: mobile ? "1fr" : (compact || detailOpen) ? "220px minmax(0, 1fr)" : "220px minmax(0, 1fr) 300px",
        gap: mobile ? 12 : 18,
        alignItems: "start",
      }}>
        {!mobile && (
          <FurniturePalette
            bedPlaced={bedPlaced}
            boomPlaced={boomPlaced}
            cartPlaced={cartPlaced}
            roomDefaults={roomDefaults}
            highlightItemId={highlightItemId}
            bedRemovalReason={layoutNotes.bedRemovalReason || ""}
            onBedRemovalReasonChange={setBedRemovalReason}
          />
        )}
        <RoomCanvas
          furniture={furniture}
          itemCounts={itemCounts}
          mobile={mobile}
          selectedZone={selectedZone}
          onSelect={setSelectedZone}
          onAddFurniture={addFurniture}
          onMoveFurniture={moveFurniture}
          onAssignItem={assignItem}
        />
        <RoomRightPanel
          mobile={mobile}
          compact={compact}
          selectedFurniture={selectedFurniture}
          assignedItems={assignedItems}
          unassignedItems={unassignedItems}
          allItems={items}
          roomDefaults={roomDefaults}
          highlightItemId={highlightItemId}
          bedRemovalReason={layoutNotes.bedRemovalReason || ""}
          onClose={() => setSelectedZone(null)}
          onRotate={rotateFurniture}
          onDelete={deleteFurniture}
          onRemoveItem={removeItem}
          onBedRemovalReasonChange={setBedRemovalReason}
        />
      </main>
      {toast && <div role="status" style={roomToastStyle}>{toast}</div>}
    </div>
  );
}

function useRoomCompactLayout(mobile) {
  const [compact, setCompact] = React.useState(() => !mobile && window.innerWidth <= 980);
  React.useEffect(() => {
    if (mobile) {
      setCompact(false);
      return undefined;
    }
    const media = window.matchMedia("(max-width: 980px)");
    const update = () => setCompact(media.matches);
    update();
    media.addEventListener?.("change", update);
    window.addEventListener("resize", update);
    return () => {
      media.removeEventListener?.("change", update);
      window.removeEventListener("resize", update);
    };
  }, [mobile]);
  return compact;
}

function RoomPageHeader({ card, mobile, loadState, saveState, onSave, side, onSideChange }) {
  return (
    <div style={{ padding: mobile ? "16px 16px 14px" : "28px 32px 18px", borderBottom: "1px solid var(--ss-line)" }}>
      <div style={{ display: "flex", flexDirection: mobile ? "column" : "row", gap: mobile ? 12 : 28, alignItems: mobile ? "stretch" : "flex-start" }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="ss-caps" style={{ color: "var(--ss-terra-deep)", marginBottom: 8 }}>Room layout</div>
          <h1 className="ss-serif" style={{ margin: 0, fontSize: mobile ? 24 : 34, fontWeight: 500, lineHeight: 1.1, color: "var(--ss-ink)" }}>
            {card.fullName || card.name || "Preference Card"}
          </h1>
          {card.maintainer && (
            <div style={{ marginTop: 8, fontSize: mobile ? 14 : 16, fontWeight: 700, color: "var(--ss-ink)", lineHeight: 1.25 }}>
              {card.maintainer}
            </div>
          )}
          <window.ProvenanceFooter
            source={card.source || "scrubstack"}
            freshness={card.freshness || "live"}
            sha={card.sha || "card"}
            version={card.version || "v1.0.0"}
            maintainer={card.maintainer}
            expanded={!mobile}
            style={{ marginTop: 10 }}
          />
        </div>
        <div style={{ display: "grid", gap: 10, justifyItems: mobile ? "stretch" : "end" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, justifyContent: mobile ? "space-between" : "flex-end" }}>
            <span className="ss-caps" style={{ color: loadState.phase === "loading" ? "var(--ss-gray-500)" : "var(--ss-match-verified)" }}>
              {loadState.phase === "loading" ? "loading" : "live"}
            </span>
            {!mobile && (
              <button onClick={onSave} disabled={saveState.phase === "saving"} style={{
                ...roomPrimaryButton,
                opacity: saveState.phase === "saving" ? 0.55 : 1,
                cursor: saveState.phase === "saving" ? "not-allowed" : "pointer",
              }}>
                {saveState.phase === "saving" ? "Saving" : "Save layout"}
              </button>
            )}
          </div>
          <div role="group" aria-label="Procedure side" style={{
            display: "inline-flex",
            alignItems: "center",
            gap: 4,
            padding: 3,
            border: "1px solid var(--ss-line)",
            borderRadius: 6,
            background: "var(--ss-warm-white)",
          }}>
            <span className="ss-caps" style={{ color: "var(--ss-gray-700)", padding: "0 5px" }}>Side</span>
            {["left", "right"].map((value) => {
              const active = side === value;
              return (
                <button
                  key={value}
                  type="button"
                  aria-pressed={active}
                  onClick={() => onSideChange(value)}
                  style={{
                    border: "1px solid transparent",
                    borderRadius: 4,
                    background: active ? "var(--ss-ink)" : "transparent",
                    color: active ? "var(--ss-cream)" : "var(--ss-gray-900)",
                    padding: "5px 9px",
                    fontSize: 12,
                    fontWeight: 700,
                    cursor: "pointer",
                    fontFamily: "var(--ss-sans)",
                    textTransform: "capitalize",
                  }}
                >
                  {value}
                </button>
              );
            })}
          </div>
        </div>
        {mobile && <span className="ss-caps" style={{ color: "var(--ss-gray-700)" }}>view only</span>}
      </div>
    </div>
  );
}

function FurniturePalette({
  bedPlaced,
  boomPlaced,
  cartPlaced,
  roomDefaults,
  highlightItemId,
  bedRemovalReason,
  onBedRemovalReasonChange,
}) {
  return (
    <aside style={roomPanelStyle}>
      <div className="ss-caps" style={{ color: "var(--ss-terra-deep)", marginBottom: 10 }}>Furniture</div>
      <div style={{ display: "grid", gap: 8 }}>
        {ROOM_PALETTE.map((kind) => {
          const meta = ROOM_FURNITURE[kind];
          return (
            <div
              key={kind}
              data-furniture-kind={kind}
              draggable
              onDragStart={(event) => event.dataTransfer.setData(ROOM_DND.furnitureKind, kind)}
              style={{
                display: "flex",
                alignItems: "center",
                gap: 9,
                padding: "9px 10px",
                border: `1px solid ${meta.bd}`,
                borderRadius: 6,
                background: meta.bg,
                color: meta.fg,
                cursor: "grab",
              }}
            >
              <RoomKindIcon meta={meta} />
              <span style={{ display: "grid", gap: 2, minWidth: 0 }}>
                <span style={{ fontSize: 13, fontWeight: 600 }}>{meta.label}</span>
                {meta.paletteNote && <span style={{ fontSize: 10.5, color: "var(--ss-gray-700)", lineHeight: 1.2 }}>{meta.paletteNote}</span>}
              </span>
            </div>
          );
        })}
      </div>
      <RoomOptionalSetup
        bedPlaced={bedPlaced}
        boomPlaced={boomPlaced}
        cartPlaced={cartPlaced}
        roomDefaults={roomDefaults}
        highlightItemId={highlightItemId}
        reason={bedRemovalReason}
        onReasonChange={onBedRemovalReasonChange}
      />
    </aside>
  );
}

function RoomOptionalSetup({ bedPlaced, boomPlaced, cartPlaced, roomDefaults, highlightItemId, reason, onReasonChange }) {
  return (
    <div style={{ marginTop: 12, paddingTop: 12, borderTop: "1px solid var(--ss-line-soft)", display: "grid", gap: 7 }}>
      <div className="ss-caps" style={{ color: "var(--ss-terra-deep)" }}>Optional targets</div>
      <OptionalTargetStatus label="OR bed" placed={bedPlaced} />
      <OptionalTargetStatus label="Boom" placed={boomPlaced} />
      <OptionalTargetStatus label="Equip cart" placed={cartPlaced} />
      {!bedPlaced && (
        <input
          value={reason}
          onChange={(event) => onReasonChange(event.target.value)}
          placeholder="Reason: stretcher, inpatient bed..."
          style={roomInputStyle}
        />
      )}
      <RoomDefaultsList defaults={roomDefaults} highlightedSourceId={highlightItemId} />
    </div>
  );
}

function OptionalTargetStatus({ label, placed }) {
  return (
    <div style={{ display: "flex", justifyContent: "space-between", gap: 8, alignItems: "center", fontSize: 12, fontWeight: 700, color: placed ? "var(--ss-match-verified)" : "var(--ss-gray-700)" }}>
      <span>{label}</span>
      <span>{placed ? "placed" : "not placed"}</span>
    </div>
  );
}

function RoomCanvas({ furniture, itemCounts, mobile, selectedZone, onSelect, onAddFurniture, onMoveFurniture, onAssignItem }) {
  const canvasRef = React.useRef(null);

  function dropOnCanvas(event) {
    if (mobile) return;
    event.preventDefault();
    const rect = canvasRef.current.getBoundingClientRect();
    const kind = event.dataTransfer.getData(ROOM_DND.furnitureKind);
    const zoneLabel = event.dataTransfer.getData(ROOM_DND.furnitureId);
    const offset = parseOffset(event.dataTransfer.getData(ROOM_DND.furnitureOffset));
    if (kind) {
      onAddFurniture(kind, pointFromEvent(event, rect));
      return;
    }
    if (zoneLabel) {
      const row = furniture.find((candidate) => candidate.zone_label === zoneLabel);
      if (!row) return;
      const x = (event.clientX - rect.left - offset.x + (row.width_pct * rect.width) / 2) / rect.width;
      const y = (event.clientY - rect.top - offset.y + (row.height_pct * rect.height) / 2) / rect.height;
      onMoveFurniture(zoneLabel, { x: clamp01(x), y: clamp01(y) });
    }
  }

  return (
    <section style={{ width: "100%", minWidth: 0 }}>
      <div
        ref={canvasRef}
        data-room-canvas="true"
        onDragOver={(event) => !mobile && event.preventDefault()}
        onDrop={dropOnCanvas}
        style={{
          position: "relative",
          width: mobile ? "100vw" : "min(100%, 1100px)",
          aspectRatio: "4 / 3",
          margin: mobile ? "0 calc(50% - 50vw)" : "0 auto",
          border: "1px solid var(--ss-line)",
          borderRadius: mobile ? 0 : 6,
          backgroundColor: "var(--ss-cream)",
          backgroundImage: "linear-gradient(rgba(122,142,123,0.11) 1px, transparent 1px), linear-gradient(90deg, rgba(122,142,123,0.11) 1px, transparent 1px)",
          backgroundSize: mobile ? "28px 28px" : "34px 34px",
          boxShadow: "var(--ss-shadow-card)",
          overflow: "hidden",
        }}
      >
        {furniture.map((row) => (
          <FurniturePiece
            key={row.zone_label}
            row={row}
            count={itemCounts[row.zone_label] || 0}
            mobile={mobile}
            selected={selectedZone === row.zone_label}
            onSelect={onSelect}
            onAssignItem={onAssignItem}
          />
        ))}
      </div>
    </section>
  );
}

function FurniturePiece({ row, count, mobile, selected, onSelect, onAssignItem }) {
  const meta = ROOM_FURNITURE[row.furniture_kind] || ROOM_FURNITURE.custom;
  const title = furnitureTitle(row, meta);
  const subtitle = furnitureSubtitle(row, meta);
  return (
    <button
      type="button"
      data-zone-label={row.zone_label}
      aria-label={`${title} drag-and-drop widget`}
      draggable={!mobile}
      onClick={() => onSelect(row.zone_label)}
      onDragStart={(event) => {
        if (mobile) return;
        const rect = event.currentTarget.getBoundingClientRect();
        event.dataTransfer.setData(ROOM_DND.furnitureId, row.zone_label);
        event.dataTransfer.setData(ROOM_DND.furnitureOffset, JSON.stringify({ x: event.clientX - rect.left, y: event.clientY - rect.top }));
      }}
      onDragOver={(event) => !mobile && event.preventDefault()}
      onDrop={(event) => {
        if (mobile) return;
        const itemId = event.dataTransfer.getData(ROOM_DND.itemId);
        if (!itemId) return;
        event.preventDefault();
        event.stopPropagation();
        onAssignItem(itemId, row.zone_label);
      }}
      style={{
        position: "absolute",
        left: `${clamp01(row.position_x) * 100}%`,
        top: `${clamp01(row.position_y) * 100}%`,
        width: `${Math.max(0.06, row.width_pct) * 100}%`,
        height: `${Math.max(0.05, row.height_pct) * 100}%`,
        minWidth: mobile ? 66 : 0,
        minHeight: mobile ? 42 : 0,
        transform: `translate(-50%, -50%) rotate(${Number(row.rotation || 0)}deg)`,
        border: `2px solid ${selected ? "var(--ss-ink)" : meta.bd}`,
        borderRadius: 6,
        background: meta.bg,
        color: meta.fg,
        boxShadow: selected ? "0 0 0 3px rgba(27,26,24,0.10)" : "var(--ss-shadow-card)",
        padding: mobile ? 4 : 8,
        display: "grid",
        alignContent: "center",
        justifyItems: mobile ? "center" : "start",
        gap: mobile ? 1 : 3,
        fontFamily: "var(--ss-sans)",
        cursor: mobile ? "pointer" : "grab",
        overflow: "hidden",
      }}
    >
      <span style={{
        display: mobile ? "grid" : "inline-flex",
        alignItems: "center",
        justifyItems: mobile ? "center" : "start",
        gap: mobile ? 2 : 6,
        minWidth: 0,
        maxWidth: mobile ? "calc(100% - 20px)" : "100%",
      }}>
        <RoomKindIcon meta={meta} small />
        <span style={{
          fontSize: mobile ? 8.5 : 12,
          fontWeight: 700,
          lineHeight: mobile ? 1.05 : 1.2,
          textAlign: mobile ? "center" : "left",
          whiteSpace: mobile ? "normal" : "nowrap",
          overflow: "hidden",
          textOverflow: "ellipsis",
          overflowWrap: "anywhere",
        }}>{title}</span>
      </span>
      {!mobile && <span style={{ fontSize: 11, color: "var(--ss-gray-700)", whiteSpace: "nowrap" }}>{subtitle}</span>}
      {count > 0 && <span aria-label={`${count} assigned items`} className="ss-mono" style={{
        position: "absolute",
        top: mobile ? 3 : 5,
        right: mobile ? 3 : 5,
        minWidth: mobile ? 16 : 20,
        height: mobile ? 16 : 20,
        borderRadius: 99,
        background: "var(--ss-ink)",
        color: "var(--ss-cream)",
        display: "inline-flex",
        alignItems: "center",
        justifyContent: "center",
        fontSize: mobile ? 9 : 11,
        fontWeight: 700,
      }}>{count}</span>}
    </button>
  );
}

function RoomRightPanel({
  mobile,
  compact,
  selectedFurniture,
  assignedItems,
  unassignedItems,
  allItems,
  roomDefaults,
  highlightItemId,
  bedRemovalReason,
  onClose,
  onRotate,
  onDelete,
  onRemoveItem,
  onBedRemovalReasonChange,
}) {
  const [collapsed, setCollapsed] = React.useState(false);
  if (selectedFurniture) {
    const meta = ROOM_FURNITURE[selectedFurniture.furniture_kind] || ROOM_FURNITURE.custom;
    const title = furnitureTitle(selectedFurniture, meta);
    const needsBedReason = selectedFurniture.furniture_kind === "or_bed";
    const deleteDisabled = selectedFurniture.is_fixed;
    return (
      <aside style={{ ...roomPanelStyle, ...roomDrawerStyle(mobile), gridColumn: compact ? "1 / -1" : undefined }}>
        <div style={{ display: "flex", justifyContent: "space-between", gap: 10, alignItems: "start" }}>
          <div>
            <div className="ss-caps" style={{ color: "var(--ss-terra-deep)", marginBottom: 5 }}>Zone detail</div>
            <h2 style={{ margin: 0, fontSize: 18, color: "var(--ss-ink)" }}>{title}</h2>
            <div style={{ color: meta.fg, fontSize: 12, marginTop: 3 }}>{furnitureSubtitle(selectedFurniture, meta)}</div>
          </div>
          <button aria-label="Close zone detail" onClick={onClose} style={roomGhostButton}>Close</button>
        </div>
        <div style={{ display: "flex", gap: 8, marginTop: 14, flexWrap: "wrap" }}>
          {!mobile && (
            <span className="ss-mono" style={{ alignSelf: "center", color: "var(--ss-gray-700)", fontSize: 11 }}>
              angle {Number(selectedFurniture.rotation || 0)} deg
            </span>
          )}
          {!mobile && <button onClick={() => onRotate(selectedFurniture.zone_label, -15)} style={roomGhostButton}>Rotate -15</button>}
          {!mobile && <button onClick={() => onRotate(selectedFurniture.zone_label, 15)} style={roomGhostButton}>Rotate +15</button>}
          {!mobile && (
            <button
              disabled={deleteDisabled}
              onClick={() => onDelete(selectedFurniture.zone_label)}
              style={{ ...roomDangerButton, opacity: deleteDisabled ? 0.45 : 1, cursor: deleteDisabled ? "not-allowed" : "pointer" }}
            >
              {needsBedReason ? "Delete OR bed" : "Delete furniture"}
            </button>
          )}
        </div>
        <RoomDetailControls
          selectedFurniture={selectedFurniture}
          meta={meta}
          roomDefaults={roomDefaults}
          bedRemovalReason={bedRemovalReason}
          onBedRemovalReasonChange={onBedRemovalReasonChange}
        />
        <div style={{ marginTop: 16, display: "grid", gap: 8 }}>
          {assignedItems.length ? assignedItems.map((item) => (
            <SupplyRow key={item.id} item={item} actionLabel="Remove from zone" onAction={() => onRemoveItem(item.id)} mobile={mobile} />
          )) : <EmptyPanel text="No items assigned" />}
        </div>
      </aside>
    );
  }

  return (
    <aside style={{ ...roomPanelStyle, margin: mobile ? "0 12px" : 0, gridColumn: compact ? "1 / -1" : undefined }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, marginBottom: collapsed ? 0 : 10 }}>
        <div className="ss-caps" style={{ color: "var(--ss-terra-deep)" }}>Supply items</div>
        <button onClick={() => setCollapsed((value) => !value)} style={roomGhostButton}>
          {collapsed ? "Expand" : "Collapse"}
        </button>
      </div>
      {!collapsed && (
        <div style={{ display: "grid", gap: 8, maxHeight: mobile ? "none" : "62vh", overflow: "auto" }}>
          {mobile && <RoomDefaultsList defaults={roomDefaults} highlightedSourceId={highlightItemId} compact />}
          {(unassignedItems.length ? unassignedItems : allItems).slice(0, 80).map((item) => (
            <SupplyDragRow key={item.id} item={item} mobile={mobile} assigned={!unassignedItems.includes(item)} />
          ))}
          {!allItems.length && <EmptyPanel text="No card items loaded" />}
        </div>
      )}
    </aside>
  );
}

function RoomDetailControls({ selectedFurniture, meta, roomDefaults, bedRemovalReason, onBedRemovalReasonChange }) {
  const defaultsForZone = roomDefaultsForFurniture(roomDefaults, selectedFurniture.furniture_kind);

  if (selectedFurniture.furniture_kind === "boom" || selectedFurniture.furniture_kind === "equipment_cart") {
    return (
      <div style={roomDetailSectionStyle}>
        <div className="ss-caps" style={{ color: "var(--ss-terra-deep)" }}>Equipment target</div>
        <div style={{ fontSize: 12.5, color: "var(--ss-gray-700)", lineHeight: 1.35 }}>
          Drag supplies onto this widget to assign equipment here.
        </div>
        <RoomDefaultsList defaults={defaultsForZone} compact />
      </div>
    );
  }

  if (selectedFurniture.furniture_kind === "custom") {
    return (
      <div style={roomDetailSectionStyle}>
        <div className="ss-caps" style={{ color: "var(--ss-terra-deep)" }}>GUDID lookup</div>
        <div style={{ display: "flex", gap: 8 }}>
          <input aria-label="Search GUDID for custom item" placeholder="Search device name or UDI" style={{ ...roomInputStyle, flex: 1 }} />
          <button type="button" style={roomGhostButton}>Lookup</button>
        </div>
      </div>
    );
  }

  if (selectedFurniture.furniture_kind === "or_bed") {
    return (
      <div style={roomDetailSectionStyle}>
        <div className="ss-caps" style={{ color: "var(--ss-terra-deep)" }}>Removal reason</div>
        <input
          value={bedRemovalReason}
          onChange={(event) => onBedRemovalReasonChange(event.target.value)}
          placeholder="Stretcher, inpatient bed, positioning access..."
          style={roomInputStyle}
        />
      </div>
    );
  }

  if (defaultsForZone.length) {
    return (
      <div style={roomDetailSectionStyle}>
        <div className="ss-caps" style={{ color: "var(--ss-terra-deep)" }}>Room default</div>
        <RoomDefaultsList defaults={defaultsForZone} compact />
      </div>
    );
  }

  return null;
}

function RoomDefaultsList({ defaults, compact = false, highlightedSourceId = null }) {
  const rowRefs = React.useRef({});
  const [activeHighlight, setActiveHighlight] = React.useState(Boolean(highlightedSourceId));
  React.useEffect(() => {
    if (!highlightedSourceId) return undefined;
    setActiveHighlight(true);
    const node = rowRefs.current[String(highlightedSourceId)];
    node?.scrollIntoView?.({ block: "center", behavior: "smooth" });
    const timer = window.setTimeout(() => setActiveHighlight(false), 2200);
    return () => window.clearTimeout(timer);
  }, [highlightedSourceId]);
  if (!defaults || !defaults.length) return null;
  return (
    <div data-room-defaults="true" style={{ display: "grid", gap: compact ? 6 : 7, marginTop: compact ? 2 : 6 }}>
      {!compact && <div className="ss-caps" style={{ color: "var(--ss-gray-700)" }}>Room defaults</div>}
      {defaults.map((entry) => {
        const highlighted = highlightedSourceId && String(entry.sourceId) === String(highlightedSourceId) && activeHighlight;
        return (
          <div
            key={entry.id}
            ref={(node) => {
              if (entry.sourceId != null) rowRefs.current[String(entry.sourceId)] = node;
            }}
            style={{
              display: "grid",
              gap: 2,
              padding: compact ? "7px 8px" : "8px 9px",
              borderRadius: 5,
              border: highlighted ? "2px solid #E1CC9E" : "1px solid #C9D8CC",
              background: highlighted ? "var(--ss-open-bg)" : "rgba(231,239,233,0.72)",
              boxShadow: highlighted ? "0 0 0 4px rgba(225,204,158,0.35)" : "none",
              transition: "box-shadow 180ms ease, background 180ms ease, border-color 180ms ease",
            }}
          >
            <span style={{ fontSize: 12, fontWeight: 700, color: "var(--ss-match-verified)" }}>{entry.defaultLabel}</span>
            <span style={{ fontSize: 11, color: "var(--ss-gray-700)", lineHeight: 1.25 }}>
              {entry.reasonLabel}{entry.sourceName ? `: ${entry.sourceName}` : ""}
            </span>
          </div>
        );
      })}
    </div>
  );
}

function SupplyDragRow({ item, mobile, assigned }) {
  return (
    <div
      data-item-id={item.id}
      draggable={!mobile}
      onDragStart={(event) => event.dataTransfer.setData(ROOM_DND.itemId, String(item.id))}
      style={{
        padding: "9px 10px",
        border: "1px solid var(--ss-line-soft)",
        borderRadius: 6,
        background: assigned ? "var(--ss-cream)" : "var(--ss-warm-white)",
        cursor: mobile ? "default" : "grab",
      }}
    >
      <SupplyText item={item} />
    </div>
  );
}

function SupplyRow({ item, actionLabel, onAction, mobile }) {
  return (
    <div style={{ padding: "9px 10px", border: "1px solid var(--ss-line-soft)", borderRadius: 6, background: "var(--ss-cream)" }}>
      <SupplyText item={item} />
      {!mobile && <button onClick={onAction} style={{ ...roomGhostButton, marginTop: 8 }}>{actionLabel}</button>}
    </div>
  );
}

function SupplyText({ item }) {
  const qty = (item.qty_open ?? item.expected ?? 1) + (item.qty_hold ?? 0);
  return (
    <>
      <div style={{ fontSize: 12.5, fontWeight: 600, color: "var(--ss-ink)", lineHeight: 1.25 }}>{item.item_name || item.name || "Unnamed item"}</div>
      <div style={{ marginTop: 4, display: "flex", gap: 6, flexWrap: "wrap", alignItems: "center" }}>
        <span className="ss-mono" style={{ fontSize: 10.5, color: "var(--ss-gray-700)" }}>qty {qty}</span>
        {item.section && <span style={{ fontSize: 10.5, color: "var(--ss-gray-700)" }}>{item.section}</span>}
        {(item.vendor || item.mfg) && <span style={{ fontSize: 10.5, color: "var(--ss-gray-500)" }}>{item.vendor || item.mfg}</span>}
      </div>
    </>
  );
}

function RoomMobileHeader({ card }) {
  const maintainer = card?.maintainer || "Maintainer";
  return (
    <header style={{ background: "var(--ss-cream)", borderBottom: "1px solid var(--ss-line)", padding: "10px 16px", display: "flex", alignItems: "center", gap: 10 }}>
      <a href="/cards" aria-label="ScrubStack home" style={{ width: 132, height: 18, overflow: "hidden", position: "relative", display: "inline-block", flexShrink: 0, textDecoration: "none" }}>
        <img src="/static/og/scrubstack-logo-transparent.png?v=3" alt="ScrubStack" style={{ position: "absolute", width: 166, height: "auto", left: -13, top: -32, display: "block" }} />
      </a>
      <div style={{ flex: 1 }} />
      <a href="/cards" style={{ padding: "5px 9px", borderRadius: 4, background: "rgba(0,0,0,0.04)", color: "var(--ss-ink)", fontSize: 12.5, fontWeight: 500, textDecoration: "none" }}>Prefcards</a>
      <window.Avatar name={maintainer} size={26} />
    </header>
  );
}

function RoomKindIcon({ meta, small = false }) {
  return (
    <span className="ss-mono" style={{
      width: small ? 22 : 28,
      height: small ? 22 : 28,
      borderRadius: 4,
      border: `1px solid ${meta.bd}`,
      background: "rgba(250,247,242,0.55)",
      display: "inline-flex",
      alignItems: "center",
      justifyContent: "center",
      fontSize: small ? 9 : 10,
      fontWeight: 700,
      color: meta.fg,
      flexShrink: 0,
    }}>
      {meta.glyph ? <RoomFurnitureGlyph kind={meta.glyph} color={meta.fg} small={small} /> : meta.icon}
    </span>
  );
}

function RoomFurnitureGlyph({ kind, color, small }) {
  if (kind === "table_wheels") {
    const stroke = color || "currentColor";
    return (
      <svg aria-hidden="true" viewBox="0 0 24 24" width={small ? 17 : 21} height={small ? 17 : 21} fill="none" stroke={stroke} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
        <path d="M4.5 8.5h15" />
        <path d="M5.5 10.8h13" />
        <path d="M6.5 8.5v2.3" />
        <path d="M17.5 8.5v2.3" />
        <path d="M7.5 10.8v6" />
        <path d="M16.5 10.8v6" />
        <path d="M6.4 18h11.2" />
        <circle cx="7.5" cy="19.1" r="1.1" />
        <circle cx="16.5" cy="19.1" r="1.1" />
      </svg>
    );
  }
  if (kind === "mayo_stand") {
    const stroke = color || "currentColor";
    return (
      <svg aria-hidden="true" viewBox="0 0 24 24" width={small ? 17 : 21} height={small ? 17 : 21} fill="none" stroke={stroke} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
        <path d="M7 6.5h10l-1 4.4H8L7 6.5Z" />
        <path d="M12 10.9v7.2" />
        <path d="M8.8 18.1h6.4" />
        <circle cx="8.2" cy="19.2" r="1.1" />
        <circle cx="15.8" cy="19.2" r="1.1" />
      </svg>
    );
  }
  if (kind === "equipment_cart") {
    const stroke = color || "currentColor";
    return (
      <svg aria-hidden="true" viewBox="0 0 24 24" width={small ? 17 : 21} height={small ? 17 : 21} fill="none" stroke={stroke} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
        <path d="M6.5 7.5h10.5l1 6.5H5.5l1-6.5Z" />
        <path d="M7.5 10.5h9" />
        <path d="M8.5 14v3.1" />
        <path d="M15.5 14v3.1" />
        <circle cx="8.5" cy="18.4" r="1.1" />
        <circle cx="15.5" cy="18.4" r="1.1" />
      </svg>
    );
  }
  if (kind === "bovie_pencil") {
    const stroke = color || "currentColor";
    return (
      <svg aria-hidden="true" viewBox="0 0 24 24" width={small ? 17 : 21} height={small ? 17 : 21} fill="none" stroke={stroke} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
        <path d="M5.5 16.5l9.8-9.8 2 2-9.8 9.8-3 .8.8-2.8Z" />
        <path d="M13.8 8.2l2 2" />
        <path d="M17.2 6.8l1.2-1.2" />
        <path d="M7.8 16.2l1.5 1.5" />
      </svg>
    );
  }
  if (kind === "suction_canister") {
    const stroke = color || "currentColor";
    return (
      <svg aria-hidden="true" viewBox="0 0 24 24" width={small ? 17 : 21} height={small ? 17 : 21} fill="none" stroke={stroke} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
        <path d="M8 7.2h8" />
        <path d="M9 7.2l.7 11h4.6l.7-11" />
        <path d="M9.6 11.5h4.8" />
        <path d="M16 9.2c2.4.2 3.5 1.4 3.5 3.2 0 1.4-.9 2.3-2.1 2.6" />
        <path d="M12 5.2v2" />
      </svg>
    );
  }
  if (kind === "xray") {
    const stroke = color || "currentColor";
    return (
      <svg aria-hidden="true" viewBox="0 0 24 24" width={small ? 17 : 21} height={small ? 17 : 21} fill="none" stroke={stroke} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
        <circle cx="12" cy="12" r="2" />
        <path d="M12 4.8v4.2" />
        <path d="M12 15v4.2" />
        <path d="M5.1 8l3.6 2.1" />
        <path d="M15.3 13.9l3.6 2.1" />
        <path d="M18.9 8l-3.6 2.1" />
        <path d="M8.7 13.9L5.1 16" />
      </svg>
    );
  }
  if (kind === "prep_stick") {
    const stroke = color || "currentColor";
    return (
      <svg aria-hidden="true" viewBox="0 0 24 24" width={small ? 17 : 21} height={small ? 17 : 21} fill="none" stroke={stroke} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
        <path d="M6.5 17.5l8.8-8.8" />
        <path d="M14 7.4l2.6-2.6 2.6 2.6-2.6 2.6Z" />
        <path d="M5.2 18.8l2 2" />
        <path d="M9.4 14.6l2 2" />
      </svg>
    );
  }
  return null;
}

function RoomBanner({ tone, message, onRetry }) {
  const error = tone === "error";
  return (
    <div style={{ margin: "12px 32px 0", border: `1px solid ${error ? "#DAB5AC" : "#E1CC9E"}`, background: error ? "var(--ss-match-reject-bg)" : "var(--ss-open-bg)", color: error ? "var(--ss-match-reject)" : "#7A5615", borderRadius: 6, padding: "10px 12px", display: "flex", justifyContent: "space-between", gap: 12 }}>
      <span style={{ fontSize: 13 }}>{message}</span>
      {onRetry && <button onClick={onRetry} style={roomGhostButton}>Retry</button>}
    </div>
  );
}

function EmptyPanel({ text }) {
  return <div style={{ border: "1px dashed var(--ss-line)", borderRadius: 6, padding: 12, color: "var(--ss-gray-700)", fontSize: 12.5 }}>{text}</div>;
}

function applyRoomPayload(payload, setFurniture, setItems, setAssignments) {
  const rows = (payload.room_layout || []).map((row) => ({
    ...row,
    is_fixed: Boolean(row.is_fixed),
    position_x: Number(row.position_x),
    position_y: Number(row.position_y),
    rotation: Number(row.rotation || 0),
    width_pct: Number(row.width_pct || ROOM_FURNITURE[row.furniture_kind]?.w || 0.12),
    height_pct: Number(row.height_pct || ROOM_FURNITURE[row.furniture_kind]?.h || 0.08),
  }));
  const flatItems = [];
  const nextAssignments = {};
  for (const [zone, zoneItems] of Object.entries(payload.items_by_zone || {})) {
    for (const item of zoneItems || []) {
      if (!flatItems.some((existing) => String(existing.id) === String(item.id))) flatItems.push(item);
      if (zone !== "_unassigned") nextAssignments[String(item.id)] = zone;
    }
  }
  setFurniture(rows.length ? rows : ROOM_SEED);
  setItems(flatItems);
  setAssignments(nextAssignments);
}

function fallbackItems() {
  return (window.PREFCARD?.supplies || []).map((item, index) => ({
    id: numericItemId(item.id) || index + 1,
    item_name: item.name,
    vendor: item.mfg,
    qty_open: item.expected || 1,
    qty_hold: 0,
    section: item.phase,
  }));
}

function countAssignments(assignments) {
  return Object.values(assignments).reduce((acc, zone) => {
    if (zone) acc[zone] = (acc[zone] || 0) + 1;
    return acc;
  }, {});
}

function inferredRoomDefaults(items) {
  const searchableItems = items.map((item) => ({
    id: item.id,
    name: item.item_name || item.name || item.raw_text || "Card item",
    text: itemSearchText(item),
  }));
  return ROOM_DEFAULT_RULES.flatMap((rule) => {
    const explicitEquipment = searchableItems.some((item) => rule.equipmentPattern.test(item.text));
    if (explicitEquipment) return [];
    const source = searchableItems.find((item) => rule.accessoryPattern.test(item.text));
    if (!source) return [];
    return [{
      id: rule.id,
      sourceId: source.id,
      label: rule.label,
      defaultLabel: rule.defaultLabel,
      reasonLabel: rule.reasonLabel,
      sourceName: source.name,
      targetKinds: rule.targetKinds,
    }];
  });
}

function roomDefaultsForFurniture(roomDefaults, kind) {
  return (roomDefaults || []).filter((entry) => (entry.targetKinds || []).includes(kind));
}

function itemSearchText(item) {
  return [
    item.item_name,
    item.name,
    item.raw_text,
    item.vendor,
    item.mfg,
    item.section,
  ].filter(Boolean).join(" ");
}

function furnitureTitle(row, meta) {
  return meta.label;
}

function furnitureSubtitle(row, meta) {
  if (row.is_fixed && meta.fixedLabel) return meta.fixedLabel;
  if (row.furniture_kind === "boom") return "ESU / lap equipment";
  if (row.furniture_kind === "equipment_cart") return "assigned supplies";
  if (row.furniture_kind === "custom") return "GUDID lookup";
  return row.zone_label;
}

function itemsForZone(items, assignments, zoneLabel) {
  return items.filter((item) => assignments[String(item.id)] === zoneLabel);
}

function clearZone(assignments, zoneLabel) {
  const next = { ...assignments };
  for (const [id, zone] of Object.entries(next)) if (zone === zoneLabel) next[id] = null;
  return next;
}

function nextZoneLabel(furniture, kind) {
  const base = kind.replaceAll("_", "-");
  const used = new Set(furniture.map((row) => row.zone_label));
  let n = 1;
  while (used.has(`${base}-${n}`)) n += 1;
  return `${base}-${n}`;
}

function pointFromEvent(event, rect) {
  return {
    x: clamp01((event.clientX - rect.left) / rect.width),
    y: clamp01((event.clientY - rect.top) / rect.height),
  };
}

function parseOffset(raw) {
  try { return JSON.parse(raw || "{}"); }
  catch (_) { return { x: 0, y: 0 }; }
}

function clamp01(value) {
  if (!Number.isFinite(value)) return 0.5;
  return Math.max(0, Math.min(1, value));
}

function numericItemId(value) {
  if (typeof value === "number") return value;
  const match = String(value || "").match(/\d+/);
  return match ? Number(match[0]) : NaN;
}

function roomStorageKey(cardId, name) {
  return `${ROOM_STORAGE_PREFIX}:${cardId}:${name}`;
}

function readRoomSetting(cardId, name, fallback) {
  try {
    const raw = window.localStorage.getItem(roomStorageKey(cardId, name));
    return raw ? JSON.parse(raw) : fallback;
  } catch (_) {
    return fallback;
  }
}

function writeRoomSetting(cardId, name, value) {
  try {
    window.localStorage.setItem(roomStorageKey(cardId, name), JSON.stringify(value));
  } catch (_) {
    // Local prototype persistence is best-effort.
  }
}

function roomDrawerStyle(mobile) {
  if (mobile) {
    return {
      position: "fixed",
      left: 12,
      right: 12,
      bottom: 12,
      zIndex: 20,
      maxHeight: "72vh",
      overflow: "auto",
    };
  }
  return {
    position: "fixed",
    top: 96,
    right: 24,
    zIndex: 20,
    width: 340,
    maxHeight: "calc(100vh - 120px)",
    overflow: "auto",
  };
}

const roomPanelStyle = {
  background: "var(--ss-warm-white)",
  border: "1px solid var(--ss-line)",
  borderRadius: 6,
  boxShadow: "var(--ss-shadow-card)",
  padding: 14,
};

const roomGhostButton = {
  padding: "6px 10px",
  borderRadius: 5,
  background: "transparent",
  border: "1px solid var(--ss-line)",
  color: "var(--ss-gray-900)",
  fontSize: 12,
  fontWeight: 500,
  cursor: "pointer",
  fontFamily: "var(--ss-sans)",
};

const roomPrimaryButton = {
  padding: "7px 12px",
  borderRadius: 5,
  background: "var(--ss-ink)",
  border: "1px solid var(--ss-ink)",
  color: "var(--ss-cream)",
  fontSize: 12.5,
  fontWeight: 600,
  cursor: "pointer",
  fontFamily: "var(--ss-sans)",
};

const roomDangerButton = {
  ...roomGhostButton,
  border: "1px solid #DAB5AC",
  color: "var(--ss-match-reject)",
};

const roomInputStyle = {
  width: "100%",
  minWidth: 0,
  boxSizing: "border-box",
  border: "1px solid var(--ss-line)",
  borderRadius: 5,
  background: "var(--ss-cream)",
  color: "var(--ss-ink)",
  padding: "7px 9px",
  fontSize: 12.5,
  fontFamily: "var(--ss-sans)",
};

const roomDetailSectionStyle = {
  marginTop: 14,
  display: "grid",
  gap: 8,
  paddingTop: 14,
  borderTop: "1px solid var(--ss-line-soft)",
};

const roomToastStyle = {
  position: "fixed",
  right: 24,
  bottom: 24,
  zIndex: 30,
  padding: "10px 14px",
  borderRadius: 6,
  background: "var(--ss-match-verified-bg)",
  border: "1px solid #B9D0BE",
  color: "var(--ss-match-verified)",
  boxShadow: "var(--ss-shadow-pop)",
  fontSize: 13,
  fontWeight: 600,
};

Object.assign(window, { RoomLayoutView });
