diff --git a/Telegram/Resources/icons/fragment.png b/Telegram/Resources/icons/fragment.png
new file mode 100644
index 000000000..d56b3eaac
Binary files /dev/null and b/Telegram/Resources/icons/fragment.png differ
diff --git a/Telegram/Resources/icons/fragment@2x.png b/Telegram/Resources/icons/fragment@2x.png
new file mode 100644
index 000000000..453b4b2b8
Binary files /dev/null and b/Telegram/Resources/icons/fragment@2x.png differ
diff --git a/Telegram/Resources/icons/fragment@3x.png b/Telegram/Resources/icons/fragment@3x.png
new file mode 100644
index 000000000..154c121c5
Binary files /dev/null and b/Telegram/Resources/icons/fragment@3x.png differ
diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 5f642ad10..3f1ed5c03 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -323,6 +323,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_intro_qr_step3" = "Scan this image to Log In";
 "lng_intro_qr_skip" = "Or log in using your phone number";
 
+"lng_intro_fragment_title" = "Enter code";
+"lng_intro_fragment_about" = "Get the code for {phone_number} in the Anonymous Numbers section on Fragment.";
+"lng_intro_fragment_button" = "Open Fragment";
+
 "lng_phone_title" = "Your Phone Number";
 "lng_phone_desc" = "Please confirm your country code and\nenter your mobile phone number.";
 "lng_phone_to_qr" = "Quick log in using QR code";
diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style
index 2710e2fe7..5f736325b 100644
--- a/Telegram/SourceFiles/boxes/boxes.style
+++ b/Telegram/SourceFiles/boxes/boxes.style
@@ -293,6 +293,13 @@ membersAbout: FlatLabel(defaultFlatLabel) {
 	style: boxLabelStyle;
 }
 
