diff --git a/src/backoffice/locale/de/LC_MESSAGES/django.po b/src/backoffice/locale/de/LC_MESSAGES/django.po
index 08f86f2090567158ea086cd8f8469406a5e2a1ca..76924f02abed1c5b42ee97f66ef4c075e275bb04 100644
--- a/src/backoffice/locale/de/LC_MESSAGES/django.po
+++ b/src/backoffice/locale/de/LC_MESSAGES/django.po
@@ -646,9 +646,6 @@ msgstr "verstecken"
 msgid "Assembly__edit__state_btn_rejected2registered"
 msgstr "doch in Erwägung ziehen"
 
-msgid "Assembly__edit__state_btn_placed"
-msgstr "platziert"
-
 msgid "Assembly__edit__state_btn_arrived"
 msgstr "angekommen (eigene Aussage)"
 
@@ -664,6 +661,21 @@ msgstr "Assembly bearbeiten"
 msgid "AssemblyTeam__message"
 msgstr "Nachricht senden"
 
+msgid "AssemblyTeam__position-final"
+msgstr "final"
+
+msgid "AssemblyTeam__position-preview"
+msgstr "Vorschau"
+
+msgid "AssemblyTeam__position-draft"
+msgstr "Entwurf"
+
+msgid "AssemblyTeam__position-internal"
+msgstr "nur Assembly"
+
+msgid "AssemblyTeam__position-none"
+msgstr "nicht verf."
+
 msgid "AssemblyTeam__lastnote"
 msgstr "letzte Notiz"
 
@@ -695,20 +707,43 @@ msgstr ""
 msgid "Assembly__edit__position_inform_assembly"
 msgstr "Verwaltende der Assemblies informieren"
 
-msgid "Assembly__edit__position_update"
-msgstr "Position aktualisieren"
+msgid "AssemblyTeam__edit_position-save"
+msgstr "Position speichern im Status:"
 
-msgid "Assembly__edit__position_recall"
-msgstr "Veröffentlichung zurücknehmen"
+msgid "AssemblyTeam__edit_position__tooltip-none"
+msgstr "Als 'keine Position' behandeln, auch wenn ggf. bereits eine gespeichert ist - z.B. ein alter Standort."
 
-msgid "Assembly__edit__position_publish"
-msgstr "mit Position veröffentlichen"
+# use translation from core
+msgid "Assembly__location_state-none"
+msgstr ""
+
+msgid "AssemblyTeam__edit_position__tooltip-draft"
+msgstr "Position ist eine Idee / nur grober Entwurf (nur für Assembly-Orga sichtbar)."
 
-msgid "Assembly__edit__position_save_unpublished"
-msgstr "Position vormerken"
+# use translation from core
+msgid "Assembly__location_state-draft"
+msgstr ""
 
-msgid "Assembly__edit__position_save_unaccepted"
-msgstr "Position für nicht akzeptierte Assembly speichern"
+msgid "AssemblyTeam__edit_position__tooltip-rough"
+msgstr "Werte als grobe Position speichern (nur im Maschinenraum für die Assembly und ggf. Habitat sichtbar)."
+
+# use translation from core
+msgid "Assembly__location_state-rough"
+msgstr ""
+
+msgid "AssemblyTeam__edit_position__tooltip-preview"
+msgstr "Werte als grobe Position speichern (wird mit diesem Hinweis auch bereits veröffentlicht)."
+
+# use translation from core
+msgid "Assembly__location_state-preview"
+msgstr ""
+
+msgid "AssemblyTeam__edit_position__tooltip-final"
+msgstr "Finale bzw. öffentliche Position speichern."
+
+# use translation from core
+msgid "Assembly__location_state-final"
+msgstr ""
 
 msgid "Assembly__edit__state"
 msgstr "Anmelde-Status der Assembly ändern"
@@ -1916,14 +1951,8 @@ msgstr "Es gab einen Fehler beim Parsen der Position, bitte erneut probieren."
 msgid "Assembly__edit__position_missing_on_publish"
 msgstr "Veröffentlichung abgebrochen: keine Positionsdaten gefunden"
 
-msgid "Assembly__edit__position_was_not_published"
-msgstr "Die Assembly war gar nicht veröffentlicht?!"
-
-msgid "Assembly__edit__position_unknown_action"
-msgstr "Unbekannte Aktion, Position wurde dennoch gespeichert."
-
-msgid "Assembly__edit__changed_position"
-msgstr "Position der Assembly wurde aktualisiert"
+msgid "AssemblyTeam__edit_position__success"
+msgstr "Die Position wurde erfolgreich aktualisert."
 
 #, python-format
 msgid "Badge__create__successful %(badge)s"
