diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1c17f764a872006f49f4cbd82a10e56c5980c18..9c8e8ea0917d73554377b667c5dbd748c289f076 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,4 @@
+2024-05-16 - add emails forwarding
 2024-05-15 - add role and exercise info into team selectors
 2024-05-15 - fix draft persistance
 2024-05-15 - add a title to the toolbar and improve the section titles
diff --git a/frontend/src/analyst/Emails/index.tsx b/frontend/src/analyst/Emails/index.tsx
index f24a6b5f7f24a3ddca905a9a74948d3d05d2ce78..88e19b908dfb5eb8f750dd8ebdba4695d7481e19 100644
--- a/frontend/src/analyst/Emails/index.tsx
+++ b/frontend/src/analyst/Emails/index.tsx
@@ -111,6 +111,8 @@ const Emails: FC<EmailsProps> = ({ exerciseId, tab, threadId }) => {
           composeButtonTitle='Composing is not allowed in the analyst view'
           allowReplying={false}
           replyButtonTitle='Replying is not allowed in the analyst view'
+          allowForwarding={false}
+          forwardButtonTitle='Forwarding is not allowed in the analyst view'
         />
       </div>
     </div>
diff --git a/frontend/src/components/ContentArea/index.tsx b/frontend/src/components/ContentArea/index.tsx
index ba337eeacd157845e302f193d8ca4a72e61d6ae4..88de541a13fa49b7b29a4aacf4bbb1ba648e4e3e 100644
--- a/frontend/src/components/ContentArea/index.tsx
+++ b/frontend/src/components/ContentArea/index.tsx
@@ -1,6 +1,6 @@
 import { TextArea } from '@blueprintjs/core'
 import type { Dispatch, FC, SetStateAction } from 'react'
-import { memo, useCallback } from 'react'
+import { useCallback } from 'react'
 
 interface ContentAreaProps {
   content: string
@@ -16,10 +16,9 @@ const ContentArea: FC<ContentAreaProps> = ({ content, setContent }) => {
     [setContent]
   )
 
-  // TODO: optimize defaultValue to not be updated on every render
   return (
     <TextArea
-      defaultValue={content}
+      value={content}
       style={{
         resize: 'none',
         overflowY: 'auto',
@@ -32,4 +31,4 @@ const ContentArea: FC<ContentAreaProps> = ({ content, setContent }) => {
   )
 }
 
-export default memo(ContentArea)
+export default ContentArea
diff --git a/frontend/src/components/ErrorMessage/index.tsx b/frontend/src/components/ErrorMessage/index.tsx
index 0989b171a04357d38f7777bfb4ff05d17385d490..9db9602454c26cc4087b884e795434180397aecb 100644
--- a/frontend/src/components/ErrorMessage/index.tsx
+++ b/frontend/src/components/ErrorMessage/index.tsx
@@ -1,11 +1,12 @@
+import { Colors } from '@blueprintjs/core'
 import { css } from '@emotion/css'
 import type { FC, PropsWithChildren } from 'react'
 
 const div = css`
-  background: #c20b0b;
-  color: var(--white-1);
-  border-radius: var(--md);
-  padding: var(--md) var(--lg);
+  background: ${Colors.RED1};
+  color: ${Colors.WHITE};
+  border-radius: 0.5rem;
+  padding: 0.5rem 1rem;
 `
 
 const ErrorMessage: FC<PropsWithChildren> = ({ children }) => (
diff --git a/frontend/src/email/EmailForm/InstructorEmailForm.tsx b/frontend/src/email/EmailForm/InstructorEmailForm.tsx
index f46e2cf8e8687d9d09153ce4b09e5a5f52334e10..87b05e1c71d33c1c8fbe97d2670f5e05b7773e2d 100644
--- a/frontend/src/email/EmailForm/InstructorEmailForm.tsx
+++ b/frontend/src/email/EmailForm/InstructorEmailForm.tsx
@@ -13,7 +13,6 @@ import { memo, useCallback, useMemo } from 'react'
 import { MainDrawer } from './css'
 import HeaderArea from './modules/HeaderArea'
 import type { EmailFormProps } from './typing'
-import useFormState from './useFormState'
 import useThreadSubmission from './useThreadSubmission'
 
 const footer = css`
@@ -23,22 +22,12 @@ const footer = css`
   gap: 0.5rem;
 `
 
-interface InstructorEmailFormProps extends EmailFormProps {
-  exerciseId: string
-}
-
-const InstructorEmailForm: FC<InstructorEmailFormProps> = ({
+const InstructorEmailForm: FC<EmailFormProps> = ({
   exerciseId,
   emailThread,
   teamId,
-  onFinish,
-}) => {
-  const formState = useFormState({
-    inInstructor: true,
-    teamId,
-    emailThreadId: emailThread?.id,
-  })
-  const {
+  onSuccess,
+  formState: {
     fileInfo,
     setFileInfo,
     activateMilestone,
@@ -57,8 +46,8 @@ const InstructorEmailForm: FC<InstructorEmailFormProps> = ({
     setSubject,
     template,
     setTemplate,
-  } = formState
-
+  },
+}) => {
   const [sendEmailMutate, { loading }] = useSendEmail()
   const { notify } = useNotifyContext()
 
@@ -82,11 +71,11 @@ const InstructorEmailForm: FC<InstructorEmailFormProps> = ({
           },
         },
         onCompleted: () => {
-          discardDraft()
+          discardDraft(false)
           notify('Email sent successfully')
+          onSuccess()
         },
       })
-      onFinish()
     },
   })
 
@@ -135,14 +124,14 @@ const InstructorEmailForm: FC<InstructorEmailFormProps> = ({
   )
 
   const onDiscard = useCallback(() => {
-    discardDraft()
-    onFinish()
-  }, [discardDraft, onFinish])
+    discardDraft(true)
+    onSuccess()
+  }, [discardDraft, onSuccess])
 
   const onSave = useCallback(() => {
     storeDraft()
-    onFinish()
-  }, [storeDraft, onFinish])
+    onSuccess()
+  }, [storeDraft, onSuccess])
 
   const onSubmit = useCallback(() => {
     onSend()
diff --git a/frontend/src/email/EmailForm/TraineeEmailForm.tsx b/frontend/src/email/EmailForm/TraineeEmailForm.tsx
index 46f278719fb9779ca646130d652da71e7c1a2151..1f108c38ec1363e39a664a47b59e7d7e73f585c1 100644
--- a/frontend/src/email/EmailForm/TraineeEmailForm.tsx
+++ b/frontend/src/email/EmailForm/TraineeEmailForm.tsx
@@ -11,7 +11,6 @@ import { memo, useCallback } from 'react'
 import { MainDrawer } from './css'
 import HeaderArea from './modules/HeaderArea'
 import type { EmailFormProps } from './typing'
-import useFormState from './useFormState'
 import useThreadSubmission from './useThreadSubmission'
 
 const footer = css`
@@ -21,18 +20,13 @@ const footer = css`
   gap: 0.5rem;
 `
 
-interface TraineeEmailFormProps extends EmailFormProps {
-  teamAddress: string
-}
-
-const TraineeEmailForm: FC<TraineeEmailFormProps> = ({
+const TraineeEmailForm: FC<EmailFormProps> = ({
   exerciseId,
   emailThread,
-  teamAddress,
   teamId,
-  onFinish,
-}) => {
-  const {
+  onSuccess,
+  formState: {
+    senderAddress,
     fileInfo,
     setFileInfo,
     content,
@@ -43,20 +37,18 @@ const TraineeEmailForm: FC<TraineeEmailFormProps> = ({
     setSelectedContacts,
     subject,
     setSubject,
-  } = useFormState({
-    teamAddress,
-    inInstructor: false,
-    teamId,
-    emailThreadId: emailThread?.id,
-  })
-
+  },
+}) => {
   const [sendEmailMutate, { loading }] = useSendEmail()
   const { notify } = useNotifyContext()
 
   const { onSend } = useThreadSubmission({
     ...(emailThread
       ? { existingThreadId: emailThread.id }
-      : { participantAddresses: [...selectedContacts, teamAddress], subject }),
+      : {
+          participantAddresses: [...selectedContacts, senderAddress],
+          subject,
+        }),
     exerciseId,
     content,
     fileId: fileInfo?.id,
@@ -65,17 +57,17 @@ const TraineeEmailForm: FC<TraineeEmailFormProps> = ({
         variables: {
           sendEmailInput: {
             threadId,
-            senderAddress: teamAddress,
+            senderAddress,
             content,
             fileId: fileInfo?.id,
           },
         },
         onCompleted: () => {
-          discardDraft()
+          discardDraft(false)
           notify('Email sent successfully')
+          onSuccess()
         },
       })
-      onFinish()
     },
   })
 
@@ -89,14 +81,14 @@ const TraineeEmailForm: FC<TraineeEmailFormProps> = ({
   const traineeList = (emailContacts?.emailContacts || []).filter(notEmpty)
 
   const onDiscard = useCallback(() => {
-    discardDraft()
-    onFinish()
-  }, [discardDraft, onFinish])
+    discardDraft(true)
+    onSuccess()
+  }, [discardDraft, onSuccess])
 
   const onSave = useCallback(() => {
     storeDraft()
-    onFinish()
-  }, [storeDraft, onFinish])
+    onSuccess()
+  }, [onSuccess, storeDraft])
 
   return (
     <div className={MainDrawer}>
@@ -112,7 +104,7 @@ const TraineeEmailForm: FC<TraineeEmailFormProps> = ({
               setSubject,
             })}
         contacts={traineeList}
-        senderAddress={teamAddress}
+        senderAddress={senderAddress}
         exerciseId={exerciseId}
       />
       <Divider style={{ margin: '0.5rem 0' }} />
diff --git a/frontend/src/email/EmailForm/modules/HeaderArea.tsx b/frontend/src/email/EmailForm/modules/HeaderArea.tsx
index 369735ba78c2844792fbe33426b86db4991647a2..ad112c68a30b44a96013049e2145548550ae4912 100644
--- a/frontend/src/email/EmailForm/modules/HeaderArea.tsx
+++ b/frontend/src/email/EmailForm/modules/HeaderArea.tsx
@@ -9,7 +9,7 @@ import type { EmailThread } from '@inject/graphql/fragments/EmailThread.generate
 import type { FileInfo } from '@inject/graphql/fragments/FileInfo.generated'
 import notEmpty from '@inject/shared/utils/notEmpty'
 import type { Dispatch, FC, SetStateAction } from 'react'
-import { memo, useEffect, useMemo } from 'react'
+import { useEffect, useMemo } from 'react'
 
 const gridContainer = css`
   display: grid;
@@ -145,7 +145,7 @@ const HeaderArea: FC<HeaderAreaProps> = ({
       ) : (
         <InputGroup
           placeholder='Subject'
-          value={subject}
+          value={subject || ''}
           onChange={event => setSubject(event.currentTarget.value)}
         />
       )}
@@ -173,4 +173,4 @@ const HeaderArea: FC<HeaderAreaProps> = ({
   )
 }
 
-export default memo(HeaderArea)
+export default HeaderArea
diff --git a/frontend/src/email/EmailForm/typing.ts b/frontend/src/email/EmailForm/typing.ts
index 4c12fb9ae9851584151e6e19d79b243bce708502..7a22a3187708e055ee57bd1c4b762ddd9058c07d 100644
--- a/frontend/src/email/EmailForm/typing.ts
+++ b/frontend/src/email/EmailForm/typing.ts
@@ -1,8 +1,10 @@
 import type { EmailThread } from '@inject/graphql/fragments/EmailThread.generated'
+import type { FormState } from './useFormState'
 
 export type EmailFormProps = {
   exerciseId: string
   emailThread?: EmailThread
   teamId: string
-  onFinish: () => void
+  onSuccess: () => void
+  formState: FormState
 }
diff --git a/frontend/src/email/EmailForm/useFormState.ts b/frontend/src/email/EmailForm/useFormState.ts
index bfe3395207dc0e75206edb851b414d7c736e75c9..7ce4808701aac6b28082d8b22aef99f389c9e5ba 100644
--- a/frontend/src/email/EmailForm/useFormState.ts
+++ b/frontend/src/email/EmailForm/useFormState.ts
@@ -3,23 +3,26 @@ import type { FileInfo } from '@inject/graphql/fragments/FileInfo.generated'
 import { useWriteEmailDraft } from '@inject/graphql/mutations/clientonly/WriteEmailDraft.generated'
 import { useGetEmailTemplateLazyQuery } from '@inject/graphql/queries/GetEmailTemplate.generated'
 import { useGetFileInfoLazyQuery } from '@inject/graphql/queries/GetFileInfo.generated'
-import { useReturnLocalEmailDraft } from '@inject/graphql/queries/clientonly/ReturnLocalEmailDraft.generated'
+import { useReturnLocalEmailDraftLazyQuery } from '@inject/graphql/queries/clientonly/ReturnLocalEmailDraft.generated'
 import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
 import notEmpty from '@inject/shared/utils/notEmpty'
 import type { Dispatch, SetStateAction } from 'react'
-import { useEffect, useState } from 'react'
+import { useCallback, useState } from 'react'
+
+export interface FormStateInput {
+  loadDraft: boolean
+  emailThreadId: string | undefined
+  senderAddress?: string
+  selectedContacts?: string[]
+  subject?: string | undefined
+  template?: EmailTemplate | undefined
+  activateMilestone?: string
+  deactivateMilestone?: string
+  content?: string
+  fileInfo?: FileInfo | undefined
+}
 
 export interface FormState {
-  content: string
-  setContent: Dispatch<SetStateAction<string>>
-  activateMilestone: string
-  setActivateMilestone: Dispatch<SetStateAction<string>>
-  deactivateMilestone: string
-  setDeactivateMilestone: Dispatch<SetStateAction<string>>
-  fileInfo: FileInfo | undefined
-  setFileInfo: Dispatch<SetStateAction<FileInfo | undefined>>
-  storeDraft: () => void
-  discardDraft: () => void
   senderAddress: string
   setSenderAddress: Dispatch<SetStateAction<string>>
   selectedContacts: string[]
@@ -28,77 +31,130 @@ export interface FormState {
   setSubject: Dispatch<SetStateAction<string | undefined>>
   template: EmailTemplate | undefined
   setTemplate: Dispatch<SetStateAction<EmailTemplate | undefined>>
+  activateMilestone: string
+  setActivateMilestone: Dispatch<SetStateAction<string>>
+  deactivateMilestone: string
+  setDeactivateMilestone: Dispatch<SetStateAction<string>>
+  content: string
+  setContent: Dispatch<SetStateAction<string>>
+  fileInfo: FileInfo | undefined
+  setFileInfo: Dispatch<SetStateAction<FileInfo | undefined>>
+  storeDraft: () => void
+  discardDraft: (notifyOnSuccess: boolean) => void
+  reload: (input: FormStateInput) => void
 }
 
 const useFormState = ({
-  teamAddress,
   teamId,
   inInstructor,
-  emailThreadId,
+  teamAddress,
 }: {
   teamId: string
   inInstructor: boolean
-  emailThreadId: string | undefined
-  teamAddress?: string | undefined
+  teamAddress?: string
 }): FormState => {
+  const [emailThreadId, setEmailThreadId] = useState<string | undefined>()
+  const [loadDraft, setLoadDraft] = useState<boolean>()
+
   const [senderAddress, setSenderAddress] = useState<string>(teamAddress || '')
-  const [content, setContent] = useState<string>('')
-  const [activateMilestone, setActivateMilestone] = useState<string>('')
-  const [deactivateMilestone, setDeactivateMilestone] = useState<string>('')
-  const [fileInfo, setFileInfo] = useState<FileInfo>()
   const [selectedContacts, setSelectedContacts] = useState<string[]>([])
   const [subject, setSubject] = useState<string | undefined>()
+
   const [template, setTemplate] = useState<EmailTemplate>()
+  const [activateMilestone, setActivateMilestone] = useState<string>('')
+  const [deactivateMilestone, setDeactivateMilestone] = useState<string>('')
+
+  const [content, setContent] = useState<string>('')
+  const [fileInfo, setFileInfo] = useState<FileInfo | undefined>()
 
   const { notify } = useNotifyContext()
 
   const [draftMutate] = useWriteEmailDraft()
-  const { data: draftData } = useReturnLocalEmailDraft({
-    variables: {
-      teamId,
-      instructor: inInstructor,
-      emailThreadId: emailThreadId || null,
-    },
-  })
+  const [getDraft] = useReturnLocalEmailDraftLazyQuery()
 
   const [getFileInfo] = useGetFileInfoLazyQuery()
   const [getTemplate] = useGetEmailTemplateLazyQuery()
 
-  useEffect(() => {
-    const draft = draftData?.returnLocalEmailDraft
-    if (!draft) {
-      return
-    }
+  const loadDraftCallback = useCallback(
+    async (emailThreadId: string | undefined) => {
+      getDraft({
+        variables: {
+          teamId,
+          instructor: inInstructor,
+          emailThreadId: emailThreadId || null,
+        },
+        onCompleted: data => {
+          const draft = data?.returnLocalEmailDraft
+          if (!draft) {
+            return
+          }
 
-    setContent(draft.content || '')
-    setActivateMilestone(draft.activateMilestone || '')
-    setDeactivateMilestone(draft.deactivateMilestone || '')
+          setContent(draft.content || '')
+          setActivateMilestone(draft.activateMilestone || '')
+          setDeactivateMilestone(draft.deactivateMilestone || '')
 
-    if (draft.fileId) {
-      getFileInfo({
-        variables: { fileInfoId: draft.fileId },
-        onCompleted: data => setFileInfo(data.fileInfo || undefined),
-      })
-    } else {
-      setFileInfo(undefined)
-    }
-    if (draft.templateId) {
-      getTemplate({
-        variables: { templateId: draft.templateId },
-        onCompleted: data => setTemplate(data.threadTemplate || undefined),
+          if (draft.fileId) {
+            getFileInfo({
+              variables: { fileInfoId: draft.fileId },
+              onCompleted: data => setFileInfo(data.fileInfo || undefined),
+            })
+          } else {
+            setFileInfo(undefined)
+          }
+          if (draft.templateId) {
+            getTemplate({
+              variables: { templateId: draft.templateId },
+              onCompleted: data =>
+                setTemplate(data.threadTemplate || undefined),
+            })
+          } else {
+            setTemplate(undefined)
+          }
+
+          if (draft.emailThreadId) {
+            return
+          }
+
+          setSenderAddress(draft.senderAddress || teamAddress || '')
+          setSelectedContacts(draft.selectedContacts?.filter(notEmpty) || [])
+          setSubject(draft.subject || undefined)
+        },
       })
-    } else {
-      setTemplate(undefined)
-    }
+    },
+    [getDraft, getFileInfo, getTemplate, inInstructor, teamAddress, teamId]
+  )
 
-    if (draft.emailThreadId) {
-      return
-    }
+  const reload = useCallback(
+    ({
+      loadDraft,
+      emailThreadId,
+      activateMilestone,
+      content,
+      deactivateMilestone,
+      fileInfo,
+      selectedContacts,
+      senderAddress,
+      subject,
+      template,
+    }: FormStateInput) => {
+      setLoadDraft(loadDraft)
+      setEmailThreadId(emailThreadId)
 
-    setSenderAddress(draft.senderAddress || teamAddress || '')
-    setSelectedContacts(draft.selectedContacts?.filter(notEmpty) || [])
-    setSubject(draft.subject || undefined)
-  }, [draftData?.returnLocalEmailDraft, getFileInfo, getTemplate, teamAddress])
+      setSenderAddress(senderAddress || teamAddress || '')
+      setSelectedContacts(selectedContacts || [])
+      setSubject(subject)
+      setTemplate(template)
+      setActivateMilestone(activateMilestone || '')
+      setDeactivateMilestone(deactivateMilestone || '')
+      setContent(content || '')
+      setFileInfo(fileInfo)
+
+      if (loadDraft) {
+        loadDraftCallback(emailThreadId)
+      }
+    },
+    [loadDraftCallback, teamAddress]
+  )
 
   const storeDraft = () => {
     draftMutate({
@@ -128,7 +184,12 @@ const useFormState = ({
       })
   }
 
-  const discardDraft = () => {
+  /**
+   * Resets the form state to the initial state. If the form was loaded from
+   * a draft, the draft will be discarded
+   * (currently, that means updating its values to the initial values).
+   */
+  const discardDraft = (notifyOnSuccess: boolean) => {
     setSenderAddress(teamAddress || '')
     setContent('')
     setActivateMilestone('')
@@ -138,6 +199,12 @@ const useFormState = ({
     setSubject(undefined)
     setTemplate(undefined)
 
+    if (!loadDraft) {
+      notify('Draft discarded')
+      return
+    }
+
+    // TODO: remove draft instead of updating it
     draftMutate({
       variables: {
         emailDraft: {
@@ -154,24 +221,20 @@ const useFormState = ({
           templateId: undefined,
         },
       },
-    }).catch(err => {
-      notify(`Error: ${err.message}`, {
-        intent: 'danger',
-      })
     })
+      .then(() => {
+        if (notifyOnSuccess) {
+          notify('Draft discarded')
+        }
+      })
+      .catch(err => {
+        notify(`Error: ${err.message}`, {
+          intent: 'danger',
+        })
+      })
   }
 
   return {
-    content,
-    setContent,
-    activateMilestone,
-    setActivateMilestone,
-    deactivateMilestone,
-    setDeactivateMilestone,
-    fileInfo,
-    setFileInfo,
-    storeDraft,
-    discardDraft,
     senderAddress,
     setSenderAddress,
     selectedContacts,
@@ -180,6 +243,17 @@ const useFormState = ({
     setSubject,
     template,
     setTemplate,
+    activateMilestone,
+    setActivateMilestone,
+    deactivateMilestone,
+    setDeactivateMilestone,
+    content,
+    setContent,
+    fileInfo,
+    setFileInfo,
+    storeDraft,
+    discardDraft,
+    reload,
   }
 }
 
diff --git a/frontend/src/email/EmailFormOverlay/TraineeEmailFormOverlay.tsx b/frontend/src/email/EmailFormOverlay/TraineeEmailFormOverlay.tsx
index e34fce983aa30f795b1cc6d401ecaeb30da8d73e..ea196bb7f5979877fd42555f9e6cf400ea44ade4 100644
--- a/frontend/src/email/EmailFormOverlay/TraineeEmailFormOverlay.tsx
+++ b/frontend/src/email/EmailFormOverlay/TraineeEmailFormOverlay.tsx
@@ -1,3 +1,6 @@
+import ErrorMessage from '@/components/ErrorMessage'
+import { NonIdealState, Spinner } from '@blueprintjs/core'
+import { useGetTeamEmailParticipant } from '@inject/graphql/queries/GetTeamEmailParticipant.generated'
 import type { FC } from 'react'
 import EmailFormOverlay from '.'
 
@@ -9,12 +12,43 @@ interface TraineeEmailFormOverlayProps {
 const TraineeEmailFormOverlay: FC<TraineeEmailFormOverlayProps> = ({
   teamId,
   exerciseId,
-}) => (
-  <EmailFormOverlay
-    teamId={teamId}
-    emailForm='trainee'
-    exerciseId={exerciseId}
-  />
-)
+}) => {
+  const { data, loading, error } = useGetTeamEmailParticipant({
+    variables: {
+      teamId,
+    },
+    skip: !teamId,
+  })
+
+  if (loading) {
+    return <Spinner />
+  }
+  if (error) {
+    return (
+      <ErrorMessage>
+        <h1>Error occurred!</h1>
+        <p>{error.message}</p>
+      </ErrorMessage>
+    )
+  }
+  if (!data || !data.teamEmailParticipant) {
+    return (
+      <NonIdealState
+        icon='low-voltage-pole'
+        title='No data'
+        description='Please wait for the data to come in'
+      />
+    )
+  }
+
+  return (
+    <EmailFormOverlay
+      teamId={teamId}
+      emailForm='trainee'
+      exerciseId={exerciseId}
+      teamAddress={data.teamEmailParticipant.address}
+    />
+  )
+}
 
 export default TraineeEmailFormOverlay
diff --git a/frontend/src/email/EmailFormOverlay/index.tsx b/frontend/src/email/EmailFormOverlay/index.tsx
index 0ec121f03cc17c8a2abf3642029d542b50d50353..8fb163816edb62232d8852f40be9927064cc97c4 100644
--- a/frontend/src/email/EmailFormOverlay/index.tsx
+++ b/frontend/src/email/EmailFormOverlay/index.tsx
@@ -1,11 +1,12 @@
 import { Button, Card, Overlay2 } from '@blueprintjs/core'
 import { css } from '@emotion/css'
 import type { EmailThread } from '@inject/graphql/fragments/EmailThread.generated'
-import { useGetTeamEmailParticipant } from '@inject/graphql/queries/GetTeamEmailParticipant.generated'
+import type { FileInfo } from '@inject/graphql/fragments/FileInfo.generated'
 import type { FC } from 'react'
-import { useEffect, useState } from 'react'
+import { useCallback, useEffect, useState } from 'react'
 import InstructorEmailForm from '../EmailForm/InstructorEmailForm'
 import TraineeEmailForm from '../EmailForm/TraineeEmailForm'
+import useFormState from '../EmailForm/useFormState'
 
 const card = css`
   height: 50vh;
@@ -20,12 +21,22 @@ const card = css`
 export const OPEN_COMPOSE_EVENT_TYPE = 'openCompose'
 export const OPEN_REPLY_EVENT_TYPE = 'openReply'
 
-interface EmailFormOverlayProps {
+type EmailFormOverlayProps = {
   teamId: string
-  emailForm: 'trainee' | 'instructor'
   exerciseId: string
+} & (
+  | { emailForm: 'trainee'; teamAddress: string }
+  | { emailForm: 'instructor'; teamAddress?: undefined }
+)
+
+interface ComposeInitialValues {
+  content?: string
+  fileInfo?: FileInfo
+  subject?: string
 }
 
+type OpenComposeEventPayload = ComposeInitialValues | undefined
+
 interface OpenReplyEventPayload {
   emailThread: EmailThread
 }
@@ -33,26 +44,60 @@ interface OpenReplyEventPayload {
 const EmailFormOverlay: FC<EmailFormOverlayProps> = ({
   teamId,
   emailForm,
+  teamAddress,
   exerciseId,
 }) => {
   const [open, setOpen] = useState(false)
   const [emailThread, setEmailThread] = useState<EmailThread | undefined>()
 
-  const handleOpenCompose = () => {
-    setOpen(true)
-    setEmailThread(undefined)
-  }
+  const formState = useFormState({
+    teamAddress: emailForm === 'trainee' ? teamAddress : undefined,
+    inInstructor: emailForm === 'instructor',
+    teamId,
+  })
+  const { reload } = formState
+
+  const handleOpenCompose = useCallback(
+    (event: CustomEvent<OpenComposeEventPayload>) => {
+      setOpen(true)
+      setEmailThread(undefined)
+
+      if (event.detail) {
+        const { content, fileInfo, subject } = event.detail
+        reload({
+          emailThreadId: undefined,
+          loadDraft: false,
+          content,
+          fileInfo,
+          subject,
+        })
+      } else {
+        reload({ emailThreadId: undefined, loadDraft: true })
+      }
+    },
+    [reload]
+  )
   useEffect(() => {
-    window.addEventListener(OPEN_COMPOSE_EVENT_TYPE, handleOpenCompose)
+    window.addEventListener(
+      OPEN_COMPOSE_EVENT_TYPE,
+      handleOpenCompose as EventListener
+    )
     return () => {
-      window.removeEventListener(OPEN_COMPOSE_EVENT_TYPE, handleOpenCompose)
+      window.removeEventListener(
+        OPEN_COMPOSE_EVENT_TYPE,
+        handleOpenCompose as EventListener
+      )
     }
-  }, [])
+  }, [handleOpenCompose])
 
-  const handleOpenReply = (event: CustomEvent<OpenReplyEventPayload>) => {
-    setOpen(true)
-    setEmailThread(event.detail.emailThread)
-  }
+  const handleOpenReply = useCallback(
+    (event: CustomEvent<OpenReplyEventPayload>) => {
+      setOpen(true)
+      setEmailThread(event.detail.emailThread)
+      reload({ emailThreadId: event.detail.emailThread.id, loadDraft: true })
+    },
+    [reload]
+  )
   useEffect(() => {
     window.addEventListener(
       OPEN_REPLY_EVENT_TYPE,
@@ -64,22 +109,7 @@ const EmailFormOverlay: FC<EmailFormOverlayProps> = ({
         handleOpenReply as EventListener
       )
     }
-  }, [])
-
-  const { data: teamParticipantData } = useGetTeamEmailParticipant({
-    variables: {
-      teamId,
-    },
-    skip: !teamId,
-  })
-  const [teamAddress, setTeamAddress] = useState<string>(
-    teamParticipantData?.teamEmailParticipant?.address || ''
-  )
-  useEffect(
-    () =>
-      setTeamAddress(teamParticipantData?.teamEmailParticipant?.address || ''),
-    [teamParticipantData?.teamEmailParticipant?.address]
-  )
+  }, [handleOpenReply])
 
   useEffect(() => {
     setOpen(false)
@@ -104,18 +134,19 @@ const EmailFormOverlay: FC<EmailFormOverlayProps> = ({
         />
         {emailForm === 'trainee' ? (
           <TraineeEmailForm
-            onFinish={() => setOpen(false)}
+            onSuccess={() => setOpen(false)}
             teamId={teamId}
             exerciseId={exerciseId}
-            teamAddress={teamAddress}
             emailThread={emailThread}
+            formState={formState}
           />
         ) : (
           <InstructorEmailForm
-            onFinish={() => setOpen(false)}
+            onSuccess={() => setOpen(false)}
             teamId={teamId}
             exerciseId={exerciseId}
             emailThread={emailThread}
+            formState={formState}
           />
         )}
       </Card>
diff --git a/frontend/src/email/TeamEmails/EmailCard.tsx b/frontend/src/email/TeamEmails/EmailCard.tsx
index 8fe1ca6eefb3fce4a8b95bfbcadda2684021859d..71d196ab51ca381bbd57e07e379cc28ec0dc6250 100644
--- a/frontend/src/email/TeamEmails/EmailCard.tsx
+++ b/frontend/src/email/TeamEmails/EmailCard.tsx
@@ -1,12 +1,28 @@
 import useFormatTimestamp from '@/analyst/useFormatTimestamp'
 import { HIGHLIGHTED_COLOR } from '@/analyst/utilities'
 import FileViewRedirectButton from '@/components/FileViewRedirectButton'
-import { Classes, Colors, Icon, Section, SectionCard } from '@blueprintjs/core'
+import {
+  Button,
+  Classes,
+  Colors,
+  Icon,
+  Section,
+  SectionCard,
+} from '@blueprintjs/core'
+import { css } from '@emotion/css'
 import type { Email } from '@inject/graphql/fragments/Email.generated'
 import { useWriteReadReceiptEmail } from '@inject/graphql/mutations/clientonly/WriteReadReceiptEmail.generated'
 import Timestamp from '@inject/shared/components/Timestamp'
 import type { FC } from 'react'
 import { useEffect, useMemo } from 'react'
+import { OPEN_COMPOSE_EVENT_TYPE } from '../EmailFormOverlay'
+
+const rightElement = css`
+  display: flex;
+  justify-content: space-between;
+  gap: 0.5rem;
+  align-items: center;
+`
 
 interface EmailCardProps {
   exerciseId: string
@@ -14,6 +30,9 @@ interface EmailCardProps {
   email: Email
   inAnalyst?: boolean
   inInstructor?: boolean
+  allowForwarding?: boolean
+  forwardButtonTitle?: string
+  subject: string
 }
 
 const EmailCard: FC<EmailCardProps> = ({
@@ -22,6 +41,9 @@ const EmailCard: FC<EmailCardProps> = ({
   email,
   inAnalyst,
   inInstructor,
+  allowForwarding,
+  forwardButtonTitle,
+  subject,
 }) => {
   const formatTimestamp = useFormatTimestamp()
 
@@ -60,7 +82,7 @@ const EmailCard: FC<EmailCardProps> = ({
       }
       title={email.sender.address}
       rightElement={
-        <span>
+        <div className={rightElement}>
           {inAnalyst ? (
             formatTimestamp(email.timestamp)
           ) : (
@@ -75,7 +97,26 @@ const EmailCard: FC<EmailCardProps> = ({
               }}
             />
           )}
-        </span>
+          <Button
+            minimal
+            icon='nest'
+            disabled={!allowForwarding}
+            title={forwardButtonTitle}
+            onClick={() =>
+              window.dispatchEvent(
+                new CustomEvent(OPEN_COMPOSE_EVENT_TYPE, {
+                  detail: {
+                    content: email.content.raw,
+                    fileInfo: email.content.fileInfo,
+                    subject,
+                  },
+                })
+              )
+            }
+          >
+            Forward
+          </Button>
+        </div>
       }
     >
       <SectionCard>
diff --git a/frontend/src/email/TeamEmails/InstructorTeamEmails.tsx b/frontend/src/email/TeamEmails/InstructorTeamEmails.tsx
index 96ad42a551a52c0dc6e8aedb10135ec5c82c4ecf..ac651ebff9244e388216ec971be51616e62fc0a2 100644
--- a/frontend/src/email/TeamEmails/InstructorTeamEmails.tsx
+++ b/frontend/src/email/TeamEmails/InstructorTeamEmails.tsx
@@ -51,6 +51,7 @@ const InstructorTeamEmails: FC<InstructorTeamEmailsProps> = ({
       replyButtonTitle={
         replyDisabled ? "You can't reply to emails between teams" : undefined
       }
+      allowForwarding
     />
   )
 }
diff --git a/frontend/src/email/TeamEmails/TraineeTeamEmails.tsx b/frontend/src/email/TeamEmails/TraineeTeamEmails.tsx
index eeebfa072604035eb84bcbdbc3c52fe0592cf47a..d78312d2bebbcbfe1ec2498b493ffe2d27d19f01 100644
--- a/frontend/src/email/TeamEmails/TraineeTeamEmails.tsx
+++ b/frontend/src/email/TeamEmails/TraineeTeamEmails.tsx
@@ -41,6 +41,7 @@ const TraineeTeamEmails: FC<TraineeTeamEmailsProps> = ({
       onClick={onClick}
       allowComposing
       allowReplying
+      allowForwarding
     />
   )
 }
diff --git a/frontend/src/email/TeamEmails/index.tsx b/frontend/src/email/TeamEmails/index.tsx
index 01b5847f9d186d6418ae3e889a2712a0702d08ba..7b43687fe0be42f97ac006f8283e43f769fea275 100644
--- a/frontend/src/email/TeamEmails/index.tsx
+++ b/frontend/src/email/TeamEmails/index.tsx
@@ -43,6 +43,8 @@ interface TeamEmailsProps {
   allowReplying: boolean
   replyButtonTitle?: string
   inInstructor?: boolean
+  allowForwarding: boolean
+  forwardButtonTitle?: string
 }
 
 const TeamEmails: FC<TeamEmailsProps> = ({
@@ -61,9 +63,17 @@ const TeamEmails: FC<TeamEmailsProps> = ({
   allowReplying,
   replyButtonTitle,
   inInstructor,
+  allowForwarding,
+  forwardButtonTitle,
 }) => {
   const emails = useMemo(
-    () => [...(selectedEmailThread?.emails || [])].reverse(),
+    () =>
+      [
+        ...(selectedEmailThread?.emails.map(email => ({
+          ...email,
+          subject: selectedEmailThread.subject,
+        })) || []),
+      ].reverse(),
     [selectedEmailThread]
   )
 
@@ -119,6 +129,9 @@ const TeamEmails: FC<TeamEmailsProps> = ({
                   email={email}
                   inAnalyst={inAnalyst}
                   inInstructor={inInstructor}
+                  allowForwarding={allowForwarding}
+                  forwardButtonTitle={forwardButtonTitle}
+                  subject={email.subject}
                 />
               ))}
             </div>
diff --git a/frontend/src/global.css b/frontend/src/global.css
index 7f4526b166382b97ab095eb25fbfa0815cf032e9..e158e8e19abe2906abb76b56b4e14ac5d8e545d1 100644
--- a/frontend/src/global.css
+++ b/frontend/src/global.css
@@ -5,27 +5,12 @@ html, body, #root {
 
 html {
   overflow-x: hidden;
-
-  /* overflow-y: hidden; */
 }
 
 body.bp5-dark {
   background-color: #2f343c;
 }
 
-body {
-  --green-1: #12ae12;
-  --white-1: #fffefe;
-  --grey-1: #aeaeae;
-  --grey-2: #9e9e93;
-  --blue-1: #43d4ee;
-  --sm: 0.25rem;
-  --md: 0.5rem;
-  --lg: 1rem;
-  --xl: 2rem;
-  --xxl: 4rem;
-}
-
 * {
   box-sizing: border-box;
 }
@@ -64,7 +49,6 @@ h6 {
 ::-webkit-scrollbar-track {
   border-radius: 8px;
   background-color: transparent;
-  /* border: 1px solid rgba(255, 255, 255, 0.2); */
   border-left: 2px none;
   border-right: 2px none;
   border-top: 2px none;
diff --git a/frontend/src/pages/(navbar)/exercise-panel.tsx b/frontend/src/pages/(navbar)/exercise-panel.tsx
index 0176e6417a470bf5d15e903cdafb35986b37df80..26c041e95f237699ae75870f56ca8f9cf9fb6790 100644
--- a/frontend/src/pages/(navbar)/exercise-panel.tsx
+++ b/frontend/src/pages/(navbar)/exercise-panel.tsx
@@ -14,7 +14,7 @@ const wrapper = css`
 `
 
 const ExercisePanel = () => {
-  useSetPageTitle('Admin Panel')
+  useSetPageTitle('Exercise Panel')
 
   return (
     <>
diff --git a/frontend/src/pages/(navbar)/graphiql.tsx b/frontend/src/pages/(navbar)/graphiql.tsx
index f5b96b241e3c62b570a4b0777ee34e2fd80de646..163b1b6479cc48fe7d554d54c8b7b015c86f31e3 100644
--- a/frontend/src/pages/(navbar)/graphiql.tsx
+++ b/frontend/src/pages/(navbar)/graphiql.tsx
@@ -1,7 +1,12 @@
+import { useSetPageTitle } from '@/utils'
 import { lazy } from 'react'
 
 const GraphiQLPage = lazy(() => import('@/logic/GraphiQL'))
 
-export const GraphiQL = () => <GraphiQLPage />
+export const GraphiQL = () => {
+  useSetPageTitle('GraphiQL')
+
+  return <GraphiQLPage />
+}
 
 export default GraphiQL
diff --git a/frontend/src/pages/(navbar)/index.tsx b/frontend/src/pages/(navbar)/index.tsx
index 5e5750aa395014ad03be54ede75123e6b9661271..e72d58d9b131e3d204498706578aa0ebb1e4865e 100644
--- a/frontend/src/pages/(navbar)/index.tsx
+++ b/frontend/src/pages/(navbar)/index.tsx
@@ -2,14 +2,12 @@ import InjectLogo from '@/assets/inject-logo--vertical-black.svg?react'
 import StaffSelector from '@/logic/StaffSelector'
 import TeamSelector from '@/logic/TeamSelector'
 import { useNavigate } from '@/router'
-import { useSetPageTitle } from '@/utils'
 import { Checkbox, Collapse } from '@blueprintjs/core'
 import { useAuthIdentity } from '@inject/graphql/auth'
 import Container from '@inject/shared/components/Container'
 import { useEffect, useState } from 'react'
 
 const Index = () => {
-  useSetPageTitle('Team Selection')
   const { isActive, isStaff, isSuperuser, isLogged, loading } = useAuthIdentity(
     !!window.INJECT_NOAUTH
   )
diff --git a/frontend/src/pages/(navbar)/settings.tsx b/frontend/src/pages/(navbar)/settings.tsx
index 0b742234837d06e2f09e57278c4299101e8030e5..d2fd5696a5bc54dbb21382e444f526f98a85df68 100644
--- a/frontend/src/pages/(navbar)/settings.tsx
+++ b/frontend/src/pages/(navbar)/settings.tsx
@@ -5,6 +5,7 @@ import InstructorTeams from '@/clientsettings/components/InstructorTeams'
 import Logout from '@/clientsettings/components/Logout'
 import Notification from '@/clientsettings/components/Notification'
 import RelativeTime from '@/clientsettings/components/RelativeTime'
+import { useSetPageTitle } from '@/utils'
 import { css } from '@emotion/css'
 import { useAuthIdentity } from '@inject/graphql/auth'
 import Container from '@inject/shared/components/Container'
@@ -14,6 +15,7 @@ const heading = css`
 `
 
 const Settings = () => {
+  useSetPageTitle('Settings')
   const { isStaff, isSuperuser } = useAuthIdentity(!!window.INJECT_NOAUTH)
 
   return (
diff --git a/frontend/src/pages/404.tsx b/frontend/src/pages/404.tsx
index c138c3fe7f6aa763482ce5aca8d1ec9ee0681a27..3f11b366fa20589ae6d7f31ef06f44ebf346b4e7 100644
--- a/frontend/src/pages/404.tsx
+++ b/frontend/src/pages/404.tsx
@@ -2,7 +2,7 @@ import { useSetPageTitle } from '@/utils'
 import { NonIdealState } from '@blueprintjs/core'
 
 const FourOhFour = () => {
-  useSetPageTitle('Page not Found')
+  useSetPageTitle('Page not found')
 
   return (
     <NonIdealState title='Page not found' icon='warning-sign'>
diff --git a/frontend/src/pages/analyst/_layout.tsx b/frontend/src/pages/analyst/_layout.tsx
index 351a8afb9d4c12d5f263f1eefef46bc285322b36..2fc70e22de2b9f11dbcc69b817eed066584e521d 100644
--- a/frontend/src/pages/analyst/_layout.tsx
+++ b/frontend/src/pages/analyst/_layout.tsx
@@ -1,8 +1,9 @@
-import { useStaffBoundary } from '@/utils'
+import { useSetPageTitle, useStaffBoundary } from '@/utils'
 import { Outlet } from 'react-router-dom'
 
 const Layout = () => {
   useStaffBoundary()
+  useSetPageTitle('Analyst')
 
   return <Outlet />
 }
diff --git a/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/email/_layout.tsx b/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/email/_layout.tsx
index 4ed7e655186a910dbe5e20e3b8558a39487b5929..252a49b3df44d12f31980e450af7af225505063e 100644
--- a/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/email/_layout.tsx
+++ b/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/email/_layout.tsx
@@ -1,7 +1,6 @@
 import { EmailSelection } from '@/analyst/utilities'
 import InstructorTeamEmails from '@/email/TeamEmails/InstructorTeamEmails'
 import { useNavigate, useParams } from '@/router'
-import { useSetPageTitle } from '@/utils'
 import { useGetEmailThreads } from '@inject/graphql/queries/GetEmailThreads.generated'
 import notEmpty from '@inject/shared/utils/notEmpty'
 
@@ -9,7 +8,6 @@ const Layout = () => {
   const { exerciseId, teamId, tab, channelId, threadId } = useParams(
     '/instructor/:exerciseId/:teamId/:channelId/email/:tab/:threadId'
   )
-  useSetPageTitle(`Team ${teamId} - Emails`)
   const nav = useNavigate()
 
   const { data: emailThreadsData } = useGetEmailThreads({
diff --git a/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/form/[actionLogId].tsx b/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/form/[actionLogId].tsx
index 37b9e71b7f5ece24df5e526bae1cccdeac6eccd4..7189b64621102364964acfafce9b51bc3f6edab0 100644
--- a/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/form/[actionLogId].tsx
+++ b/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/form/[actionLogId].tsx
@@ -1,17 +1,26 @@
-import { useParams } from '@/router'
+import { useNavigate, useParams } from '@/router'
 import InjectMessageView from '@/views/InjectMessageView'
 
 const Page = () => {
   const { teamId, exerciseId, channelId, actionLogId } = useParams(
     '/instructor/:exerciseId/:teamId/:channelId/form/:actionLogId'
   )
+  const nav = useNavigate()
 
   return (
     <InjectMessageView
       teamId={teamId}
       exerciseId={exerciseId}
-      channelId={channelId}
       actionLogId={actionLogId}
+      onBack={() =>
+        nav(`/instructor/:exerciseId/:teamId/:channelId/form`, {
+          params: {
+            teamId,
+            exerciseId,
+            channelId,
+          },
+        })
+      }
     />
   )
 }
diff --git a/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/info/[actionLogId].tsx b/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/info/[actionLogId].tsx
index 0f5e66c679b03b2295e7fc7233ca956415515d0e..4453c58b4b4fc3a3af6e1bb31ef653863dcfdf80 100644
--- a/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/info/[actionLogId].tsx
+++ b/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/info/[actionLogId].tsx
@@ -1,17 +1,26 @@
-import { useParams } from '@/router'
+import { useNavigate, useParams } from '@/router'
 import InjectMessageView from '@/views/InjectMessageView'
 
 const Page = () => {
   const { teamId, exerciseId, channelId, actionLogId } = useParams(
     '/instructor/:exerciseId/:teamId/:channelId/info/:actionLogId'
   )
+  const nav = useNavigate()
 
   return (
     <InjectMessageView
       teamId={teamId}
       exerciseId={exerciseId}
-      channelId={channelId}
       actionLogId={actionLogId}
+      onBack={() =>
+        nav(`/instructor/:exerciseId/:teamId/:channelId/info`, {
+          params: {
+            teamId,
+            exerciseId,
+            channelId,
+          },
+        })
+      }
     />
   )
 }
diff --git a/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/tool/[actionLogId].tsx b/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/tool/[actionLogId].tsx
index 6bfc38d6848e3b2b57cef7e1c5423162441becc2..7877894995a913390ac859ada6f55f78ba17912d 100644
--- a/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/tool/[actionLogId].tsx
+++ b/frontend/src/pages/instructor/[exerciseId]/[teamId]/[channelId]/tool/[actionLogId].tsx
@@ -1,17 +1,26 @@
-import { useParams } from '@/router'
+import { useNavigate, useParams } from '@/router'
 import InjectMessageView from '@/views/InjectMessageView'
 
 const Page = () => {
   const { teamId, exerciseId, channelId, actionLogId } = useParams(
     '/instructor/:exerciseId/:teamId/:channelId/tool/:actionLogId'
   )
+  const nav = useNavigate()
 
   return (
     <InjectMessageView
       teamId={teamId}
       exerciseId={exerciseId}
-      channelId={channelId}
       actionLogId={actionLogId}
+      onBack={() =>
+        nav(`/instructor/:exerciseId/:teamId/:channelId/tool`, {
+          params: {
+            teamId,
+            exerciseId,
+            channelId,
+          },
+        })
+      }
     />
   )
 }
diff --git a/frontend/src/pages/instructor/_layout.tsx b/frontend/src/pages/instructor/_layout.tsx
index 1129364df61e41395bc81308d633d391a20a229c..2a8289ba34e758f0cd3c65bf3ce18324d01c182f 100644
--- a/frontend/src/pages/instructor/_layout.tsx
+++ b/frontend/src/pages/instructor/_layout.tsx
@@ -1,11 +1,12 @@
 import { useParams } from '@/router'
-import { useStaffBoundary } from '@/utils'
+import { useSetPageTitle, useStaffBoundary } from '@/utils'
 import InstructorView from '@/views/InstructorView'
 import { Outlet } from 'react-router-dom'
 
 const Layout = () => {
   const { exerciseId, teamId } = useParams('/instructor/:exerciseId/:teamId')
   useStaffBoundary()
+  useSetPageTitle('Instructor')
 
   return (
     <InstructorView exerciseId={exerciseId} teamId={teamId}>
diff --git a/frontend/src/pages/instructor/index.tsx b/frontend/src/pages/instructor/index.tsx
index 16312bd878045651460cdd42dfa6aaaaf516c20b..4c07a7831c2c6bfa02bb91486125275ef60fd918 100644
--- a/frontend/src/pages/instructor/index.tsx
+++ b/frontend/src/pages/instructor/index.tsx
@@ -1,16 +1,11 @@
-import { useSetPageTitle } from '@/utils'
 import { NonIdealState } from '@blueprintjs/core'
 
-const InstructorIndexPage = () => {
-  useSetPageTitle('Instructor')
-
-  return (
-    <NonIdealState
-      icon='search'
-      title='No teams selected'
-      description='Select teams to interact with'
-    />
-  )
-}
+const InstructorIndexPage = () => (
+  <NonIdealState
+    icon='search'
+    title='No teams selected'
+    description='Select teams to interact with'
+  />
+)
 
 export default InstructorIndexPage
diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx
index 2b6d0d42d492c1561a93b2bb1dfb4336126a0b4d..50c13f000607cc774b291718cc9275017e60a82c 100644
--- a/frontend/src/pages/login.tsx
+++ b/frontend/src/pages/login.tsx
@@ -1,18 +1,23 @@
 import InjectLogo from '@/assets/inject-logo--vertical-black.svg?react'
 import Login from '@/logic/Login'
+import { useSetPageTitle } from '@/utils'
 import Container from '@inject/shared/components/Container'
 
-const LoginPage = () => (
-  <Container>
-    <InjectLogo
-      style={{
-        width: '100%',
-        height: '300px',
-        margin: 'auto',
-      }}
-    />
-    <Login />
-  </Container>
-)
+const LoginPage = () => {
+  useSetPageTitle('Login')
+
+  return (
+    <Container>
+      <InjectLogo
+        style={{
+          width: '100%',
+          height: '300px',
+          margin: 'auto',
+        }}
+      />
+      <Login />
+    </Container>
+  )
+}
 
 export default LoginPage
diff --git a/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/email/_layout.tsx b/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/email/_layout.tsx
index 9deebfe9d6a76d0835a1c2ed49a0bd873012442b..d0a15810936cbba6914d9e7776664ec0202b0068 100644
--- a/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/email/_layout.tsx
+++ b/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/email/_layout.tsx
@@ -1,7 +1,6 @@
 import { EmailSelection } from '@/analyst/utilities'
 import TraineeTeamEmails from '@/email/TeamEmails/TraineeTeamEmails'
 import { useNavigate, useParams } from '@/router'
-import { useSetPageTitle } from '@/utils'
 import { useGetEmailThreads } from '@inject/graphql/queries/GetEmailThreads.generated'
 import notEmpty from '@inject/shared/utils/notEmpty'
 
@@ -9,7 +8,6 @@ const Layout = () => {
   const { exerciseId, teamId, tab, channelId, threadId } = useParams(
     '/trainee/:exerciseId/:teamId/:channelId/email/:tab/:threadId'
   )
-  useSetPageTitle(`Team ${teamId} - Emails`)
   const nav = useNavigate()
 
   const { data: emailThreadsData } = useGetEmailThreads({
diff --git a/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/form/[actionLogId].tsx b/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/form/[actionLogId].tsx
index 030bda6db75161c812cc6f9f9cb7d65cddfe983d..ebf9cff61ca456b1404b6b283d278886f5639c25 100644
--- a/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/form/[actionLogId].tsx
+++ b/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/form/[actionLogId].tsx
@@ -1,17 +1,26 @@
-import { useParams } from '@/router'
+import { useNavigate, useParams } from '@/router'
 import InjectMessageView from '@/views/InjectMessageView'
 
 const Page = () => {
   const { teamId, exerciseId, channelId, actionLogId } = useParams(
     '/trainee/:exerciseId/:teamId/:channelId/form/:actionLogId'
   )
+  const nav = useNavigate()
 
   return (
     <InjectMessageView
       teamId={teamId}
       exerciseId={exerciseId}
-      channelId={channelId}
       actionLogId={actionLogId}
+      onBack={() =>
+        nav(`/trainee/:exerciseId/:teamId/:channelId/form`, {
+          params: {
+            teamId,
+            exerciseId,
+            channelId,
+          },
+        })
+      }
     />
   )
 }
diff --git a/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/info/[actionLogId].tsx b/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/info/[actionLogId].tsx
index 500f9f2815ef3f355777e9171827ea1a0c7ce7fa..9681b94eea4f8d65880a7de64832562f83b931a5 100644
--- a/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/info/[actionLogId].tsx
+++ b/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/info/[actionLogId].tsx
@@ -1,17 +1,26 @@
-import { useParams } from '@/router'
+import { useNavigate, useParams } from '@/router'
 import InjectMessageView from '@/views/InjectMessageView'
 
 const Page = () => {
   const { teamId, exerciseId, channelId, actionLogId } = useParams(
     '/trainee/:exerciseId/:teamId/:channelId/info/:actionLogId'
   )
+  const nav = useNavigate()
 
   return (
     <InjectMessageView
       teamId={teamId}
       exerciseId={exerciseId}
-      channelId={channelId}
       actionLogId={actionLogId}
+      onBack={() =>
+        nav(`/trainee/:exerciseId/:teamId/:channelId/info`, {
+          params: {
+            teamId,
+            exerciseId,
+            channelId,
+          },
+        })
+      }
     />
   )
 }
diff --git a/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/tool/[actionLogId].tsx b/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/tool/[actionLogId].tsx
index 6d5b6dbefa7497c068fffe5a4c6085f4e4ef1f24..e1df6f4fad6baeb3c9d4493e75b72aaa5c811fd0 100644
--- a/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/tool/[actionLogId].tsx
+++ b/frontend/src/pages/trainee/[exerciseId]/[teamId]/[channelId]/tool/[actionLogId].tsx
@@ -1,17 +1,26 @@
-import { useParams } from '@/router'
+import { useNavigate, useParams } from '@/router'
 import InjectMessageView from '@/views/InjectMessageView'
 
 const Page = () => {
   const { teamId, exerciseId, channelId, actionLogId } = useParams(
     '/trainee/:exerciseId/:teamId/:channelId/tool/:actionLogId'
   )
+  const nav = useNavigate()
 
   return (
     <InjectMessageView
       teamId={teamId}
       exerciseId={exerciseId}
-      channelId={channelId}
       actionLogId={actionLogId}
+      onBack={() =>
+        nav(`/trainee/:exerciseId/:teamId/:channelId/tool`, {
+          params: {
+            teamId,
+            exerciseId,
+            channelId,
+          },
+        })
+      }
     />
   )
 }
diff --git a/frontend/src/pages/trainee/_layout.tsx b/frontend/src/pages/trainee/_layout.tsx
index 931d1ee5b998fbd9331c9c47f16485c8b555b60d..e2ae62bb40557703a9bd53f168d21c45241cdb2f 100644
--- a/frontend/src/pages/trainee/_layout.tsx
+++ b/frontend/src/pages/trainee/_layout.tsx
@@ -1,9 +1,11 @@
 import { useParams } from '@/router'
+import { useSetPageTitle } from '@/utils'
 import TraineeView from '@/views/TraineeView'
 import { Outlet } from 'react-router-dom'
 
 const Layout = () => {
   const { exerciseId, teamId } = useParams('/trainee/:exerciseId/:teamId')
+  useSetPageTitle('Trainee')
 
   return (
     <TraineeView exerciseId={exerciseId} teamId={teamId}>
diff --git a/frontend/src/pages/users/_layout.tsx b/frontend/src/pages/users/_layout.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..b72fa7ef516545626480aa84266bbafa69c6e812
--- /dev/null
+++ b/frontend/src/pages/users/_layout.tsx
@@ -0,0 +1,10 @@
+import { useSetPageTitle } from '@/utils'
+import { Outlet } from 'react-router-dom'
+
+const Layout = () => {
+  useSetPageTitle('User management')
+
+  return <Outlet />
+}
+
+export default Layout
diff --git a/frontend/src/pages/users/index.tsx b/frontend/src/pages/users/index.tsx
index bae1aef41f5cf226bb1a8174e3adc1e0970c93c0..39aaf4ef2c1525ed9b232236b28337d73f0b63d1 100644
--- a/frontend/src/pages/users/index.tsx
+++ b/frontend/src/pages/users/index.tsx
@@ -1,10 +1,5 @@
 import UserTable from '@/users/UserTable'
-import { useSetPageTitle } from '@/utils'
 
-const UserManagementIndexPage = () => {
-  useSetPageTitle('User management')
-
-  return <UserTable />
-}
+const UserManagementIndexPage = () => <UserTable />
 
 export default UserManagementIndexPage
diff --git a/frontend/src/views/ChannelButton.tsx b/frontend/src/views/ChannelButton.tsx
index c05949f21689ab4f2deb0f579dd5813d4436ddd8..114feedc8f6a2b353fac3403efaa0971cf01d561 100644
--- a/frontend/src/views/ChannelButton.tsx
+++ b/frontend/src/views/ChannelButton.tsx
@@ -2,11 +2,12 @@ import { EmailSelection } from '@/analyst/utilities'
 import type { LinkType } from '@/components/LinkButton'
 import LinkButton from '@/components/LinkButton'
 import type { Path } from '@/router'
-import type { IconName } from '@blueprintjs/core'
+import { Colors, type IconName } from '@blueprintjs/core'
+import { Dot } from '@blueprintjs/icons'
 import type { Channel } from '@inject/graphql/fragments/Channel.generated'
 import { useWriteReadReceiptChannel } from '@inject/graphql/mutations/clientonly/WriteReadReceiptChannel.generated'
 import type { ChannelType } from '@inject/graphql/types'
-import type { FC } from 'react'
+import { useMemo, type FC } from 'react'
 import { matchPath } from 'react-router-dom'
 
 interface ChannelButtonProps {
@@ -131,24 +132,30 @@ const ChannelButton: FC<ChannelButtonProps> = ({
     )
   }
 
+  const isUnread = useMemo(
+    () =>
+      channel.readReceipt.find(
+        ({ readReceipt, teamId: receiptTeamId }) =>
+          receiptTeamId === teamId && readReceipt === null
+      ),
+    [channel.readReceipt, teamId]
+  )
+
   return (
     <LinkButton
       key={getLink(channel)[0] as string}
       link={getLink(channel)}
       button={{
         icon: getIcon(channel.type),
-        text: !hideLabel && channel.name,
         title: channel.name,
         fill: true,
         alignText: 'left',
         minimal: true,
         active: getActive(channel),
-        intent: channel.readReceipt?.find(
-          x => x?.teamId == teamId && x.readReceipt === null
-        )
-          ? 'warning'
-          : undefined,
+        intent: isUnread ? 'warning' : undefined,
+        rightIcon: isUnread ? <Dot color={Colors.RED3} /> : undefined,
         onClick: mutate,
+        children: !hideLabel && isUnread ? <b>{channel.name}</b> : channel.name,
       }}
     />
   )
diff --git a/frontend/src/views/InjectMessageView/index.tsx b/frontend/src/views/InjectMessageView/index.tsx
index d735551b3c55cb3cae48c6187778fc3b36163895..c1b429db4eef87f8ef56b85fbb669bae8ddb8700 100644
--- a/frontend/src/views/InjectMessageView/index.tsx
+++ b/frontend/src/views/InjectMessageView/index.tsx
@@ -1,6 +1,5 @@
 import InjectMessage from '@/actionlog/InjectMessage'
 import ErrorMessage from '@/components/ErrorMessage'
-import { useNavigate } from '@/router'
 import { Button } from '@blueprintjs/core'
 import { css } from '@emotion/css'
 import { useGetSingleActionLog } from '@inject/graphql/queries/GetSingleActionLog.generated'
@@ -19,22 +18,21 @@ const wrapper = css`
 interface InjectMessageViewProps {
   teamId: string
   exerciseId: string
-  channelId: string
   actionLogId: string
+  onBack: () => void
 }
 
 const InjectMessageView: FC<InjectMessageViewProps> = ({
   teamId,
   exerciseId,
-  channelId,
   actionLogId,
+  onBack,
 }) => {
   const { data } = useGetSingleActionLog({
     variables: {
       logId: actionLogId,
     },
   })
-  const nav = useNavigate()
   const inInstructor = useInInstructor()
 
   if (!data || !data.actionLog) {
@@ -49,18 +47,7 @@ const InjectMessageView: FC<InjectMessageViewProps> = ({
           text='Back'
           minimal
           style={{ marginBottom: '0.25rem' }}
-          onClick={() =>
-            nav(
-              `/${inInstructor ? 'instructor' : 'trainee'}/:exerciseId/:teamId/:channelId/info`,
-              {
-                params: {
-                  teamId,
-                  exerciseId,
-                  channelId,
-                },
-              }
-            )
-          }
+          onClick={onBack}
         />
         <InjectMessage
           exerciseId={exerciseId}
diff --git a/graphql/client/resolvers.ts b/graphql/client/resolvers.ts
index 2b2e9241ad91f9fda3cfed2d335edae1ed7e1932..aa76639b27db22a1234f02869a3e5304d40dc873 100644
--- a/graphql/client/resolvers.ts
+++ b/graphql/client/resolvers.ts
@@ -48,7 +48,7 @@ const resolvers: Resolvers = {
       const item = localStorage.getItem(
         await getKey(id, getKeyContext(variables.instructor))
       )
-      const emailDraft = JSON.parse(item || '') as EmailDraftType
+      const emailDraft = JSON.parse(item || '{}') as EmailDraftType
       return {
         __typename: 'EmailDraftType',
         ...emailDraft,
diff --git a/shared/document/plugins/pdf/PDFControls.tsx b/shared/document/plugins/pdf/PDFControls.tsx
index f9b033a713e51a75aee6fda056b14747534d1420..bd7d93abd28f596e10b2b6d41e87bf6d2a9259db 100644
--- a/shared/document/plugins/pdf/PDFControls.tsx
+++ b/shared/document/plugins/pdf/PDFControls.tsx
@@ -1,5 +1,4 @@
-import { Button } from '@blueprintjs/core'
-import { useTranslation } from '@cyntler/react-doc-viewer/dist/esm/hooks/useTranslation'
+import { Button, ButtonGroup, Card, Divider } from '@blueprintjs/core'
 import { PDFContext } from '@cyntler/react-doc-viewer/dist/esm/renderers/pdf/state'
 import {
   setPDFPaginated,
@@ -9,95 +8,76 @@ import { css } from '@emotion/css'
 import { useContext } from 'react'
 import PDFPagination from './PDFPagination'
 
-const pdfsticky = css`
-  position: sticky;
-  top: 0.5rem;
-  left: 0;
-  right: 0;
-  z-index: 4;
-  padding-left: 0.5rem;
-  padding-right: 0.5rem;
-  overflow: visible;
-`
-
 const pdfcontrol = css`
   display: flex;
-  gap: 0.5rem;
-  justify-content: flex-end;
-  align-items: center;
-  background-color: white;
+  justify-content: space-between;
   padding: 0.5rem;
-  border-radius: 0.5rem;
-  box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.2);
+`
+
+const buttonGroup = css`
+  display: flex;
+  justify-content: center;
+`
 
-  .bp5-dark & {
-    background-color: #2f343c;
-  }
+const viewToggle = css`
+  display: flex;
+  gap: 1rem;
+  align-items: center;
 `
 
 const PDFControls = () => {
-  const { t } = useTranslation()
   const {
-    state: {
-      mainState,
-      paginated,
-      zoomLevel,
-      numPages,
-      zoomJump,
-      defaultZoomLevel,
-    },
+    state: { paginated, zoomLevel, numPages, zoomJump, defaultZoomLevel },
     dispatch,
   } = useContext(PDFContext)
 
-  const currentDocument = mainState?.currentDocument || null
-
   return (
-    <div className={pdfsticky}>
-      <div id='pdf-controls' className={pdfcontrol}>
-        {paginated && numPages > 1 && <PDFPagination />}
-
-        {currentDocument?.fileData && (
+    <Card id='pdf-controls' className={pdfcontrol}>
+      <div className={viewToggle}>
+        {numPages > 1 && (
           <Button
-            id='pdf-download'
-            onClick={() => {
-              const link = document.createElement('a')
-              link.href = currentDocument?.fileData as string
-              link.download = currentDocument?.fileName || currentDocument?.uri
-              link.dispatchEvent(new MouseEvent('click'))
-            }}
-            title={t('downloadButtonLabel')}
-            icon='cloud-download'
+            id='pdf-toggle-pagination'
+            onMouseDown={() => dispatch(setPDFPaginated(!paginated))}
+            icon={paginated ? 'sort' : 'duplicate'}
+            minimal
+            text={
+              paginated
+                ? 'Switch to continuos scroll'
+                : 'Switch to paginated view'
+            }
+            title={
+              paginated
+                ? 'Switch to continuous scroll'
+                : 'Switch to paginated view'
+            }
           />
         )}
+        {paginated && numPages > 1 && <PDFPagination />}
+      </div>
 
+      <ButtonGroup minimal className={buttonGroup}>
         <Button
           id='pdf-zoom-out'
           onMouseDown={() => dispatch(setZoomLevel(zoomLevel - zoomJump))}
           icon='zoom-out'
+          title='Zoom out'
         />
-
         <Button
           id='pdf-zoom-in'
           onMouseDown={() => dispatch(setZoomLevel(zoomLevel + zoomJump))}
           icon='zoom-in'
+          title='Zoom in'
         />
-
+        <Divider />
         <Button
           id='pdf-zoom-reset'
           onMouseDown={() => dispatch(setZoomLevel(defaultZoomLevel))}
           disabled={zoomLevel === defaultZoomLevel}
           icon='zoom-to-fit'
+          title='Zoom to fit'
         />
-
-        {numPages > 1 && (
-          <Button
-            id='pdf-toggle-pagination'
-            onMouseDown={() => dispatch(setPDFPaginated(!paginated))}
-            icon={paginated ? 'header' : 'document'}
-          />
-        )}
-      </div>
-    </div>
+      </ButtonGroup>
+    </Card>
   )
 }
 
diff --git a/shared/document/plugins/pdf/PDFPages.tsx b/shared/document/plugins/pdf/PDFPages.tsx
index f2aa05796b4e12c218ebf862ce0d8c2cb915e7e4..cd594909c459029c8d73b9d16939d41fac3efd2d 100644
--- a/shared/document/plugins/pdf/PDFPages.tsx
+++ b/shared/document/plugins/pdf/PDFPages.tsx
@@ -9,8 +9,13 @@ import PDFAllPages from './PDFAllPages'
 import PDFSinglePage from './PDFSinglePage'
 
 const pdfpage = css`
+  flex: 1;
+  overflow-y: auto;
   display: flex;
   flex-direction: column;
+`
+
+const documentComponent = css`
   margin: 0 auto;
 `
 
@@ -30,16 +35,18 @@ const PDFPages = () => {
   if (!currentDocument || currentDocument.fileData === undefined) return null
 
   return (
-    <Document
-      className={pdfpage}
-      file={currentDocument.fileData}
-      externalLinkTarget='_blank'
-      externalLinkRel='noopener noreferrer'
-      onLoadSuccess={({ numPages }) => dispatch(setNumPages(numPages))}
-      loading={<span>{t('pdfPluginLoading')}</span>}
-    >
-      {paginated ? <PDFSinglePage /> : <PDFAllPages />}
-    </Document>
+    <div className={pdfpage}>
+      <Document
+        className={documentComponent}
+        file={currentDocument.fileData}
+        externalLinkTarget='_blank'
+        externalLinkRel='noopener noreferrer'
+        onLoadSuccess={({ numPages }) => dispatch(setNumPages(numPages))}
+        loading={<span>{t('pdfPluginLoading')}</span>}
+      >
+        {paginated ? <PDFSinglePage /> : <PDFAllPages />}
+      </Document>
+    </div>
   )
 }
 
diff --git a/shared/document/plugins/pdf/PDFPagination.tsx b/shared/document/plugins/pdf/PDFPagination.tsx
index 428914d5061438a3199b6c4324d8a9958b3a6374..8cb106e55d877b3c967b265f87a6c59aa86a9e0e 100644
--- a/shared/document/plugins/pdf/PDFPagination.tsx
+++ b/shared/document/plugins/pdf/PDFPagination.tsx
@@ -2,8 +2,16 @@ import { Button } from '@blueprintjs/core'
 import { useTranslation } from '@cyntler/react-doc-viewer/dist/esm/hooks/useTranslation'
 import { PDFContext } from '@cyntler/react-doc-viewer/dist/esm/renderers/pdf/state'
 import { setCurrentPage } from '@cyntler/react-doc-viewer/dist/esm/renderers/pdf/state/actions'
+import { css } from '@emotion/css'
 import { useContext } from 'react'
 
+const pdfPagination = css`
+  display: flex;
+  gap: 1rem;
+  justify-content: center;
+  align-items: center;
+`
+
 const PDFPagination = () => {
   const {
     state: { currentPage, numPages },
@@ -12,12 +20,14 @@ const PDFPagination = () => {
   const { t } = useTranslation()
 
   return (
-    <>
+    <div className={pdfPagination}>
       <Button
         id='pdf-pagination-prev'
         onClick={() => dispatch(setCurrentPage(currentPage - 1))}
         disabled={currentPage === 1}
         icon='chevron-left'
+        title='Previous page'
+        minimal
       />
 
       <span id='pdf-pagination-info'>
@@ -32,8 +42,10 @@ const PDFPagination = () => {
         onClick={() => dispatch(setCurrentPage(currentPage + 1))}
         disabled={currentPage >= numPages}
         icon='chevron-right'
+        title='Next page'
+        minimal
       />
-    </>
+    </div>
   )
 }
 
diff --git a/shared/document/plugins/pdf/index.tsx b/shared/document/plugins/pdf/index.tsx
index e567a352f5ad96a2e32fd71c6aabc16e9022b69b..3520abd8526022abe048c53a470d746b4c3576c0 100644
--- a/shared/document/plugins/pdf/index.tsx
+++ b/shared/document/plugins/pdf/index.tsx
@@ -17,6 +17,7 @@ const container = css`
   flex-direction: column;
   flex: 1;
   overflow-y: auto;
+  gap: 1rem;
 `
 
 pdfjs.GlobalWorkerOptions.workerSrc = pdfWorker
diff --git a/shared/document/viewer.tsx b/shared/document/viewer.tsx
index daa56ec47127eb7e761597647ef307bd4518eaf8..1d40dd9a70dc5ea90472b4b93a76de26745b1a12 100644
--- a/shared/document/viewer.tsx
+++ b/shared/document/viewer.tsx
@@ -81,7 +81,11 @@ const DocViewerComponent: FC<DocViewerProps> = ({ doc }) => {
           </a>
         </ButtonGroup>
       </div>
-      <DocViewer documents={docs} pluginRenderers={plugins} />
+      <DocViewer
+        documents={docs}
+        pluginRenderers={plugins}
+        config={{ pdfVerticalScrollByDefault: true }}
+      />
     </div>
   )
 }