"""Widgets for showing notification messages in toasts."""

from __future__ import annotations

from typing import ClassVar

from rich.console import RenderableType
from rich.text import Text

from .. import on
from ..containers import Container
from ..css.query import NoMatches
from ..events import Click, Mount
from ..notifications import Notification, Notifications
from ._static import Static


class ToastHolder(Container, inherit_css=False):
    """Container that holds a single toast.

    Used to control the alignment of each of the toasts in the main toast
    container.
    """

    DEFAULT_CSS = """
    ToastHolder {
        align-horizontal: right;
        width: 1fr;
        height: auto;
        visibility: hidden;
    }
    """


class Toast(Static, inherit_css=False):
    """A widget for displaying short-lived notifications."""

    DEFAULT_CSS = """
    Toast {
        width: 60;
        max-width: 50%;
        height: auto;
        visibility: visible;
        margin-top: 1;
        padding: 1 1;
        background: $panel;
        tint: white 5%;
    }

    .toast--title {
        text-style: bold;
    }

    Toast {
        border-right: wide $background;
    }

    Toast.-information {
        border-left: wide $success;
    }

    Toast.-information .toast--title {
        color: $success-darken-1;
    }

    Toast.-warning {
        border-left: wide $warning;
    }

    Toast.-warning .toast--title {
        color: $warning-darken-1;
    }

    Toast.-error {
        border-left: wide $error;
    }

    Toast.-error .toast--title {
       color: $error-darken-1;
    }
    """

    COMPONENT_CLASSES: ClassVar[set[str]] = {"toast--title"}
    """
    | Class | Description |
    | :- | :- |
    | `toast--title` | Targets the title of the toast. |
    """

    def __init__(self, notification: Notification) -> None:
        """Initialise the toast.

        Args:
            notification: The notification to show in the toast.
        """
        super().__init__(classes=f"-{notification.severity}")
        self._notification = notification
        self._timeout = notification.time_left

    def render(self) -> RenderableType:
        """Render the toast's content.

        Returns:
            A Rich renderable for the title and content of the Toast.
        """
        notification = self._notification
        if notification.title:
            header_style = self.get_component_rich_style("toast--title")
            notification_text = Text.assemble(
                (notification.title, header_style),
                "\n",
                Text.from_markup(notification.message),
            )
        else:
            notification_text = Text.assemble(
                Text.from_markup(notification.message),
            )
        return notification_text

    def _on_mount(self, _: Mount) -> None:
        """Set the time running once the toast is mounted."""
        self.set_timer(self._timeout, self._expire)

    @on(Click)
    def _expire(self) -> None:
        """Remove the toast once the timer has expired."""
        # Before we removed ourself, we also call on the app to forget about
        # the notification that caused us to exist. Note that we tell the
        # app to not bother refreshing the display on our account, we're
        # about to handle that anyway.
        self.app._unnotify(self._notification, refresh=False)
        # Note that we attempt to remove our parent, because we're wrapped
        # inside an alignment container. The testing that we are is as much
        # to keep type checkers happy as anything else.
        (self.parent if isinstance(self.parent, ToastHolder) else self).remove()


class ToastRack(Container, inherit_css=False):
    """A container for holding toasts."""

    DEFAULT_CSS = """
    ToastRack {
        layer: _toastrack;
        width: 1fr;
        height: auto;
        dock: top;
        align: right bottom;
        visibility: hidden;
        layout: vertical;
        overflow-y: scroll;
        margin-bottom: 1;
        margin-right: 1;
    }
    """

    @staticmethod
    def _toast_id(notification: Notification) -> str:
        """Create a Textual-DOM-internal ID for the given notification.

        Args:
            notification: The notification to create the ID for.

        Returns:
            An ID for the notification that can be used within the DOM.
        """
        return f"--textual-toast-{notification.identity}"

    def show(self, notifications: Notifications) -> None:
        """Show the notifications as toasts.

        Args:
            notifications: The notifications to show.
        """

        # Look for any stale toasts and remove them.
        for toast in self.query(Toast):
            if toast._notification not in notifications:
                toast.remove()

        # Gather up all the notifications that we don't have toasts for yet.
        new_toasts: list[Notification] = []
        for notification in notifications:
            try:
                # See if there's already a toast for that notification.
                _ = self.get_child_by_id(self._toast_id(notification))
            except NoMatches:
                if not notification.has_expired:
                    new_toasts.append(notification)

        # If we got any...
        if new_toasts:
            # ...mount them.
            self.mount_all(
                ToastHolder(Toast(toast), id=self._toast_id(toast))
                for toast in new_toasts
            )
            self.call_later(self.scroll_end, animate=False, force=True)

