// Surface — Organisations & roles
//
// Split out of the old combined Roles surface (see §25.14). This page owns
// ONLY the DEFINITION side:
//   · create / rename / recolour / delete organisations (each shown as a
//     module in the page body — see §25.10),
//   · create / rename / delete roles,
//   · assign each role to an organisation (role → org mapping).
// The content-binding matrix (roles ↔ modules / assessment questions) lives
// on its own "Content mapping" surface — never on this page.
//
// MODEL (§25.10): there is NO grouping toggle. Organisations are created
// from the page-header "Add organisation" button and render as modules in
// the body. "Grouped" is derived purely from `brands.length > 0`. The
// schema flag `course.selectBrand` (Player <selectBrand> tag) maps to
// "are there any organisations?" — it is never a UI control here.

// PERSISTENCE (Stage 5 Phase 2b). `roles` and `onUpdateRoles` come from
// `courseSettings.roles`, so everything typed here survives a reload and reaches
// the ZIP. Three things this rewrite deliberately changed:
//
//  1. `roles` + `roleCfg` used to be TWO copies of the same fact — an array of
//     `{code,label,brand}` plus a map of `{label,brand}`, with the rows reading
//     the map and deletes maintaining both. Once persisted they could diverge in
//     the database, so they are now ONE array (`feedback_one_rule_one_place`).
//  2. `label` is a LocalizedString. The row edits `label[defaultLang]` ONLY —
//     never the object — because binding the whole object to a text input blanks
//     the screen and loses the other languages
//     (`feedback_localizedstring_bound_to_plain_widget`). Other languages come
//     from Localisation › Roles.
//  3. Nothing is seeded. Before this, `rolesStartBlank` defaulted to false so a
//     brand-new course displayed three `SAMPLE_ROLES` personas it did not own
//     and never saved.
// PERSISTENCE FOR ORGANISATIONS (Phase 4b). Organisations used to live in a
// local `React.useState` seeded from `course.brands`, which meant they died on
// navigation and never reached the server — and the role→organisation
// ASSIGNMENT was dropped too, one layer down, by the canonical role projection.
// They now come from `courseSettings.brands` through props, exactly as `roles`
// have since Phase 2b. Three shape changes came with that, each forced rather
// than chosen:
//
//  1. `id` → `code`. The schema calls it `code`, `sample-content.jsx`'s
//     brand-select fixture calls it `code`, and roles call it `code`. Three
//     precedents against one, and one name beats a rename in the projection —
//     the layer where a `moduleId` translation once cost 47 green-but-wrong
//     tests (`feedback_an_adapter_and_its_fixture_share_one_misreading`).
//  2. `label` is a LocalizedString, not a plain string. Not tidiness: the Player
//     renders an organisation pill only when `label_text[selected_language]` is
//     defined (`js/scripts.js:3889`), so an organisation unlabelled in Italian is
//     offered to NOBODY Italian — and every role inside it becomes unreachable
//     with it. As with roles, the row edits `label[defaultLang]` ONLY, never the
//     object (`feedback_localizedstring_bound_to_plain_widget`); other languages
//     come from Localisation.
//  3. `color`/`textColor` persist but are AUTHORING-ONLY. They cannot reach the
//     learner — theming resolves a CSS folder name (`changeBrandingTo`,
//     `js/scripts.js:7146`) and the pinned runtime ships exactly one,
//     `css/branding/brand_BT/`, so a hex has nowhere to go. A per-organisation
//     LOGO is different and IS supported; that is Phase 4d.
//
//     ★ This paragraph used to end "and the header says so rather than implying
//     otherwise". It did not say so — the header read only "Define the audiences
//     for this course…", and the swatch's tooltip was the neutral "Click to change
//     the organisation colour". So the one fact an author could not discover by
//     looking was documented HERE, where only I would read it, and the screen kept
//     offering a colour it would not honour. Written the same day as the logo bug
//     that had the identical cause (`feedback_a_comment_is_not_a_contract`).
//     Phase 4c makes it true: the swatch tooltip and a note under the bands both
//     say it, and both contrast it with the logo, which learners DO see.
function SurfaceOrgRoles({ roles, onUpdateRoles, brands, onUpdateBrands,
  course, defaultLang, startBlank }) {
  const [focusRole, setFocusRole] = React.useState(null);
  const lang = defaultLang || 'en';

  // Grouping is derived, not toggled: any organisation → grouped view.
  const grouped = brands.length > 0;

  const setRoles = (fn) => onUpdateRoles(typeof fn === 'function' ? fn(roles) : fn);
  const setBrands = (fn) => onUpdateBrands(typeof fn === 'function' ? fn(brands) : fn);

  // The name in the course's default language — the only entry this surface
  // edits, and what every list here displays.
  const nameOf = (r) => (r && r.label && typeof r.label === 'object' ? (r.label[lang] || '') : '');

  // ── Organisation mutators ───────────────────────────────────────────────
  const addBrand = () => {
    // Same fixed-length base-36 stamp as `addRole`, and it satisfies
    // `BrandSchema.code` (`^[a-z][a-z0-9_]{1,31}$`). Unlike role codes there is
    // no substring hazard to avoid here — organisation matching in the Player is
    // exact — but a stable machine-minted code still matters, because it lands in
    // the `data-brand` DOM attribute and in SCORM suspend data, so renaming the
    // organisation must never change its identity.
    const code = 'b' + Date.now().toString(36);
    const palette = ['#5C2D91', '#1B2A3D', '#0F766E', '#B91C1C', '#B45309', '#1D4ED8'];
    const color = palette[brands.length % palette.length];
    setBrands(bs => [...bs, {
      code, label: { [lang]: 'New organisation' }, color, textColor: '#ffffff',
    }]);
  };
  // `patch` may carry `name` (the default-language label), `color` or `textColor`.
  // `name` is folded into `label[lang]` so the other languages survive — a patch
  // that replaced `label` wholesale would silently drop every translation, which
  // is the same trap `updateRole` documents below.
  const updateBrand = (code, patch) =>
    setBrands(bs => bs.map(b => {
      if (b.code !== code) return b;
      const next = { ...b };
      if ('color' in patch) next.color = patch.color;
      if ('textColor' in patch) next.textColor = patch.textColor;
      if ('name' in patch) next.label = { ...(b.label || {}), [lang]: patch.name };
      // Phase 4d. `null` (the slot's "remove") DELETES the key rather than storing
      // a null: `BrandSchema.logo` is `z.string().optional()` and `.strict()`, so a
      // null would 400 the whole course on save — the same shape of mistake as
      // `role.brand`, which needed `.nullable()` for exactly this reason. Deleting
      // is also the honest representation: no logo is an ABSENT logo, not a null one
      // (`feedback_absence_and_emptiness_read_the_same`).
      if ('logo' in patch) {
        if (patch.logo) next.logo = patch.logo;
        else delete next.logo;
      }
      return next;
    }));
  const removeBrand = (code) => {
    setBrands(bs => bs.filter(b => b.code !== code));
    // Roles on the removed org reset to null and drop into the "No
    // organisation" module — never silently moved elsewhere.
    //
    // That leaves the course in a state that SAVES fine and cannot be EXPORTED
    // until the author reassigns those roles, because an unassigned role is
    // invisible to learners once organisations exist
    // (`organisation-gate.ts`). Deliberate on both counts: inventing a new home
    // for someone's roles would be worse than asking, and an in-progress state
    // must never block a save — an earlier version of this checked it in the
    // schema, which made this very click return 400 and blocked saving the
    // author's unrelated work too.
    setRoles(rs => rs.map(r => r.brand === code ? { ...r, brand: null } : r));
  };

  // ── Role mutators ────────────────────────────────────────────────────────
  // brandId === undefined → unassigned (lands in "No organisation").
  // The page-header "Add role" NEVER auto-assigns to an organisation; only
  // an organisation module's own "Add role" passes its brand id (§25.10).
  const addRole = (brandId = null) => {
    // Fixed-length base-36 stamp. It satisfies `RoleSchema.code`
    // (`^[a-z][a-z0-9_]{1,31}$`) and — being the same length every time — can
    // never be a substring of a sibling code, which the Player's substring role
    // matcher would otherwise turn into a role seeing another's questions.
    const code = 'r' + Date.now().toString(36);
    // A new role starts seeing EVERY module, listed explicitly.
    //
    // Inferred from Omar's Q4 ("a new module is visible to every role") rather
    // than stated by him: the symmetric case — a new ROLE — was not asked about,
    // and the same principle applies, nothing an author creates is silently
    // invisible. Explicit matters as much as the default: an EMPTY
    // `<modules_list>` does not mean "sees nothing" to the Player, it INVERTS and
    // shows every module anyway — so listing the ids gives identical behaviour
    // while being readable in the XML, and avoids the Q2 warning firing on a role
    // the author has not misconfigured.
    const allModuleIds = ((course && course.modules) || []).map(m => m.id);
    setRoles(rs => [...rs, {
      code, label: { [lang]: 'New role' }, brand: brandId, modules: allModuleIds,
    }]);
    setFocusRole(code);
  };
  // `patch` may carry `name` (the default-language label) and/or `brand`.
  // `name` is folded into `label[lang]` so the other languages survive — a patch
  // that replaced `label` wholesale would silently drop every translation.
  const updateRole = (code, patch) =>
    setRoles(rs => rs.map(r => {
      if (r.code !== code) return r;
      const next = { ...r };
      if ('brand' in patch) next.brand = patch.brand;
      if ('name' in patch) next.label = { ...(r.label || {}), [lang]: patch.name };
      return next;
    }));
  const removeRole = (code) => setRoles(rs => rs.filter(r => r.code !== code));

  // ── Bands: one module per organisation + a trailing "No organisation" ─────
  const bands = React.useMemo(() => {
    if (!grouped) return [{ brand: null, roles }];
    const out = brands.map(b => ({
      brand: b, roles: roles.filter(r => r.brand === b.code),
    }));
    const unassigned = roles.filter(r => !brands.some(x => x.code === r.brand));
    // Always show the "No organisation" module when grouped — it is the
    // landing spot for header-added roles and the reassignment source.
    //
    // Its label is a LocalizedString like a real organisation's, so `nameOf`
    // works on it unchanged. A plain string here would render as blank the
    // moment anything read it through the same helper — the shape mismatch that
    // makes one branch of a list quietly different from the others.
    out.push({
      brand: {
        code: '__none', label: { [lang]: 'No organisation' },
        color: 'var(--border-strong)',
      },
      roles: unassigned,
    });
    return out;
  }, [grouped, brands, roles, lang]);

  const empty = roles.length === 0 && brands.length === 0;

  return (
    <div data-screen-label="Surface · Organisations & roles" style={{
      display: 'flex', flexDirection: 'column', height: '100%', background: 'var(--bg)',
    }}>
      {/* The Save button compares this string to decide whether there is
          anything unsaved. It MUST include organisations: without them the button
          would keep reading "Saved to the server" over organisation edits made
          after the last save — the exact false-reassurance the header carried
          before PR #129 (`project_roles_save_doors_built` note 2). */}
      <OrgRolesHeader onAddRole={() => addRole()} onAddOrganisation={addBrand}
        dirtySignal={JSON.stringify([roles, brands])} />

      <div style={{ flex: 1, overflowY: 'auto', padding: '20px 24px 40px' }}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          {empty ? (
            <EmptyRolesState onAddRole={() => addRole()} onAddOrganisation={addBrand} />
          ) : (
            <React.Fragment>
              {bands.map(({ brand, roles: bandRoles }) => (
                <RoleBand key={brand ? brand.code : '_all'}
                  brand={brand} grouped={grouped} roles={bandRoles}
                  brands={brands} nameOf={nameOf} focusRole={focusRole}
                  onAddRole={() => addRole(brand && brand.code !== '__none' ? brand.code : null)}
                  onUpdateRole={updateRole} onRemoveRole={removeRole}
                  onUpdateBrand={updateBrand} onRemoveBrand={removeBrand} />
              ))}
              <p style={{ margin: '2px 2px 0', fontSize: 11.5, color: 'var(--text-faint)',
                display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                <I.Globe size={11} />Role names are translated in Localisation › Roles.
              </p>
              {/* The colour's reach, said once on the screen rather than once per
                  organisation — the same quiet-note idiom as the line above, and NOT
                  `PreviewNote`, whose warning colour means "this feature is not built".
                  The colour IS built and does persist; what is bounded is where it
                  applies. Naming the logo in the same breath is the point: the two
                  controls sit on one row and only one of them reaches a learner. */}
              {grouped && (
                <p style={{ margin: '2px 2px 0', fontSize: 11.5, color: 'var(--text-faint)',
                  display: 'flex', alignItems: 'center', gap: 6 }}>
                  <I.Info size={11} style={{ flexShrink: 0 }} />
                  <span>Organisation colours are for finding your way around here — learners
                    never see them. A logo is the part they do see, in the course header and
                    on the Home screen.</span>
                </p>
              )}
            </React.Fragment>
          )}
        </div>
      </div>
    </div>
  );
}

// ── Page header ─────────────────────────────────────────────────────────────
function OrgRolesHeader({ onAddRole, onAddOrganisation, dirtySignal }) {
  return (
    // The style this screen used to own is now `SurfaceHeader`, shared by all six
    // surfaces. Its sentence also stops wrapping: `maxWidth: 680` put it on two
    // lines here and one line on Content mapping, which is the divergence Omar
    // spotted when he asked for a single header style.
    <window.SurfaceHeader title="Organisations & roles"
      description={<>Define the audiences for this course and, optionally, the organisations
        they belong to. Decide <em>what</em> each role sees over in <strong>Content mapping</strong>.</>}>
      <button className="btn sm" onClick={onAddOrganisation}
        title="Add an organisation. Roles can be assigned to it, or left unassigned.">
        <I.Plus size={12} />Add organisation
      </button>
      <button className="btn sm primary" onClick={onAddRole}>
        <I.Plus size={12} />Add role
      </button>
      {/* THE MISSING DOOR. This screen had no way to reach the server at all, so
          every role an author created lived in that one browser's IndexedDB until
          some UNRELATED screen's Save (or a Build) happened to push the whole
          aggregate. Omar created an "HR" role in Chrome on 2026-08-11 and Edge
          could not see it, because it had never been sent. `dirtySignal` is the
          role set itself, so the button stops claiming "Saved to the server" the
          moment another role is added or renamed. */}
      <window.DraftSaveButton dirtySignal={dirtySignal}
        title="Write these roles to the server, so they survive a cleared browser and can be opened on another machine" />
    </window.SurfaceHeader>
  );
}

// ── RoleBand · one organisation module (or the flat list) ────────────────────
function RoleBand({ brand, grouped, roles, brands, nameOf, focusRole,
  onAddRole, onUpdateRole, onRemoveRole, onUpdateBrand, onRemoveBrand }) {
  const isNone = brand && brand.code === '__none';
  const isOrg = grouped && brand && !isNone;
  return (
    <div style={{ background: 'var(--surface)', border: '1px solid var(--border)',
      borderRadius: 'var(--radius-md)', overflow: 'hidden' }}>
      {grouped && (
        isOrg ? (
          <OrgBandHeader brand={brand} name={nameOf(brand)} count={roles.length}
            onUpdate={patch => onUpdateBrand(brand.code, patch)}
            onRemove={() => onRemoveBrand(brand.code)}
            onAddRole={onAddRole} hasRoles={roles.length > 0} />
        ) : (
          <div style={{ display: 'flex', alignItems: 'center', gap: 10,
            padding: '10px 14px', borderBottom: roles.length ? '1px solid var(--border)' : 0,
            background: 'var(--surface-inset)' }}>
            <span style={{ width: 12, height: 12, borderRadius: 3, flexShrink: 0,
              background: brand.color, border: '1px dashed var(--border-strong)' }} />
            <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--text-muted)' }}>{nameOf(brand)}</span>
            <span className="pill" style={{ fontSize: 10 }}>{roles.length}</span>
            <div style={{ flex: 1 }} />
            <button className="btn sm ghost" onClick={onAddRole}>
              <I.Plus size={11} />Add role
            </button>
          </div>
        )
      )}

      {roles.length === 0 ? (
        <div style={{ padding: '14px', fontSize: 12, color: 'var(--text-faint)' }}>
          {isNone ? 'No unassigned roles. New roles added from the header land here.'
            : 'No roles in this organisation yet.'}
        </div>
      ) : (
        roles.map(r => (
          <RoleRow key={r.code} role={r} name={nameOf(r)} nameOf={nameOf} grouped={grouped}
            brands={brands} autoFocus={focusRole === r.code}
            onUpdate={patch => onUpdateRole(r.code, patch)}
            onRemove={() => onRemoveRole(r.code)} />
        ))
      )}

      {!grouped && (
        <div style={{ padding: '10px 14px', borderTop: roles.length ? '1px solid var(--border)' : 0 }}>
          <button className="btn sm ghost" onClick={onAddRole}>
            <I.Plus size={11} />Add role
          </button>
        </div>
      )}
    </div>
  );
}

// ── OrgBandHeader · organisation module header: colour + rename + add + delete
// `name` is the label in the course's default language, resolved by the parent —
// NOT `brand.label`, which is now a LocalizedString object. Binding an object to a
// text input renders "[object Object]" and, on commit, replaces the whole label
// with a string, silently destroying every other language
// (`feedback_localizedstring_bound_to_plain_widget`). The commit sends `{ name }`
// and the parent folds it into `label[lang]`.
function OrgBandHeader({ brand, name, count, onUpdate, onRemove, onAddRole, hasRoles }) {
  const [editing, setEditing] = React.useState(false);
  const [draft, setDraft] = React.useState(name);
  React.useEffect(() => { setDraft(name); }, [name]);
  const commit = () => { onUpdate({ name: draft.trim() || name }); setEditing(false); };
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10,
      padding: '10px 14px', borderBottom: hasRoles ? '1px solid var(--border)' : 0,
      background: 'var(--surface-inset)' }}>
      {/* Colour swatch — native colour input disguised as a chip.
          The tooltip states the LIMIT, because the control cannot: a colour picker
          on an organisation implies the organisation will be that colour for
          somebody, and this one never is. Learner theming resolves a CSS folder
          name and the runtime ships one folder, so the hex has nowhere to go
          (§3.9.4). It is still worth keeping — it is how the author tells the bands
          and the Content-mapping grid apart — which is exactly why the honest
          sentence is "only here", not "not yet". Deliberately contrasts with the
          LOGO on this same row, which does reach learners; labelling both the same
          way is the specific mistake Phase 4c was warned against. */}
      <label style={{ position: 'relative', width: 14, height: 14, borderRadius: 4,
        background: brand.color, border: '1px solid rgba(0,0,0,.12)',
        cursor: 'default', display: 'inline-block', flexShrink: 0 }}
        title={'Colour for “' + name + '” — used only here in the authoring tool, to tell '
          + 'the organisations apart. Learners never see it. The logo on the right is the '
          + 'part they do see.'}>
        <input type="color" value={brand.color}
          onChange={(e) => onUpdate({ color: e.target.value })}
          style={{ position: 'absolute', inset: 0, opacity: 0, cursor: 'default',
            width: '100%', height: '100%' }} />
      </label>
      {editing ? (
        <input autoFocus value={draft}
          onChange={(e) => setDraft(e.target.value)}
          onBlur={commit}
          onKeyDown={(e) => {
            if (e.key === 'Enter') commit();
            if (e.key === 'Escape') { setDraft(name); setEditing(false); }
          }}
          style={{ border: 0, outline: 0, background: 'transparent', font: 'inherit',
            fontSize: 13, fontWeight: 600, width: Math.max(80, draft.length * 8),
            color: 'var(--text)' }} />
      ) : (
        <button onClick={() => setEditing(true)} title="Rename organisation" style={{
          border: 0, background: 'transparent', font: 'inherit', padding: 0,
          fontSize: 13, fontWeight: 600, color: 'var(--text)', cursor: 'default' }}>
          {name}
        </button>
      )}
      <span className="pill" style={{ fontSize: 10 }}>{count}</span>
      <div style={{ flex: 1 }} />
      {/* ── Per-organisation LOGO (Phase 4d, 2026-08-12) ─────────────────────
          Omar: "there should be the ability to upload a logo for each
          Organisation. It shouldn't be mandatory but when I select an organisation
          that has its own logo, then this logo should be displayed … on the Home
          top left section."

          OPTIONAL, and its absence is a normal state — an organisation with no
          logo simply leaves the course's own logo in place, which is what every
          course does today.

          ONE image, filling BOTH of the runtime's live logo slots (`#eLHeaderLogo`
          in the header, `#homeInfoCompanyLogo` on Home). The shipped BT package
          already points one file at both for Openreach
          (`brand_BT/clientSpecific.css:197-199`), so that is the precedent rather
          than an invention. The old mock's four "logo variants" came from
          `<brand>`'s XML logo fields, which feed only commented-out code.

          `window.ImageUploadSlot` is the SHARED control (surface-draft.jsx),
          generalised for this rather than hand-copied — it does the real presigned
          upload and, unlike `CompanionSlot`, says so when one fails. */}
      {window.ImageUploadSlot && (
        <window.ImageUploadSlot value={brand.logo || null}
          onChange={(ref) => onUpdate({ logo: ref })}
          label="Logo" hint="any shape" title={`Logo for ${name} — shown to learners who pick it`}
          width={72} minHeight={34} fit="contain"
          /* The Player draws this in boxes of 150×75 (desktop) and up to ~164×82
             (mobile), so 600px covers a 3× retina screen with room to spare while
             keeping the SCORM package small. Downscale only — a smaller file is
             uploaded untouched. */
          maxEdge={600} />
      )}
      <button className="btn sm ghost" onClick={onAddRole}>
        <I.Plus size={11} />Add role
      </button>
      <button className="btn sm ghost danger" onClick={onRemove}
        title="Delete organisation. Its roles move to “No organisation”."
        style={{ width: 28, padding: 0, justifyContent: 'center' }}>
        <I.Trash size={12} />
      </button>
    </div>
  );
}

