diff --git a/mo/web/org_round.py b/mo/web/org_round.py
index 979093b19910c10f96cda26db941ac362ffd935c..4ac98010b07a3e1d8e43c550a4c416fd05c3f410 100644
--- a/mo/web/org_round.py
+++ b/mo/web/org_round.py
@@ -1,6 +1,9 @@
 from flask import render_template, g, redirect, url_for, flash, request
 import locale
 from flask_wtf.form import FlaskForm
+import bleach
+from bleach.sanitizer import ALLOWED_TAGS
+import markdown
 from sqlalchemy import func
 from sqlalchemy.orm import joinedload
 from sqlalchemy.sql.functions import coalesce
@@ -428,6 +431,7 @@ class RoundEditForm(FlaskForm):
         "Hranice bodů pro úspěšné řešitele", validators=[validators.Optional()],
         description="Řešitelé s alespoň tolika body budou označeni za úspěšné řešitele, prázdná hodnota = žádné neoznačovat",
     )
+    has_messages = wtforms.BooleanField("Zprávičky pro účastníky (aktivuje možnost vytvářet novinky zobrazované účastníkům)")
     submit = wtforms.SubmitField('Uložit')
 
     def validate_state(self, field):
@@ -489,3 +493,78 @@ def org_task_statement(id: int):
         raise werkzeug.exceptions.Forbidden()
 
     return mo.web.util.send_task_statement(round)
+
+
+class MessageAddForm(FlaskForm):
+    title = wtforms.StringField('Nadpis', validators=[validators.Required()])
+    markdown = wtforms.TextAreaField(
+        'Text novinky', description='Zprávičky lze formátovat pomocí Markdownu',
+        validators=[validators.Required()],
+        render_kw={'rows': 10},
+    )
+    submit = wtforms.SubmitField(label='Vložit zprávičku')
+    preview = wtforms.SubmitField(label='Zobrazit náhled')
+
+
+class MessageRemoveForm(FlaskForm):
+    message_id = wtforms.IntegerField(validators=[validators.Required()])
+    message_remove = wtforms.SubmitField()
+
+
+@app.route('/org/contest/r/<int:id>/messages/', methods=('GET', 'POST'))
+def org_round_messages(id: int):
+    sess = db.get_session()
+    round, _, rr = get_round_rr(id, None, True)
+
+    if not round.has_messages:
+        flash('Toto kolo nemá aktivní zprávičky pro účastníky, aktivujte je v nastavení kola', 'warning')
+        return redirect(url_for('org_round', id=id))
+
+    messages = sess.query(db.Message).filter_by(round_id=id).order_by(db.Message.created_at).all()
+
+    add_form: Optional[MessageAddForm] = None
+    remove_form: Optional[MessageRemoveForm] = None
+    preview: Optional[db.Message] = None
+    if rr.have_right(Right.manage_round):
+        add_form = MessageAddForm()
+        remove_form = MessageRemoveForm()
+
+        if remove_form.validate_on_submit() and remove_form.message_remove.data:
+            msg = sess.query(db.Message).get(remove_form.message_id.data)
+            if not msg or msg.round_id != id:
+                raise werkzeug.exceptions.NotFound()
+            sess.delete(msg)
+            sess.commit()
+            app.logger.info(f"Zprávička pro kolo {id} odstraněna: {db.row2dict(msg)}")
+
+            flash('Zprávička odstraněna', 'success')
+            return redirect(url_for('org_round_messages', id=id))
+
+        if add_form.validate_on_submit():
+            msg = db.Message(
+                round_id=id,
+                created_by=g.user.user_id,
+                created_at=mo.now,
+            )
+            add_form.populate_obj(msg)
+            msg.html = bleach.clean(
+                markdown.markdown(msg.markdown),
+                tags=ALLOWED_TAGS+['p']
+            )
+
+            if add_form.preview.data:
+                preview = msg
+            elif add_form.submit.data:
+                sess.add(msg)
+                sess.commit()
+                app.logger.info(f"Vložena nová zprávička pro kolo {id}: {db.row2dict(msg)}")
+
+                flash('Zprávička úspěšně vložena', 'success')
+                return redirect(url_for('org_round_messages', id=id))
+
+    return render_template(
+        'org_round_messages.html',
+        round=round, rr=rr, messages=messages,
+        add_form=add_form, remove_form=remove_form,
+        preview=preview,
+    )
diff --git a/mo/web/templates/org_round.html b/mo/web/templates/org_round.html
index 6f4a2a560f656e64f0036bb4ca0582659fe636ab..6c5705dc35258985d0b6b711ac38ae08ee7e4e2e 100644
--- a/mo/web/templates/org_round.html
+++ b/mo/web/templates/org_round.html
@@ -58,6 +58,9 @@
 	{% if can_manage_round %}
 	<a class="btn btn-default" href='{{ url_for('org_round_edit', id=round.round_id) }}'>Editovat nastavení kola</a>
 	{% endif %}