src/textual/widgets/_toast.py
"""Widgets for showing notification messages in toasts."""

from __future__ import annotations

from typing import ClassVar

from rich.console import RenderableType
from rich.text import Text

from .. import on
from ..containers import Container
from ..css.query import NoMatches
from ..events import Click, Mount
from ..notifications import Notification, Notifications
from ._static import Static


class ToastHolder(Container, inherit_css=False):
    """Container that holds a single toast.

    Used to control the alignment of each of the toasts in the main toast
    container.
    """

    DEFAULT_CSS = """
    ToastHolder {
        align-horizontal: right;
        width: 1fr;
        height: auto;
        visibility: hidden;
    }
    """


class Toast(Static, inherit_css=False):
    """A widget for displaying short-lived notifications."""

    DEFAULT_CSS = """
    Toast {
        width: 60;
        max-width: 50%;
        height: auto;
        visibility: visible;
        margin-top: 1;
        padding: 1 1;
        background: $panel;
        tint: white 5%;
        link-background:;
        link-color: $text;
        link-style: underline;
        link-hover-background: $accent;
        link-hover-color: $text;
        link-hover-style: bold not underline;
    }

    .toast--title {
        text-style: bold;
    }

    Toast {
        border-right: wide $background;
    }

    Toast.-information {
        border-left: wide $success;
    }

    Toast.-information .toast--title {
        color: $success-darken-1;
    }

    Toast.-warning {
        border-left: wide $warning;
    }

    Toast.-warning .toast--title {
        color: $warning-darken-1;
    }

    Toast.-error {
        border-left: wide $error;
    }

    Toast.-error .toast--title {
       color: $error-darken-1;
    }
    """

    COMPONENT_CLASSES: ClassVar[set[str]] = {"toast--title"}
    """
    | Class | Description |
    | :- | :- |
    | `toast--title` | Targets the title of the toast. |
    """

    def __init__(self, notification: Notification) -> None:
        """Initialise the toast.

        Args:
            notification: The notification to show in the toast.
        """
        super().__init__(classes=f"-{notification.severity}")
        self._notification = notification
        self._timeout = notification.time_left

    def render(self) -> RenderableType:
        """Render the toast's content.

        Returns:
            A Rich renderable for the title and content of the Toast.
        """
        notification = self._notification
        if notification.title:
            header_style = self.get_component_rich_style("toast--title")
            notification_text = Text.assemble(
                (notification.title, header_style),
                "\n",
                Text.from_markup(notification.message),
            )
        else:
            notification_text = Text.assemble(
                Text.from_markup(notification.message),
            )
        return notification_text

    def _on_mount(self, _: Mount) -> None:
        """Set the time running once the toast is mounted."""
        self.set_timer(self._timeout, self._expire)

    @on(Click)
    def _expire(self) -> None:
        """Remove the toast once the timer has expired."""
        # Before we removed ourself, we also call on the app to forget about
        # the notification that caused us to exist. Note that we tell the
        # app to not bother refreshing the display on our account, we're
        # about to handle that anyway.
        self.app._unnotify(self._notification, refresh=False)
        # Note that we attempt to remove our parent, because we're wrapped
        # inside an alignment container. The testing that we are is as much
        # to keep type checkers happy as anything else.
        (self.parent if isinstance(self.parent, ToastHolder) else self).remove()