// ── RoleRow · editable name + organisation assignment + delete ───────────────
function RoleRow({ role, name, nameOf, grouped, brands, autoFocus, onUpdate, onRemove }) {
  const inputRef = React.useRef(null);
  React.useEffect(() => { if (autoFocus && inputRef.current) inputRef.current.select(); }, [autoFocus]);
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 14px',
      borderBottom: '1px solid var(--border-faint)' }}>
      <I.User size={14} style={{ color: 'var(--text-faint)', flexShrink: 0 }} />
      <input ref={inputRef} className="field" value={name}
        onChange={e => onUpdate({ name: e.target.value })}
        placeholder="e.g. Manager"
        style={{ flex: 1, minWidth: 0, maxWidth: 340, fontWeight: 500 }} />
      <div style={{ flex: 1 }} />
      {grouped && (
        <label style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          <I.Tag size={12} style={{ color: 'var(--text-faint)' }} />
          <select className="field select-elegant" value={role.brand || ''}
            onChange={e => onUpdate({ brand: e.target.value || null })}
            style={{ paddingRight: 28, minWidth: 168 }}>
            <option value="">No organisation</option>
            {/* `nameOf` — b.label is a LocalizedString now, and rendering the
                object here would put "[object Object]" in the dropdown. */}
            {brands.map(b => (
              <option key={b.code} value={b.code}>{nameOf(b) || b.code}</option>
            ))}
          </select>
        </label>
      )}
      <button className="btn sm ghost danger" onClick={onRemove}
        title="Delete role" style={{ width: 28, padding: 0, justifyContent: 'center' }}>
        <I.Trash size={12} />
      </button>
    </div>
  );
}