diff --git a/src/backoffice/locale/en/LC_MESSAGES/django.po b/src/backoffice/locale/en/LC_MESSAGES/django.po
index a07f265e18d6390bfa569a347137506f377137cb..7ec79a616cce53b7044d4a0bf5a7e89d7eed79b0 100644
--- a/src/backoffice/locale/en/LC_MESSAGES/django.po
+++ b/src/backoffice/locale/en/LC_MESSAGES/django.po
@@ -646,9 +646,6 @@ msgstr "hide"
 msgid "Assembly__edit__state_btn_rejected2registered"
 msgstr "consider it"
 
-msgid "Assembly__edit__state_btn_placed"
-msgstr "placed"
-
 msgid "Assembly__edit__state_btn_arrived"
 msgstr "arrived (self)"
 
@@ -664,6 +661,21 @@ msgstr "edit assembly"
 msgid "AssemblyTeam__message"
 msgstr "send a message"
 
+msgid "AssemblyTeam__position-final"
+msgstr "final"
+
+msgid "AssemblyTeam__position-preview"
+msgstr "preview"
+
+msgid "AssemblyTeam__position-draft"
+msgstr "draft"
+
+msgid "AssemblyTeam__position-internal"
+msgstr "assembly"
+
+msgid "AssemblyTeam__position-none"
+msgstr "none"
+
 msgid "AssemblyTeam__lastnote"
 msgstr "latest note"
 
@@ -695,20 +707,43 @@ msgstr ""
 msgid "Assembly__edit__position_inform_assembly"
 msgstr "inform the assembly's management"
 
-msgid "Assembly__edit__position_update"
-msgstr "update position"
+msgid "AssemblyTeam__edit_position-save"
+msgstr "save the position with this state:"
 
-msgid "Assembly__edit__position_recall"
-msgstr "recall the assembly"
+msgid "AssemblyTeam__edit_position__tooltip-none"
+msgstr "handle this assembly as 'no position', even if a location may be set (e.g. old one)"
 
-msgid "Assembly__edit__position_publish"
-msgstr "publish the assembly"
+# use translation from core
+msgid "Assembly__location_state-none"
+msgstr ""
+
+msgid "AssemblyTeam__edit_position__tooltip-draft"
+msgstr "draft position, assembly team only"
 
-msgid "Assembly__edit__position_save_unpublished"
-msgstr "save location (w/o publish)"
+# use translation from core
+msgid "Assembly__location_state-draft"
+msgstr ""
 
-msgid "Assembly__edit__position_save_unaccepted"
-msgstr "save location for yet-unaccepted assembly"
+msgid "AssemblyTeam__edit_position__tooltip-rough"
+msgstr "rough position, will be visible here in the backoffice only (i.e. with assemblies/habitats, too, but not external services like e.g. c3nav)"
+
+# use translation from core
+msgid "Assembly__location_state-rough"
+msgstr ""
+
+msgid "AssemblyTeam__edit_position__tooltip-preview"
+msgstr "rough position, will be shared with external services like e.g. c3nav"
+
+# use translation from core
+msgid "Assembly__location_state-preview"
+msgstr "change assembly's registration state"
+
+msgid "AssemblyTeam__edit_position__tooltip-final"
+msgstr "final position, will be shared publicly"
+
+# use translation from core
+msgid "Assembly__location_state-final"
+msgstr ""
 
 msgid "Assembly__edit__state"
 msgstr "change assembly's registration state"
@@ -1921,14 +1956,8 @@ msgstr "The given location data could not be parsed."
 msgid "Assembly__edit__position_missing_on_publish"
 msgstr "Could not publish the assembly as there was no location data present."
 
-msgid "Assembly__edit__position_was_not_published"
-msgstr "Could not recall the assembly: it was not published?!"
-
-msgid "Assembly__edit__position_unknown_action"
-msgstr "Unknown action requested, location was updated nonetheless."
-
-msgid "Assembly__edit__changed_position"
-msgstr "Location of the assembly was updated."
+msgid "AssemblyTeam__edit_position__success"
+msgstr "The assembly's position has been updated successfully."
 
 #, python-format
 msgid "Badge__create__successful %(badge)s"
diff --git a/src/backoffice/templates/backoffice/assemblyteam_assembly_detail.html b/src/backoffice/templates/backoffice/assemblyteam_assembly_detail.html
index 96eec278f4c0d9161ec0ff381d6b216136dff0e6..03bef91ee15e0cf3c907f8530d26594fe1585633 100644
--- a/src/backoffice/templates/backoffice/assemblyteam_assembly_detail.html
+++ b/src/backoffice/templates/backoffice/assemblyteam_assembly_detail.html
@@ -147,15 +147,6 @@
 
                   {% else %}
                     {% if assembly.is_public %}