class ToastRack(Container, inherit_css=False):
    """A container for holding toasts."""

    DEFAULT_CSS = """
    ToastRack {
        layer: _toastrack;
        width: 1fr;
        height: auto;
        dock: top;
        align: right bottom;
        visibility: hidden;
        layout: vertical;
        overflow-y: scroll;
        margin-bottom: 1;
        margin-right: 1;
    }
    """

    @staticmethod
    def _toast_id(notification: Notification) -> str:
        """Create a Textual-DOM-internal ID for the given notification.

        Args:
            notification: The notification to create the ID for.

        Returns:
            An ID for the notification that can be used within the DOM.
        """
        return f"--textual-toast-{notification.identity}"

    def show(self, notifications: Notifications) -> None:
        """Show the notifications as toasts.

        Args:
            notifications: The notifications to show.
        """

        # Look for any stale toasts and remove them.
        for toast in self.query(Toast):
            if toast._notification not in notifications:
                toast.remove()

        # Gather up all the notifications that we don't have toasts for yet.
        new_toasts: list[Notification] = []
        for notification in notifications:
            try:
                # See if there's already a toast for that notification.
                _ = self.get_child_by_id(self._toast_id(notification))
            except NoMatches:
                if not notification.has_expired:
                    new_toasts.append(notification)

        # If we got any...
        if new_toasts:
            # ...mount them.
            self.mount_all(
                ToastHolder(Toast(toast), id=self._toast_id(toast))
                for toast in new_toasts
            )
            self.call_later(self.scroll_end, animate=False, force=True)

src/textual/widgets/_toast.py
diff --git a/src/textual/widgets/_toast.py b/src/textual/widgets/_toast.py
index dff62b5a5d..6b62304838 100644
--- a/src/textual/widgets/_toast.py
+++ b/src/textual/widgets/_toast.py
@@ -45,6 +45,12 @@ class Toast(Static, inherit_css=False):
         padding: 1 1;
         background: $panel;
         tint: white 5%;