+	{% if round.has_messages %}
+	<a class="btn btn-default" href='{{ url_for('org_round_messages', id=round.round_id) }}'>Zprávičky</a>
+	{% endif %}
 	{% if g.user.is_admin %}
 	<a class="btn btn-default" href='{{ log_url('round', round.round_id) }}'>Historie</a>
 	{% endif %}
diff --git a/mo/web/templates/org_round_messages.html b/mo/web/templates/org_round_messages.html
new file mode 100644
index 0000000000000000000000000000000000000000..1be2a7367b9db1ef420ee6ad65f7a19d925270f4
--- /dev/null
+++ b/mo/web/templates/org_round_messages.html
@@ -0,0 +1,34 @@
+{% extends "base.html" %}
+{% import "bootstrap/wtf.html" as wtf %}
+
+{% block title %}{{ round.name }} {{ round.round_code() }} – zprávičky{% endblock %}
+{% block breadcrumbs %}
+{{ contest_breadcrumbs(round=round, action='Zprávičky') }}
+{% endblock %}
+
+{% block body %}
+
+{% if messages %}
+{% with message_remove_form=remove_form %}
+	{% include "parts/messages.html" %}
+{% endwith %}
+{% else %}<p><i>Žádné zprávičky k vypsání.</i></p>{% endif %}
+
+{% if add_form %}
+	<h3>Nová zprávička</h3>
+	<div class="form-frame">
+	{{ wtf.quick_form(add_form, button_map={'submit': 'primary', 'preview': 'default'}) }}
+	</div>
+	{% if preview %}
+	<div class="form-frame">
+		<b>Náhled zprávičky:</b>
+		{% with messages = [preview] %}
+		{% include "parts/messages.html" %}
+		{% endwith %}
+	</div>
+	{% endif %}
+{% else %}
+	<p><i>Nemáte právo k přidávání nových zpráviček.</i></p>
+{% endif %}
+
+{% endblock %}
diff --git a/mo/web/templates/parts/messages.html b/mo/web/templates/parts/messages.html
new file mode 100644
index 0000000000000000000000000000000000000000..d90436f4af4b3bcb363f896936d3ba7ab1249099
--- /dev/null
+++ b/mo/web/templates/parts/messages.html
@@ -0,0 +1,14 @@
+{% for msg in messages %}
+<div class="message">
+	<span class="msg-title">{{ msg.title }}</span>
+	<span class="msg-date">{{ msg.created_at|time_and_timedelta }}</span>
+	<div class="msg-text">{{ msg.html|safe }}</div>
+	{% if message_remove_form %}
+		<form method="POST" onsubmit="return confirm('Opravdu nenávratně smazat?');" style="float: right; margin-top: -20px;">
+			{{ message_remove_form.csrf_token }}
+			<input type="hidden" name="message_id" value="{{ msg.message_id }}">
+			<input type="submit" name="message_remove" class="btn btn-xs btn-danger" value="Smazat">
+		</form>
+	{% endif %}
+</div>
+{% endfor %}
diff --git a/static/mo.css b/static/mo.css
index 9826de39d25461d82bfbf4df2e5c60e7bfc90c22..dec13006ac04a3a4b4393624fa95db3c9aeca644 100644
--- a/static/mo.css
+++ b/static/mo.css
@@ -382,3 +382,24 @@ div.alert + div.alert {
 .collapsible input[type="checkbox"].toggle:checked ~ .collapsible-inner {
 	max-height: 100vh;
 }
+
+div.message {
+	padding: 5px 10px;
+	margin-bottom: 5px;
+	border-radius: 5px;
+	border: solid 1px #f8d99b;
+	color:#46381f;
+	background-color:#fff8d5;
+}
+div.message.new {
+	background-color: #ffdede;
+	border: solid 1px #ff5e5e;
+}
+div.message .msg-title {
+	font-weight: bold;
+}
+div.message .msg-date {
+	float: right;
+	font-style: italic;
+	color: #777;
+}