-                      {% if assembly.state != 'placed' %}
-                        <li>
-                          <a class="dropdown-item text-secondary"
-                             role="button"
-                             href="{% url 'backoffice:assemblyteam-editstate' pk=assembly.id %}?state=placed">
-                            {% trans "Assembly__edit__state_btn_placed" %}
-                          </a>
-                        </li>
-                      {% endif %}
                       {% if assembly.state != 'arrived' %}
                         <li>
                           <a class="dropdown-item text-info"
@@ -224,7 +215,7 @@
           <div class="card-body d-flex flex-row">
             <div>
               <label for="pos_public">
-                <i class="bi bi-eye{% if not object.is_placed %}-slash{% endif %}"></i> {% trans "public" %}:
+                <i class="bi bi-eye{% if not object.is_placed %}-slash{% endif %}"></i> Status:
               </label>
               <br>
               <label for="pos_point">
@@ -236,10 +227,26 @@
               </label>
             </div>
             <div class="flex-grow-1 ps-3">
-              {% if object.is_placed %}
-                <span id="pos_public" class="text-success"><i class="bi bi-check-square"></i> {% trans "yes" %}</span>
+              {% if object.location_state == object.LocationState.FINAL %}
+                <span id="pos_public"
+                      class="text-success"
+                      title="{{ object.get_location_state_display }}"><i class="bi bi-check-square"></i> {% trans "AssemblyTeam__position-final" %}</span>
+              {% elif object.location_state == object.LocationState.PREVIEW %}
+                <span id="pos_public"
+                      class="text-success"
+                      title="{{ object.get_location_state_display }}"><i class="bi bi-check-square"></i> {% trans "AssemblyTeam__position-preview" %}</span>
+              {% elif object.location_state == object.LocationState.DRAFT %}
+                <span id="pos_public"
+                      class="text-warning"
+                      title="{{ object.get_location_state_display }}"><i class="bi bi-x-square"></i> {% trans "AssemblyTeam__position-draft" %}</span>
+              {% elif object.location_state == object.LocationState.ROUGH %}
+                <span id="pos_public"
+                      class="text-warning"
+                      title="{{ object.get_location_state_display }}"><i class="bi bi-x-square"></i> {% trans "AssemblyTeam__position-internal" %}</span>
               {% else %}
-                <span id="pos_public" class="text-warning"><i class="bi bi-x-square"></i> {% trans "no" %}</span>
+                <span id="pos_public"
+                      class="text-danger"
+                      title="{{ object.get_location_state_display }}"><i class="bi bi-x-square"></i> {% trans "AssemblyTeam__position-none" %}</span>
               {% endif %}
               <br>
               {% if object.location_data.point %}
diff --git a/src/backoffice/templates/backoffice/assemblyteam_editposition.html b/src/backoffice/templates/backoffice/assemblyteam_editposition.html
index 83a33fe28583df5aadb402e47c005630d99b474b..c12dde635e12dc0808157e425b0f82410d96a6da 100644
--- a/src/backoffice/templates/backoffice/assemblyteam_editposition.html
+++ b/src/backoffice/templates/backoffice/assemblyteam_editposition.html
@@ -79,35 +79,44 @@
                       placeholder="(optional)"
                       autocomplete="off"></textarea>
 
-            {% if assembly.is_placed %}
+            <label class="col-sm-2 col-form-label text-muted" for="buttons">{% trans "AssemblyTeam__edit_position-save" %}</label>
+            <div class="btn-group" id="buttons">
               <button type="submit"
-                      class="btn btn-sm btn-primary"
-                      name="action"
-                      value="save">{% trans "Assembly__edit__position_update" %}</button>
-
+                      class="btn btn-sm {% if assembly.location_state == 'none' %}btn-primary{% else %}btn-outline-danger{% endif %}"
+                      name="location_state"
+                      value="none"
+                      title="{% trans "AssemblyTeam__edit_position__tooltip-none" %}">
+                {% trans "Assembly__location_state-none" %}
+              </button>
               <button type="submit"
-                      class="btn btn-sm btn-danger"
-                      name="action"
-                      value="recall">{% trans "Assembly__edit__position_recall" %}</button>
-
-            {% elif assembly.is_public %}
+                      class="btn btn-sm {% if assembly.location_state == 'draft' %}btn-primary{% elif assembly.location_state == 'none' %}btn-outline-success{% elif assembly.location_state == 'rough' %}btn-outline-warning{% else %}btn-outline-danger{% endif %}"
+                      name="location_state"
+                      value="draft"
+                      title="{% trans "AssemblyTeam__edit_position__tooltip-draft" %}">
+                {% trans "Assembly__location_state-draft" %}
+              </button>
               <button type="submit"