+fragmentBoxButton: RoundButton(introNextButton) {
+	width: 256px;
+	icon: icon {{ "fragment", activeButtonFg }};
+	iconOver: icon {{ "fragment", activeButtonFgOver }};
+	iconPosition: point(-10px, 9px);
+}
+
 passcodeHeaderFont: font(19px);
 passcodeHeaderHeight: 80px;
 passcodeInput: InputField(introPhone) {
diff --git a/Telegram/SourceFiles/intro/intro.style b/Telegram/SourceFiles/intro/intro.style
index c05508bc5..f6381e444 100644
--- a/Telegram/SourceFiles/intro/intro.style
+++ b/Telegram/SourceFiles/intro/intro.style
@@ -85,6 +85,7 @@ introCoverDuration: 200;
 introNextButton: RoundButton(defaultActiveButton) {
 	width: 300px;
 	height: 42px;
+	radius: 6px;
 	textTop: 11px;
 	font: font(boxFontSize semibold);
 }
diff --git a/Telegram/SourceFiles/intro/intro_code.cpp b/Telegram/SourceFiles/intro/intro_code.cpp
index 2932decb5..3d4eab1ac 100644
--- a/Telegram/SourceFiles/intro/intro_code.cpp
+++ b/Telegram/SourceFiles/intro/intro_code.cpp
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_keys.h"
 #include "intro/intro_signup.h"
 #include "intro/intro_password_check.h"
+#include "core/file_utilities.h"
 #include "core/update_checker.h"
 #include "ui/widgets/buttons.h"
 #include "ui/widgets/labels.h"
@@ -19,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "main/main_account.h"
 #include "mtproto/mtp_instance.h"
 #include "styles/style_intro.h"
+#include "styles/style_boxes.h"
 
 namespace Intro {
 namespace details {
@@ -99,7 +101,9 @@ CodeWidget::CodeWidget(
 
 	_code->setDigitsCountMax(getData()->codeLength);
 
-	setTitleText(rpl::single(Ui::FormatPhone(getData()->phone)));
+	setTitleText(getData()->codeByFragmentUrl.isEmpty()
+		? rpl::single(Ui::FormatPhone(getData()->phone))
+		: tr::lng_intro_fragment_title());
 	updateDescText();
 }
 
@@ -117,10 +121,19 @@ int CodeWidget::errorTop() const {
 
 void CodeWidget::updateDescText() {
 	const auto byTelegram = getData()->codeByTelegram;
+	const auto isFragment = !getData()->codeByFragmentUrl.isEmpty();
 	setDescriptionText(
-		(byTelegram ? tr::lng_code_from_telegram : tr::lng_code_desc)(
-			Ui::Text::RichLangValue));
-	if (getData()->codeByTelegram) {
+		isFragment
+			? tr::lng_intro_fragment_about(
+				lt_phone_number,
+				rpl::single(TextWithEntities{
+					.text = Ui::FormatPhone(getData()->phone)
+				}),
+				Ui::Text::RichLangValue)
+			: (byTelegram ? tr::lng_code_from_telegram : tr::lng_code_desc)(
+				Ui::Text::RichLangValue));
+	if (isFragment) {
+	} else if (getData()->codeByTelegram) {
 		_noTelegramCode->show();
 		_callTimer.cancel();
 	} else {
@@ -300,7 +313,7 @@ void CodeWidget::codeSubmitFail(const MTP::Error &error) {
 
 void CodeWidget::codeChanged() {
 	hideError();
-	submit();
+	submitCode();
 }
 
 void CodeWidget::sendCall() {
@@ -362,6 +375,14 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
 }
 
 void CodeWidget::submit() {
+	if (getData()->codeByFragmentUrl.isEmpty()) {
+		submitCode();
+	} else {
+		File::OpenUrl(getData()->codeByFragmentUrl);
+	}
+}
+
+void CodeWidget::submitCode() {
 	const auto text = QString(
 		_code->getLastText()
 	).remove(
@@ -393,6 +414,18 @@ void CodeWidget::submit() {
 	}).handleFloodErrors().send();
 }
 
+rpl::producer<QString> CodeWidget::nextButtonText() const {
+	return getData()->codeByFragmentUrl.isEmpty()
+		? Step::nextButtonText()
+		: tr::lng_intro_fragment_button();
+}
+
+const style::RoundButton *CodeWidget::nextButtonStyle() const {
+	return !getData()->codeByFragmentUrl.isEmpty()
+		? &st::fragmentBoxButton
+		: nullptr;
+}
+
 void CodeWidget::noTelegramCode() {
 	if (_noTelegramCodeRequestId) {
 		return;
diff --git a/Telegram/SourceFiles/intro/intro_code.h b/Telegram/SourceFiles/intro/intro_code.h
index d267c477d..95ae597a1 100644
--- a/Telegram/SourceFiles/intro/intro_code.h
+++ b/Telegram/SourceFiles/intro/intro_code.h
@@ -55,6 +55,8 @@ public:
 	void finished() override;
 	void cancelled() override;
 	void submit() override;
+	rpl::producer<QString> nextButtonText() const override;
+	const style::RoundButton *nextButtonStyle() const override;
 
 	void updateDescText();
 
@@ -83,6 +85,8 @@ private:
 	void noTelegramCodeDone(const MTPauth_SentCode &result);
 	void noTelegramCodeFail(const MTP::Error &result);
 
+	void submitCode();
+
 	void stopCheck();
 
 	object_ptr<Ui::LinkButton> _noTelegramCode;
diff --git a/Telegram/SourceFiles/intro/intro_step.cpp b/Telegram/SourceFiles/intro/intro_step.cpp
index 9162775d1..68ff6b40f 100644
--- a/Telegram/SourceFiles/intro/intro_step.cpp
+++ b/Telegram/SourceFiles/intro/intro_step.cpp
@@ -119,6 +119,10 @@ rpl::producer<QString> Step::nextButtonText() const {
 	return tr::lng_intro_next();
 }
 
+const style::RoundButton *Step::nextButtonStyle() const {
+	return nullptr;
+}
+
 void Step::goBack() {
 	if (_goCallback) {
 		_goCallback(nullptr, StackAction::Back, Animate::Back);
diff --git a/Telegram/SourceFiles/intro/intro_step.h b/Telegram/SourceFiles/intro/intro_step.h
index 2148e6e94..256396bf2 100644
--- a/Telegram/SourceFiles/intro/intro_step.h
+++ b/Telegram/SourceFiles/intro/intro_step.h
@@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/rp_widget.h"
 #include "ui/effects/animations.h"
 
+namespace style {
+struct RoundButton;
+} // namespace style;
+
 namespace Main {
 class Account;
 } // namespace Main;
@@ -77,6 +81,7 @@ public:
 
 	virtual void submit() = 0;
 	[[nodiscard]] virtual rpl::producer<QString> nextButtonText() const;
+	[[nodiscard]] virtual const style::RoundButton *nextButtonStyle() const;
 
 	[[nodiscard]] int contentLeft() const;
 	[[nodiscard]] int contentTop() const;
diff --git a/Telegram/SourceFiles/intro/intro_widget.cpp b/Telegram/SourceFiles/intro/intro_widget.cpp
index 6e1f427ad..e90cf82d0 100644
--- a/Telegram/SourceFiles/intro/intro_widget.cpp
+++ b/Telegram/SourceFiles/intro/intro_widget.cpp
@@ -74,6 +74,7 @@ Widget::Widget(
 : RpWidget(parent)
 , _account(account)
 , _data(details::Data{ .controller = controller })
+, _nextStyle(&st::introNextButton)
 , _back(this, object_ptr<Ui::IconButton>(this, st::introBackButton))
 , _settings(
 	this,
@@ -83,7 +84,7 @@ Widget::Widget(
 		st::defaultBoxButton))
 , _next(
 	this,
-	object_ptr<Ui::RoundButton>(this, nullptr, st::introNextButton))
+	object_ptr<Ui::RoundButton>(this, nullptr, *_nextStyle))
 , _connecting(std::make_unique<Window::ConnectionState>(
 		this,
 		account,
@@ -127,10 +128,6 @@ Widget::Widget(
 	_back->entity()->setClickedCallback([=] { backRequested(); });
 	_back->hide(anim::type::instant);
 
-	_next->entity()->setClickedCallback([=] { getStep()->submit(); });
-	_next->entity()->setTextTransform(
-		Ui::RoundButton::TextTransform::NoTransform);
-
 	if (_changeLanguage) {
 		_changeLanguage->finishAnimating();
 	}
@@ -344,13 +341,31 @@ void Widget::historyMove(StackAction action, Animate animate) {
 	if (_terms) {
 		hideAndDestroy(std::exchange(_terms, { nullptr }));
 	}
+	{
+		const auto st = getStep()->nextButtonStyle();
+		const auto nextStyle = st ? st : &st::introNextButton;
+		if (_nextStyle != nextStyle) {
+			_nextStyle = nextStyle;
+			_next = nullptr;
+			_next.create(
+				this,
+				object_ptr<Ui::RoundButton>(this, nullptr, *nextStyle));
+			showControls();
+			updateControlsGeometry();
+		}
+	}
 
 	getStep()->finishInit();
 	getStep()->prepareShowAnimated(wasStep);
 	if (wasStep->hasCover() != getStep()->hasCover()) {
 		_nextTopFrom = wasStep->contentTop() + st::introNextTop;
 		_controlsTopFrom = wasStep->hasCover() ? st::introCoverHeight : 0;
-		_coverShownAnimation.start([this] { updateControlsGeometry(); }, 0., 1., st::introCoverDuration, wasStep->hasCover() ? anim::linear : anim::easeOutCirc);
+		_coverShownAnimation.start(
+			[this] { updateControlsGeometry(); },
+			0.,
+			1.,
+			st::introCoverDuration,
+			wasStep->hasCover() ? anim::linear : anim::easeOutCirc);
 	}
 
 	_stepLifetime.destroy();
@@ -665,6 +680,10 @@ void Widget::showControls() {
 }
 
 void Widget::setupNextButton() {
+	_next->entity()->setClickedCallback([=] { getStep()->submit(); });
+	_next->entity()->setTextTransform(
+		Ui::RoundButton::TextTransform::NoTransform);
+
 	_next->entity()->setText(getStep()->nextButtonText(
 	) | rpl::filter([](const QString &text) {
 		return !text.isEmpty();
@@ -757,13 +776,18 @@ void Widget::resizeEvent(QResizeEvent *e) {
 }
 
 void Widget::updateControlsGeometry() {
-	auto shown = _coverShownAnimation.value(1.);
+	const auto skip = st::introSettingsSkip;
+	const auto shown = _coverShownAnimation.value(1.);
 
-	auto controlsTopTo = getStep()->hasCover() ? st::introCoverHeight : 0;
-	auto controlsTop = anim::interpolate(_controlsTopFrom, controlsTopTo, shown);
-	_settings->moveToRight(st::introSettingsSkip, controlsTop + st::introSettingsSkip);
+	const auto controlsTop = anim::interpolate(
+		_controlsTopFrom,
+		getStep()->hasCover() ? st::introCoverHeight : 0,
+		shown);
+	_settings->moveToRight(skip, controlsTop + skip);
 	if (_update) {
-		_update->moveToRight(st::introSettingsSkip + _settings->width() + st::introSettingsSkip, _settings->y());
+		_update->moveToRight(
+			skip + _settings->width() + skip,
+			_settings->y());
 	}
 	_back->moveToLeft(0, controlsTop);
 
@@ -779,13 +803,19 @@ void Widget::updateControlsGeometry() {
 		? QRect(0, 0, width(), realNextTop)
 		: QRect());
 	if (_changeLanguage) {
-		_changeLanguage->moveToLeft((width() - _changeLanguage->width()) / 2, _next->y() + _next->height() + _changeLanguage->height());
+		_changeLanguage->moveToLeft(
+			(width() - _changeLanguage->width()) / 2,
+			_next->y() + _next->height() + _changeLanguage->height());
 	}
 	if (_resetAccount) {
-		_resetAccount->moveToLeft((width() - _resetAccount->width()) / 2, height() - st::introResetBottom - _resetAccount->height());
+		_resetAccount->moveToLeft(
+			(width() - _resetAccount->width()) / 2,
+			height() - st::introResetBottom - _resetAccount->height());
 	}
 	if (_terms) {
-		_terms->moveToLeft((width() - _terms->width()) / 2, height() - st::introTermsBottom - _terms->height());
+		_terms->moveToLeft(
+			(width() - _terms->width()) / 2,
+			height() - st::introTermsBottom - _terms->height());
 	}
 }
 
diff --git a/Telegram/SourceFiles/intro/intro_widget.h b/Telegram/SourceFiles/intro/intro_widget.h
index e40d914c9..ef224b831 100644
--- a/Telegram/SourceFiles/intro/intro_widget.h
+++ b/Telegram/SourceFiles/intro/intro_widget.h
@@ -186,6 +186,8 @@ private:
 	int _nextTopFrom = 0;
 	int _controlsTopFrom = 0;
 
+	const style::RoundButton *_nextStyle = nullptr;
+
 	object_ptr<Ui::FadeWrap<Ui::IconButton>> _back;
 	object_ptr<Ui::FadeWrap<Ui::RoundButton>> _update = { nullptr };
 	object_ptr<Ui::FadeWrap<Ui::RoundButton>> _settings;