// ── EmptyRolesState · blank page · both actions, no funnel ───────────────────
function EmptyRolesState({ onAddRole, onAddOrganisation }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center',
      justifyContent: 'center', gap: 14, padding: '60px 24px', textAlign: 'center',
      color: 'var(--text-muted)', background: 'var(--surface)',
      border: '1px solid var(--border)', borderRadius: 'var(--radius-md)' }}>
      <I.Users size={32} style={{ color: 'var(--text-faint)' }} />
      <div>
        <h3 style={{ margin: 0, fontSize: 14, fontWeight: 600, color: 'var(--text)' }}>
          No roles or organisations yet
        </h3>
        <p style={{ margin: '6px auto 0', fontSize: 12.5, maxWidth: 420, lineHeight: 1.5 }}>
          Add a role to define an audience for this course. Add an organisation to
          group roles — roles can stay unassigned until you decide.
        </p>
      </div>
      <div style={{ display: 'flex', gap: 8 }}>
        <button className="btn sm" onClick={onAddOrganisation}>
          <I.Plus size={12} />Add organisation
        </button>
        <button className="btn sm primary" onClick={onAddRole}>
          <I.Plus size={12} />Add the first role
        </button>
      </div>
    </div>
  );
}

Object.assign(window, { SurfaceOrgRoles });