-                      class="btn btn-sm btn-primary"
-                      name="action"
-                      value="publish">{% trans "Assembly__edit__position_publish" %}</button>
-
+                      class="btn btn-sm {% if assembly.location_state == 'rough' %}btn-primary{% elif assembly.location_state == 'draft' %}btn-outline-success{% elif assembly.location_state == 'none' %}btn-outline-danger{% elif assembly.location_state == 'final' %}btn-outline-warning{% else %}btn-outline-primary{% endif %}"
+                      name="location_state"
+                      value="rough"
+                      title="{% trans "AssemblyTeam__edit_position__tooltip-rough" %}">
+                {% trans "Assembly__location_state-rough" %}
+              </button>
               <button type="submit"
-                      class="btn btn-sm btn-secondary"
-                      name="action"
-                      value="save">{% trans "Assembly__edit__position_save_unpublished" %}</button>
-
-            {% else %}
+                      class="btn btn-sm {% if assembly.location_state == 'preview' %}btn-primary{% elif assembly.location_state == 'draft' %}btn-outline-warning{% elif assembly.location_state == 'none' %}btn-outline-danger{% elif assembly.location_state == 'final' %}btn-outline-warning{% else %}btn-outline-primary{% endif %}"
+                      name="location_state"
+                      value="preview"
+                      title="{% trans "AssemblyTeam__edit_position__tooltip-preview" %}">
+                {% trans "Assembly__location_state-preview" %}
+              </button>
               <button type="submit"
-                      class="btn btn-sm btn-danger"
-                      name="action"
-                      value="save">{% trans "Assembly__edit__position_save_unaccepted" %}</button>
-
-            {% endif %}
+                      class="btn btn-sm {% if assembly.location_state == 'final' %}btn-primary{% elif assembly.location_state == 'none' %}btn-outline-danger{% elif assembly.location_state == 'draft' %}btn-outline-danger{% else %}btn-outline-primary{% endif %}"
+                      name="location_state"
+                      value="final"
+                      title="{% trans "AssemblyTeam__edit_position__tooltip-final" %}">
+                {% trans "Assembly__location_state-final" %}
+              </button>
+            </div>
           </form>
         </div>
       </div>
diff --git a/src/backoffice/views/assemblyteam.py b/src/backoffice/views/assemblyteam.py
index 40cedbb0c1e3dcd056f2440a6b533ef096204c1c..42b35b619d1917f846ade973a2ed31a343a8a043 100644
--- a/src/backoffice/views/assemblyteam.py
+++ b/src/backoffice/views/assemblyteam.py
@@ -566,7 +566,7 @@ class AssemblyEditPlacementView(SingleAssemblyTeamMixin, View):
         request = self.request
         assembly = self.assembly
 
-        action = request.POST.get('action', 'save')
+        location_state = request.POST.get('location_state', None)
         inform_assembly = request.POST.get('inform_assembly', None) is not None
         poi = request.POST.get('location_point', None)
         boundaries = request.POST.get('location_boundaries', None)
@@ -574,9 +574,9 @@ class AssemblyEditPlacementView(SingleAssemblyTeamMixin, View):
         comment = request.POST.get('comment', '').strip()
 
         old_values = {
-            'state': assembly.state,
-            'location_point': assembly.get_location_point_xy() or '',
-            'location_boundaries': assembly.get_location_boundaries_xy() or '',
+            'location_state': assembly.location_state,
+            'location_point': str(assembly.get_location_point_xy() or ''),
+            'location_boundaries': str(assembly.get_location_boundaries_xy() or ''),
             'location_floor': str(assembly.get_location_floor_index() or ''),
         }
         changes = {}
@@ -621,34 +621,24 @@ class AssemblyEditPlacementView(SingleAssemblyTeamMixin, View):
             messages.error(request, gettext('Assembly__edit__position_error'))
             return redirect('backoffice:assemblyteam-editposition', pk=assembly.pk)
 
-        if action == 'publish':
-            # if assembly.location_point is not None or assembly.location_boundaries is not None:
-            if assembly.location_data.get('point') or assembly.location_data.get('boundaries'):
-                assembly.state = Assembly.State.PLACED
-                changes['state'] = assembly.state
-            else:
+        if location_state and location_state != assembly.location_state:
+            if location_state != Assembly.LocationState.NONE and not (assembly.location_data.get('point') or assembly.location_data.get('boundaries')):
                 messages.warning(request, gettext('Assembly__edit__position_missing_on_publish'))
                 return redirect('backoffice:assemblyteam-editposition', pk=assembly.pk)
 