+        link-background:;
+        link-color: $text;
+        link-style: underline;
+        link-hover-background: $accent;
+        link-hover-color: $text;
+        link-hover-style: bold not underline;
     }
 
     .toast--title {
diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
index 98c66c4729..c13e391f15 100644
--- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
+++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr
@@ -21537,6 +21537,324 @@
   
   '''
 # ---
+# name: test_notification_with_inline_link
+  '''
+  <svg class="rich-terminal" viewBox="0 0 994 635.5999999999999" xmlns="http://www.w3.org/2000/svg">
+      <!-- Generated with Rich https://www.textualize.io -->
+      <style>
+  
+      @font-face {
+          font-family: "Fira Code";
+          src: local("FiraCode-Regular"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+          font-style: normal;
+          font-weight: 400;
+      }
+      @font-face {
+          font-family: "Fira Code";
+          src: local("FiraCode-Bold"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+          font-style: bold;
+          font-weight: 700;
+      }
+  
+      .terminal-2986389628-matrix {
+          font-family: Fira Code, monospace;
+          font-size: 20px;
+          line-height: 24.4px;
+          font-variant-east-asian: full-width;
+      }
+  
+      .terminal-2986389628-title {
+          font-size: 18px;
+          font-weight: bold;
+          font-family: arial;
+      }
+  
+      .terminal-2986389628-r1 { fill: #e1e1e1 }
+  .terminal-2986389628-r2 { fill: #c5c8c6 }
+  .terminal-2986389628-r3 { fill: #56c278 }
+  .terminal-2986389628-r4 { fill: #2e3339 }
+  .terminal-2986389628-r5 { fill: #e3e4e4 }
+  .terminal-2986389628-r6 { fill: #e3e4e4;text-decoration: underline; }
+      </style>
+  
+      <defs>
+      <clipPath id="terminal-2986389628-clip-terminal">
+        <rect x="0" y="0" width="975.0" height="584.5999999999999" />
+      </clipPath>
+      <clipPath id="terminal-2986389628-line-0">
+      <rect x="0" y="1.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-1">
+      <rect x="0" y="25.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-2">
+      <rect x="0" y="50.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-3">
+      <rect x="0" y="74.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-4">
+      <rect x="0" y="99.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-5">
+      <rect x="0" y="123.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-6">
+      <rect x="0" y="147.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-7">
+      <rect x="0" y="172.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-8">
+      <rect x="0" y="196.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-9">
+      <rect x="0" y="221.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-10">
+      <rect x="0" y="245.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-11">
+      <rect x="0" y="269.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-12">
+      <rect x="0" y="294.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-13">
+      <rect x="0" y="318.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-14">
+      <rect x="0" y="343.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-15">
+      <rect x="0" y="367.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-16">
+      <rect x="0" y="391.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-17">
+      <rect x="0" y="416.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-18">
+      <rect x="0" y="440.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-19">
+      <rect x="0" y="465.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-20">
+      <rect x="0" y="489.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-21">
+      <rect x="0" y="513.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-2986389628-line-22">
+      <rect x="0" y="538.3" width="976" height="24.65"/>
+              </clipPath>
+      </defs>
+  
+      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-2986389628-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">NotifyWithInlineLinkApp</text>
+              <g transform="translate(26,22)">
+              <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+              <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+              <circle cx="44" cy="0" r="7" fill="#28c840"/>
+              </g>
+          
+      <g transform="translate(9, 41)" clip-path="url(#terminal-2986389628-clip-terminal)">
+      <rect fill="#1e1e1e" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="489.5" width="500.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="500.2" y="489.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="512.4" y="489.5" width="427" height="24.65" shape-rendering="crispEdges"/><rect fill="#1d1d1d" x="939.4" y="489.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="489.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="513.9" width="500.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="500.2" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="512.4" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="524.6" y="513.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="597.8" y="513.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="646.6" y="513.9" width="244" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="890.6" y="513.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="927.2" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1d1d1d" x="939.4" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="513.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="538.3" width="500.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="500.2" y="538.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="512.4" y="538.3" width="427" height="24.65" shape-rendering="crispEdges"/><rect fill="#1d1d1d" x="939.4" y="538.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="538.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="562.7" width="976" height="24.65" shape-rendering="crispEdges"/>
+      <g class="terminal-2986389628-matrix">
+      <text class="terminal-2986389628-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-2986389628-line-0)">
+  </text><text class="terminal-2986389628-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-2986389628-line-1)">
+  </text><text class="terminal-2986389628-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-2986389628-line-2)">
+  </text><text class="terminal-2986389628-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-2986389628-line-3)">
+  </text><text class="terminal-2986389628-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-2986389628-line-4)">
+  </text><text class="terminal-2986389628-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-2986389628-line-5)">
+  </text><text class="terminal-2986389628-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-2986389628-line-6)">
+  </text><text class="terminal-2986389628-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-2986389628-line-7)">
+  </text><text class="terminal-2986389628-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-2986389628-line-8)">
+  </text><text class="terminal-2986389628-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-2986389628-line-9)">
+  </text><text class="terminal-2986389628-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-2986389628-line-10)">
+  </text><text class="terminal-2986389628-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-2986389628-line-11)">
+  </text><text class="terminal-2986389628-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-2986389628-line-12)">
+  </text><text class="terminal-2986389628-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-2986389628-line-13)">
+  </text><text class="terminal-2986389628-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-2986389628-line-14)">
+  </text><text class="terminal-2986389628-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-2986389628-line-15)">
+  </text><text class="terminal-2986389628-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-2986389628-line-16)">
+  </text><text class="terminal-2986389628-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-2986389628-line-17)">
+  </text><text class="terminal-2986389628-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-2986389628-line-18)">
+  </text><text class="terminal-2986389628-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-2986389628-line-19)">
+  </text><text class="terminal-2986389628-r3" x="500.2" y="508" textLength="12.2" clip-path="url(#terminal-2986389628-line-20)">▎</text><text class="terminal-2986389628-r4" x="939.4" y="508" textLength="12.2" clip-path="url(#terminal-2986389628-line-20)">▊</text><text class="terminal-2986389628-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-2986389628-line-20)">
+  </text><text class="terminal-2986389628-r3" x="500.2" y="532.4" textLength="12.2" clip-path="url(#terminal-2986389628-line-21)">▎</text><text class="terminal-2986389628-r5" x="524.6" y="532.4" textLength="73.2" clip-path="url(#terminal-2986389628-line-21)">Click&#160;</text><text class="terminal-2986389628-r6" x="597.8" y="532.4" textLength="48.8" clip-path="url(#terminal-2986389628-line-21)">here</text><text class="terminal-2986389628-r5" x="646.6" y="532.4" textLength="244" clip-path="url(#terminal-2986389628-line-21)">&#160;for&#160;the&#160;bell&#160;sound.</text><text class="terminal-2986389628-r4" x="939.4" y="532.4" textLength="12.2" clip-path="url(#terminal-2986389628-line-21)">▊</text><text class="terminal-2986389628-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-2986389628-line-21)">
+  </text><text class="terminal-2986389628-r3" x="500.2" y="556.8" textLength="12.2" clip-path="url(#terminal-2986389628-line-22)">▎</text><text class="terminal-2986389628-r4" x="939.4" y="556.8" textLength="12.2" clip-path="url(#terminal-2986389628-line-22)">▊</text><text class="terminal-2986389628-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-2986389628-line-22)">
+  </text>
+      </g>
+      </g>
+  </svg>
+  
+  '''
+# ---
+# name: test_notification_with_inline_link_hover
+  '''
+  <svg class="rich-terminal" viewBox="0 0 994 635.5999999999999" xmlns="http://www.w3.org/2000/svg">
+      <!-- Generated with Rich https://www.textualize.io -->
+      <style>
+  
+      @font-face {
+          font-family: "Fira Code";
+          src: local("FiraCode-Regular"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
+          font-style: normal;
+          font-weight: 400;
+      }
+      @font-face {
+          font-family: "Fira Code";
+          src: local("FiraCode-Bold"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
+                  url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
+          font-style: bold;
+          font-weight: 700;
+      }
+  
+      .terminal-651867408-matrix {
+          font-family: Fira Code, monospace;
+          font-size: 20px;
+          line-height: 24.4px;
+          font-variant-east-asian: full-width;
+      }
+  
+      .terminal-651867408-title {
+          font-size: 18px;
+          font-weight: bold;
+          font-family: arial;
+      }
+  
+      .terminal-651867408-r1 { fill: #e1e1e1 }
+  .terminal-651867408-r2 { fill: #c5c8c6 }
+  .terminal-651867408-r3 { fill: #56c278 }
+  .terminal-651867408-r4 { fill: #2e3339 }
+  .terminal-651867408-r5 { fill: #e3e4e4 }
+  .terminal-651867408-r6 { fill: #ddedf9;font-weight: bold }
+      </style>
+  
+      <defs>
+      <clipPath id="terminal-651867408-clip-terminal">
+        <rect x="0" y="0" width="975.0" height="584.5999999999999" />
+      </clipPath>
+      <clipPath id="terminal-651867408-line-0">
+      <rect x="0" y="1.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-1">
+      <rect x="0" y="25.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-2">
+      <rect x="0" y="50.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-3">
+      <rect x="0" y="74.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-4">
+      <rect x="0" y="99.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-5">
+      <rect x="0" y="123.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-6">
+      <rect x="0" y="147.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-7">
+      <rect x="0" y="172.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-8">
+      <rect x="0" y="196.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-9">
+      <rect x="0" y="221.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-10">
+      <rect x="0" y="245.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-11">
+      <rect x="0" y="269.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-12">
+      <rect x="0" y="294.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-13">
+      <rect x="0" y="318.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-14">
+      <rect x="0" y="343.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-15">
+      <rect x="0" y="367.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-16">
+      <rect x="0" y="391.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-17">
+      <rect x="0" y="416.3" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-18">
+      <rect x="0" y="440.7" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-19">
+      <rect x="0" y="465.1" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-20">
+      <rect x="0" y="489.5" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-21">
+      <rect x="0" y="513.9" width="976" height="24.65"/>
+              </clipPath>
+  <clipPath id="terminal-651867408-line-22">
+      <rect x="0" y="538.3" width="976" height="24.65"/>
+              </clipPath>
+      </defs>
+  
+      <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="992" height="633.6" rx="8"/><text class="terminal-651867408-title" fill="#c5c8c6" text-anchor="middle" x="496" y="27">NotifyWithInlineLinkApp</text>
+              <g transform="translate(26,22)">
+              <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+              <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+              <circle cx="44" cy="0" r="7" fill="#28c840"/>
+              </g>
+          
+      <g transform="translate(9, 41)" clip-path="url(#terminal-651867408-clip-terminal)">
+      <rect fill="#1e1e1e" x="0" y="1.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="25.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="50.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="74.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="99.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="123.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="147.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="172.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="196.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="221.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="245.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="269.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="294.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="318.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="343.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="367.5" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="391.9" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="416.3" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="440.7" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="465.1" width="976" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="489.5" width="500.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="500.2" y="489.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="512.4" y="489.5" width="427" height="24.65" shape-rendering="crispEdges"/><rect fill="#1d1d1d" x="939.4" y="489.5" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="489.5" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="513.9" width="500.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="500.2" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="512.4" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="524.6" y="513.9" width="73.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#0178d4" x="597.8" y="513.9" width="48.8" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="646.6" y="513.9" width="244" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="890.6" y="513.9" width="36.6" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="927.2" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1d1d1d" x="939.4" y="513.9" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="513.9" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="538.3" width="500.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="500.2" y="538.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#2e3339" x="512.4" y="538.3" width="427" height="24.65" shape-rendering="crispEdges"/><rect fill="#1d1d1d" x="939.4" y="538.3" width="12.2" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="951.6" y="538.3" width="24.4" height="24.65" shape-rendering="crispEdges"/><rect fill="#1e1e1e" x="0" y="562.7" width="976" height="24.65" shape-rendering="crispEdges"/>
+      <g class="terminal-651867408-matrix">
+      <text class="terminal-651867408-r2" x="976" y="20" textLength="12.2" clip-path="url(#terminal-651867408-line-0)">
+  </text><text class="terminal-651867408-r2" x="976" y="44.4" textLength="12.2" clip-path="url(#terminal-651867408-line-1)">
+  </text><text class="terminal-651867408-r2" x="976" y="68.8" textLength="12.2" clip-path="url(#terminal-651867408-line-2)">
+  </text><text class="terminal-651867408-r2" x="976" y="93.2" textLength="12.2" clip-path="url(#terminal-651867408-line-3)">
+  </text><text class="terminal-651867408-r2" x="976" y="117.6" textLength="12.2" clip-path="url(#terminal-651867408-line-4)">
+  </text><text class="terminal-651867408-r2" x="976" y="142" textLength="12.2" clip-path="url(#terminal-651867408-line-5)">
+  </text><text class="terminal-651867408-r2" x="976" y="166.4" textLength="12.2" clip-path="url(#terminal-651867408-line-6)">
+  </text><text class="terminal-651867408-r2" x="976" y="190.8" textLength="12.2" clip-path="url(#terminal-651867408-line-7)">
+  </text><text class="terminal-651867408-r2" x="976" y="215.2" textLength="12.2" clip-path="url(#terminal-651867408-line-8)">
+  </text><text class="terminal-651867408-r2" x="976" y="239.6" textLength="12.2" clip-path="url(#terminal-651867408-line-9)">
+  </text><text class="terminal-651867408-r2" x="976" y="264" textLength="12.2" clip-path="url(#terminal-651867408-line-10)">
+  </text><text class="terminal-651867408-r2" x="976" y="288.4" textLength="12.2" clip-path="url(#terminal-651867408-line-11)">
+  </text><text class="terminal-651867408-r2" x="976" y="312.8" textLength="12.2" clip-path="url(#terminal-651867408-line-12)">
+  </text><text class="terminal-651867408-r2" x="976" y="337.2" textLength="12.2" clip-path="url(#terminal-651867408-line-13)">
+  </text><text class="terminal-651867408-r2" x="976" y="361.6" textLength="12.2" clip-path="url(#terminal-651867408-line-14)">
+  </text><text class="terminal-651867408-r2" x="976" y="386" textLength="12.2" clip-path="url(#terminal-651867408-line-15)">
+  </text><text class="terminal-651867408-r2" x="976" y="410.4" textLength="12.2" clip-path="url(#terminal-651867408-line-16)">
+  </text><text class="terminal-651867408-r2" x="976" y="434.8" textLength="12.2" clip-path="url(#terminal-651867408-line-17)">
+  </text><text class="terminal-651867408-r2" x="976" y="459.2" textLength="12.2" clip-path="url(#terminal-651867408-line-18)">
+  </text><text class="terminal-651867408-r2" x="976" y="483.6" textLength="12.2" clip-path="url(#terminal-651867408-line-19)">
+  </text><text class="terminal-651867408-r3" x="500.2" y="508" textLength="12.2" clip-path="url(#terminal-651867408-line-20)">▎</text><text class="terminal-651867408-r4" x="939.4" y="508" textLength="12.2" clip-path="url(#terminal-651867408-line-20)">▊</text><text class="terminal-651867408-r2" x="976" y="508" textLength="12.2" clip-path="url(#terminal-651867408-line-20)">
+  </text><text class="terminal-651867408-r3" x="500.2" y="532.4" textLength="12.2" clip-path="url(#terminal-651867408-line-21)">▎</text><text class="terminal-651867408-r5" x="524.6" y="532.4" textLength="73.2" clip-path="url(#terminal-651867408-line-21)">Click&#160;</text><text class="terminal-651867408-r6" x="597.8" y="532.4" textLength="48.8" clip-path="url(#terminal-651867408-line-21)">here</text><text class="terminal-651867408-r5" x="646.6" y="532.4" textLength="244" clip-path="url(#terminal-651867408-line-21)">&#160;for&#160;the&#160;bell&#160;sound.</text><text class="terminal-651867408-r4" x="939.4" y="532.4" textLength="12.2" clip-path="url(#terminal-651867408-line-21)">▊</text><text class="terminal-651867408-r2" x="976" y="532.4" textLength="12.2" clip-path="url(#terminal-651867408-line-21)">
+  </text><text class="terminal-651867408-r3" x="500.2" y="556.8" textLength="12.2" clip-path="url(#terminal-651867408-line-22)">▎</text><text class="terminal-651867408-r4" x="939.4" y="556.8" textLength="12.2" clip-path="url(#terminal-651867408-line-22)">▊</text><text class="terminal-651867408-r2" x="976" y="556.8" textLength="12.2" clip-path="url(#terminal-651867408-line-22)">
+  </text>
+      </g>
+      </g>
+  </svg>
+  
+  '''
+# ---
 # name: test_notifications_example
   '''
   <svg class="rich-terminal" viewBox="0 0 994 635.5999999999999" xmlns="http://www.w3.org/2000/svg">
diff --git a/tests/snapshot_tests/snapshot_apps/notification_with_inline_link.py b/tests/snapshot_tests/snapshot_apps/notification_with_inline_link.py
new file mode 100644
index 0000000000..c12b03c41b
--- /dev/null
+++ b/tests/snapshot_tests/snapshot_apps/notification_with_inline_link.py
@@ -0,0 +1,11 @@
+from textual.app import App
+
+
+class NotifyWithInlineLinkApp(App):
+    def on_mount(self) -> None:
+        self.notify("Click [@click=bell]here[/] for the bell sound.")
+
+
+if __name__ == "__main__":
+    app = NotifyWithInlineLinkApp()
+    app.run()
diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py
index 7797a8c2bd..e22e6015d6 100644
--- a/tests/snapshot_tests/test_snapshots.py
+++ b/tests/snapshot_tests/test_snapshots.py
@@ -707,6 +707,23 @@ def test_notifications_through_modes(snap_compare) -> None:
     assert snap_compare(SNAPSHOT_APPS_DIR / "notification_through_modes.py")
 
 
+def test_notification_with_inline_link(snap_compare) -> None:
+    # https://github.com/Textualize/textual/issues/3530
+    assert snap_compare(SNAPSHOT_APPS_DIR / "notification_with_inline_link.py")
+
+
+def test_notification_with_inline_link_hover(snap_compare) -> None:
+    # https://github.com/Textualize/textual/issues/3530
+    async def run_before(pilot) -> None:
+        await pilot.pause()
+        await pilot.hover("Toast", offset=(8, 1))
+
+    assert snap_compare(
+        SNAPSHOT_APPS_DIR / "notification_with_inline_link.py",
+        run_before=run_before,
+    )
+
+
 def test_print_capture(snap_compare) -> None:
     assert snap_compare(SNAPSHOT_APPS_DIR / "capture_print.py")
 

Missing link styles in `Toast` notifcations
This follows on from discussion #3526 about adding a clickable link in a notification.

 The `Toast` widget doesn't inherit the styling from the base class, so the "default" links styling defined in Widget is lost. You can obviously just add it back in like I've done here, but it might be nicer if you didn't have to do that.

```python
# toast_with_link.py

import webbrowser

from textual.app import App


class ToastLinkApp(App):
    # Uncomment the CSS_PATH to see the link!
    # CSS_PATH = "toast_with_link.css"

    def on_mount(self) -> None:
        self.notify(
            "Don't forget to "
            "[@click=link('https://github.com/Textualize/textual')]star[/] "
            "the repository!",
            timeout=10,
        )

    def action_link(self, href: str) -> None:
        webbrowser.open(href)


if __name__ == "__main__":
    app = ToastLinkApp()
    app.run()
```

```css
/* toast_with_link.css */

Toast {
  link-background: ;
  link-color: $text;
  link-style: underline;
  link-hover-background: $accent;
  link-hover-color: $text;
  link-hover-style: bold not underline;
}
```