-        elif action == 'recall':
-            if assembly.is_placed:
-                assembly.state = Assembly.State.ACCEPTED
-                changes['state'] = assembly.state
-            else:
-                messages.warning(request, gettext('Assembly__edit__position_was_not_published'))
-
-        elif action != 'save':
-            messages.warning(request, gettext('Assembly__edit__position_unknown_action'))
+            assembly.location_state = location_state
+            changes['location_state'] = location_state
 
-        assembly.save(update_fields=['state', 'location_point', 'location_boundaries', 'location_floor', 'location_data'])
+        assembly.save(update_fields=['location_state', 'location_point', 'location_boundaries', 'location_floor', 'location_data'])
 
         # log the action
-        messages.success(request, gettext('Assembly__edit__changed_position'))
+        messages.success(request, gettext('AssemblyTeam__edit_position__success'))
         logger.info(
-            'Assembly "%(assembly_slug)s" (%(assembly_pk)s): set POI to "%(poi)s" & boundaries to "%(boundaries)s" & floor to "%(floor)s" upon request by <%(user)s>.',  # noqa:E501
+            'Assembly "%(assembly_slug)s" (%(assembly_pk)s): set location_state to "%(location_state)s, POI to "%(poi)s" & boundaries to "%(boundaries)s" & floor to "%(floor)s" upon request by <%(user)s>.',  # noqa:E501
             {
                 'assembly_slug': assembly.slug,
                 'assembly_pk': assembly.pk,
+                'location_state': location_state,
                 'floor': floor,
                 'poi': poi,
                 'boundaries': boundaries,
@@ -662,18 +652,6 @@ class AssemblyEditPlacementView(SingleAssemblyTeamMixin, View):
             **{k: ActivityLogChange(old=old_values[k], new=v) for k, v in changes.items() if old_values[k] != v},
         )
 
-        if assembly.state != old_values['state']:
-            messages.success(request, gettext('Assembly__edit__changed_state'))
-            logger.info(
-                'Assembly "%(assembly_slug)s" (%(assembly_pk)s): changed state to "%(state)s" upon request by <%(user)s>.',
-                {
-                    'assembly_slug': assembly.slug,
-                    'assembly_pk': assembly.pk,
-                    'state': assembly.state,
-                    'user': self.request.user.username,
-                },
-            )
-
         # invalidate export caches where necessary
         ConferenceExportCache.signal_assembly_modification(conference=self.conference, assembly=assembly)
 
diff --git a/src/core/models/assemblies.py b/src/core/models/assemblies.py
index 1a342cb69ba17ae1d3b0474523b1f81015047a1a..8990771ec281287afde06b9e82804aa0fa103c52 100644
--- a/src/core/models/assemblies.py
+++ b/src/core/models/assemblies.py
@@ -98,7 +98,7 @@ class Assembly(TaggedItemMixin, ActivityLogMixin, RulesModel):
         HIDDEN = 'hidden', _('Assembly__state-hidden')
         ACCEPTED = 'accepted', _('Assembly__state-accepted')
         REJECTED = 'rejected', _('Assembly__state-rejected')
-        PLACED = 'placed', _('Assembly__state-placed')
+        PLACED = 'placed', _('Assembly__state-placed')  # TODO: entfernen, obsolet mit location_state
         ARRIVED_SELF = 'arrived', _('Assembly__state-arrived_self')
         ARRIVED_CONFIRMED = 'confirmed', _('Assembly__state-arrived_confirmed')
 
@@ -285,11 +285,7 @@ class Assembly(TaggedItemMixin, ActivityLogMixin, RulesModel):
         State.ARRIVED_SELF.value,
     ]
 
-    PLACED_STATES = [
-        State.PLACED.value,
-        State.ARRIVED_CONFIRMED.value,
-        State.ARRIVED_SELF.value,
-    ]
+    PLACED_LOCATION_STATES = [LocationState.PREVIEW, LocationState.FINAL]
 
     def __str__(self):
         return self.name
@@ -380,7 +376,7 @@ class Assembly(TaggedItemMixin, ActivityLogMixin, RulesModel):
 
     @cached_property
     def is_placed(self):
-        return self.state in self.PLACED_STATES
+        return self.location_state in self.PLACED_LOCATION_STATES
 
     @cached_property
     def is_cluster(self):