diff --git a/examples/Console/Colors/Program.cs b/examples/Console/Colors/Program.cs
index 5e7708d..b73fc50 100644
--- a/examples/Console/Colors/Program.cs
+++ b/examples/Console/Colors/Program.cs
@@ -7,7 +7,7 @@ namespace Spectre.Console.Examples
             /////////////////////////////////////////////////////////////////
             // No colors
             /////////////////////////////////////////////////////////////////
-            if (AnsiConsole.Profile.ColorSystem == ColorSystem.NoColors)
+            if (AnsiConsole.Profile.Capabilities.ColorSystem == ColorSystem.NoColors)
             {
                 AnsiConsole.WriteLine("No colors are supported.");
                 return;
diff --git a/examples/Console/Info/Program.cs b/examples/Console/Info/Program.cs
index 2d757df..304836a 100644
--- a/examples/Console/Info/Program.cs
+++ b/examples/Console/Info/Program.cs
@@ -8,13 +8,13 @@ namespace Spectre.Console.Examples
                 .AddColumn(new GridColumn().NoWrap().PadRight(4))
                 .AddColumn()
                 .AddRow("[b]Enrichers[/]", string.Join(", ", AnsiConsole.Profile.Enrichers))
-                .AddRow("[b]Color system[/]", $"{AnsiConsole.Profile.ColorSystem}")
+                .AddRow("[b]Color system[/]", $"{AnsiConsole.Profile.Capabilities.ColorSystem}")
                 .AddRow("[b]Unicode?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Unicode)}")
                 .AddRow("[b]Supports ansi?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Ansi)}")
                 .AddRow("[b]Supports links?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Links)}")
                 .AddRow("[b]Legacy console?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Legacy)}")
                 .AddRow("[b]Interactive?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Interactive)}")
-                .AddRow("[b]TTY?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Tty)}")
+                .AddRow("[b]Terminal?[/]", $"{YesNo(AnsiConsole.Profile.Out.IsTerminal)}")
                 .AddRow("[b]Buffer width[/]", $"{AnsiConsole.Console.Profile.Width}")
                 .AddRow("[b]Buffer height[/]", $"{AnsiConsole.Console.Profile.Height}")
                 .AddRow("[b]Encoding[/]", $"{AnsiConsole.Console.Profile.Encoding.EncodingName}");
diff --git a/src/Spectre.Console.Testing/Fakes/FakeAnsiConsole.cs b/src/Spectre.Console.Testing/Fakes/FakeAnsiConsole.cs
index da1f36d..2e1149c 100644
--- a/src/Spectre.Console.Testing/Fakes/FakeAnsiConsole.cs
+++ b/src/Spectre.Console.Testing/Fakes/FakeAnsiConsole.cs
@@ -33,7 +33,7 @@ namespace Spectre.Console.Testing
             {
                 Ansi = ansi,
                 ColorSystem = (ColorSystemSupport)colors,
-                Out = _writer,
+                Out = new AnsiConsoleOutput(_writer),
                 Enrichment = new ProfileEnrichment
                 {
                     UseDefaultEnrichers = false,
diff --git a/src/Spectre.Console.Testing/Fakes/FakeCapabilities.cs b/src/Spectre.Console.Testing/Fakes/FakeCapabilities.cs
index 85547e1..6feedea 100644
--- a/src/Spectre.Console.Testing/Fakes/FakeCapabilities.cs
+++ b/src/Spectre.Console.Testing/Fakes/FakeCapabilities.cs
@@ -2,13 +2,15 @@ namespace Spectre.Console.Testing
 {
     public sealed class FakeCapabilities : IReadOnlyCapabilities
     {
+        public ColorSystem ColorSystem { get; set; } = ColorSystem.TrueColor;
+
         public bool Ansi { get; set; }
 
         public bool Links { get; set; }
 
         public bool Legacy { get; set; }
 
-        public bool Tty { get; set; }
+        public bool IsTerminal { get; set; }
 
         public bool Interactive { get; set; }
 
diff --git a/src/Spectre.Console.Testing/Fakes/FakeConsole.cs b/src/Spectre.Console.Testing/Fakes/FakeConsole.cs
index 8474fe9..335de66 100644
--- a/src/Spectre.Console.Testing/Fakes/FakeConsole.cs
+++ b/src/Spectre.Console.Testing/Fakes/FakeConsole.cs
@@ -16,7 +16,7 @@ namespace Spectre.Console.Testing
         public RenderPipeline Pipeline { get; }
 
         public FakeConsoleInput Input { get; }
-        public string Output => Profile.Out.ToString();
+        public string Output => Profile.Out.Writer.ToString();
         public IReadOnlyList<string> Lines => Output.TrimEnd('\n').Split(new char[] { '\n' });
 
         public FakeConsole(
@@ -28,10 +28,10 @@ namespace Spectre.Console.Testing
             ExclusivityMode = new FakeExclusivityMode();
             Pipeline = new RenderPipeline();
 
-            Profile = new Profile(new StringWriter(), encoding ?? Encoding.UTF8);
+            Profile = new Profile(new AnsiConsoleOutput(new StringWriter()), encoding ?? Encoding.UTF8);
             Profile.Width = width;
             Profile.Height = height;
-            Profile.ColorSystem = colorSystem;
+            Profile.Capabilities.ColorSystem = colorSystem;
             Profile.Capabilities.Ansi = supportsAnsi;
             Profile.Capabilities.Legacy = legacyConsole;
             Profile.Capabilities.Interactive = interactive;
@@ -41,7 +41,7 @@ namespace Spectre.Console.Testing
 
         public void Dispose()
         {
-            Profile.Out.Dispose();
+            Profile.Out.Writer.Dispose();
         }
 
         public void Clear(bool home)
@@ -52,7 +52,7 @@ namespace Spectre.Console.Testing
         {
             foreach (var segment in renderable.GetSegments(this))
             {
-                Profile.Out.Write(segment.Text);
+                Profile.Out.Writer.Write(segment.Text);
             }
         }
 
diff --git a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Colors.cs b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Colors.cs
index a07c1f5..c03ebe6 100644
--- a/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Colors.cs
+++ b/src/Spectre.Console.Tests/Unit/AnsiConsoleTests.Colors.cs
@@ -19,11 +19,11 @@ namespace Spectre.Console.Tests.Unit
             var console = AnsiConsole.Create(new AnsiConsoleSettings
             {
                 ColorSystem = requested,
-                Out = new StringWriter(),
+                Out = new AnsiConsoleOutput(new StringWriter()),
             });
 
             // Then
-            console.Profile.ColorSystem.ShouldBe(expected);
+            console.Profile.Capabilities.ColorSystem.ShouldBe(expected);
         }
 
         public sealed class TrueColor
diff --git a/src/Spectre.Console.Tests/Unit/Progress/ProgressColumnFixture.cs b/src/Spectre.Console.Tests/Unit/Progress/ProgressColumnFixture.cs
index 58a51bd..9d2550c 100644
--- a/src/Spectre.Console.Tests/Unit/Progress/ProgressColumnFixture.cs
+++ b/src/Spectre.Console.Tests/Unit/Progress/ProgressColumnFixture.cs
@@ -20,7 +20,7 @@ namespace Spectre.Console.Tests.Unit
         public string Render()
         {
             var console = new FakeConsole();
-            var context = new RenderContext(console.Profile.ColorSystem, console.Profile.Capabilities);
+            var context = new RenderContext(console.Profile.Capabilities);
             console.Write(Column.Render(context, Task, TimeSpan.Zero));
             return console.Output;
         }
diff --git a/src/Spectre.Console.Tests/Unit/RuleTests.cs b/src/Spectre.Console.Tests/Unit/RuleTests.cs
index 71a8358..36d98d7 100644
--- a/src/Spectre.Console.Tests/Unit/RuleTests.cs
+++ b/src/Spectre.Console.Tests/Unit/RuleTests.cs
@@ -130,7 +130,6 @@ namespace Spectre.Console.Tests.Unit
         }
 
         [Theory]
-        [InlineData(0, "Hello World Hello World Hello World Hello World Hello World", "")]
         [InlineData(1, "Hello World Hello World Hello World Hello World Hello World", "─")]
         [InlineData(2, "Hello World Hello World Hello World Hello World Hello World", "──")]
         [InlineData(3, "Hello World Hello World Hello World Hello World Hello World", "───")]
diff --git a/src/Spectre.Console.Tests/Unit/TextTests.cs b/src/Spectre.Console.Tests/Unit/TextTests.cs
index 013a499..6504779 100644
--- a/src/Spectre.Console.Tests/Unit/TextTests.cs
+++ b/src/Spectre.Console.Tests/Unit/TextTests.cs
@@ -15,7 +15,7 @@ namespace Spectre.Console.Tests.Unit
             var text = new Text("Foo Bar Baz\nQux\nLol mobile");
 
             // When
-            var result = ((IRenderable)text).Measure(new RenderContext(ColorSystem.TrueColor, caps), 80);
+            var result = ((IRenderable)text).Measure(new RenderContext(caps), 80);
 
             // Then
             result.Min.ShouldBe(6);
@@ -29,7 +29,7 @@ namespace Spectre.Console.Tests.Unit
             var text = new Text("Foo Bar Baz\nQux\nLol mobile");
 
             // When
-            var result = ((IRenderable)text).Measure(new RenderContext(ColorSystem.TrueColor, caps), 80);
+            var result = ((IRenderable)text).Measure(new RenderContext(caps), 80);
 
             // Then
             result.Max.ShouldBe(11);
diff --git a/src/Spectre.Console/AnsiConsole.cs b/src/Spectre.Console/AnsiConsole.cs
index 2baebf8..85cf2c7 100644
--- a/src/Spectre.Console/AnsiConsole.cs
+++ b/src/Spectre.Console/AnsiConsole.cs
@@ -15,7 +15,7 @@ namespace Spectre.Console
                 {
                     Ansi = AnsiSupport.Detect,
                     ColorSystem = ColorSystemSupport.Detect,
-                    Out = System.Console.Out,
+                    Out = new AnsiConsoleOutput(System.Console.Out),
                 });
 
                 Created = true;
diff --git a/src/Spectre.Console/AnsiConsoleFactory.cs b/src/Spectre.Console/AnsiConsoleFactory.cs
index 43194c4..3d8e0e5 100644
--- a/src/Spectre.Console/AnsiConsoleFactory.cs
+++ b/src/Spectre.Console/AnsiConsoleFactory.cs
@@ -22,13 +22,18 @@ namespace Spectre.Console
                 throw new ArgumentNullException(nameof(settings));
             }
 
-            var buffer = settings.Out ?? System.Console.Out;
+            var output = settings.Out ?? new AnsiConsoleOutput(System.Console.Out);
+            if (output.Writer == null)
+            {
+                throw new InvalidOperationException("Output writer was null");
+            }
 
             // Detect if the terminal support ANSI or not
-            var (supportsAnsi, legacyConsole) = DetectAnsi(settings, buffer);
+            var (supportsAnsi, legacyConsole) = DetectAnsi(settings, output.Writer);
 
             // Use console encoding or fall back to provided encoding
-            var encoding = buffer.IsStandardOut() || buffer.IsStandardError() ? System.Console.OutputEncoding : buffer.Encoding;
+            var encoding = output.Writer.IsStandardOut() || output.Writer.IsStandardError()
+                ? System.Console.OutputEncoding : output.Writer.Encoding;
 
             // Get the color system
             var colorSystem = settings.ColorSystem == ColorSystemSupport.Detect
@@ -42,11 +47,9 @@ namespace Spectre.Console
                 interactive = Environment.UserInteractive;
             }
 
-            var profile = new Profile(buffer, encoding)
-            {
-                ColorSystem = colorSystem,
-            };
+            var profile = new Profile(output, encoding);
 
+            profile.Capabilities.ColorSystem = colorSystem;
             profile.Capabilities.Ansi = supportsAnsi;
             profile.Capabilities.Links = supportsAnsi && !legacyConsole;
             profile.Capabilities.Legacy = legacyConsole;
diff --git a/src/Spectre.Console/AnsiConsoleOutput.cs b/src/Spectre.Console/AnsiConsoleOutput.cs
new file mode 100644
index 0000000..907def7
--- /dev/null
+++ b/src/Spectre.Console/AnsiConsoleOutput.cs
@@ -0,0 +1,58 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace Spectre.Console
+{
+    /// <summary>
+    /// Represents console output.
+    /// </summary>
+    public sealed class AnsiConsoleOutput : IAnsiConsoleOutput
+    {
+        /// <inheritdoc/>
+        public TextWriter Writer { get; }
+
+        /// <inheritdoc/>
+        public bool IsTerminal
+        {
+            get
+            {
+                if (Writer.IsStandardOut())
+                {
+                    return !System.Console.IsOutputRedirected;
+                }
+
+                if (Writer.IsStandardError())
+                {
+                    return !System.Console.IsErrorRedirected;
+                }
+
+                return false;
+            }
+        }
+
+        /// <inheritdoc/>
+        public int Width => ConsoleHelper.GetSafeWidth(Constants.DefaultTerminalWidth);
+
+        /// <inheritdoc/>
+        public int Height => ConsoleHelper.GetSafeHeight(Constants.DefaultTerminalWidth);
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="AnsiConsoleOutput"/> class.
+        /// </summary>
+        /// <param name="writer">The output writer.</param>
+        public AnsiConsoleOutput(TextWriter writer)
+        {
+            Writer = writer ?? throw new ArgumentNullException(nameof(writer));
+        }
+
+        /// <inheritdoc/>
+        public void SetEncoding(Encoding encoding)
+        {
+            if (Writer.IsStandardOut() || Writer.IsStandardError())
+            {
+                System.Console.OutputEncoding = encoding;
+            }
+        }
+    }
+}
diff --git a/src/Spectre.Console/AnsiConsoleSettings.cs b/src/Spectre.Console/AnsiConsoleSettings.cs
index 6fee233..7038eb2 100644
--- a/src/Spectre.Console/AnsiConsoleSettings.cs
+++ b/src/Spectre.Console/AnsiConsoleSettings.cs
@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using System.IO;
 
 namespace Spectre.Console
 {
@@ -22,7 +21,7 @@ namespace Spectre.Console
         /// <summary>
         /// Gets or sets the out buffer.
         /// </summary>
-        public TextWriter? Out { get; set; }
+        public IAnsiConsoleOutput? Out { get; set; }
 
         /// <summary>
         /// Gets or sets a value indicating whether or not the
diff --git a/src/Spectre.Console/Capabilities.cs b/src/Spectre.Console/Capabilities.cs
index 98effac..2c367b8 100644
--- a/src/Spectre.Console/Capabilities.cs
+++ b/src/Spectre.Console/Capabilities.cs
@@ -7,7 +7,12 @@ namespace Spectre.Console
     /// </summary>
     public sealed class Capabilities : IReadOnlyCapabilities
     {
-        private readonly Profile _profile;
+        private readonly IAnsiConsoleOutput _out;
+
+        /// <summary>
+        /// Gets or sets the color system.
+        /// </summary>
+        public ColorSystem ColorSystem { get; set; }
 
         /// <summary>
         /// Gets or sets a value indicating whether or not
@@ -33,26 +38,10 @@ namespace Spectre.Console
 
         /// <summary>
         /// Gets a value indicating whether or not
-        /// console output has been redirected.
+        /// the output is a terminal.
         /// </summary>
-        public bool Tty
-        {
-            get
-            {
-                if (_profile.Out.IsStandardOut())
-                {
-                    return System.Console.IsOutputRedirected;
-                }
-
-                if (_profile.Out.IsStandardError())
-                {
-                    return System.Console.IsErrorRedirected;
-                }
-
-                // Not stdout, so must be a TTY.
-                return true;
-            }
-        }
+        [Obsolete("Use Profile.Out.IsTerminal instead")]
+        public bool IsTerminal => _out.IsTerminal;
 
         /// <summary>
         /// Gets or sets a value indicating whether
@@ -67,11 +56,12 @@ namespace Spectre.Console
         public bool Unicode { get; set; }
 
         /// <summary>
-        /// Initializes a new instance of the <see cref="Capabilities"/> class.
+        /// Initializes a new instance of the
+        /// <see cref="Capabilities"/> class.
         /// </summary>
-        internal Capabilities(Profile profile)
+        internal Capabilities(IAnsiConsoleOutput @out)
         {
-            _profile = profile ?? throw new ArgumentNullException(nameof(profile));
+            _out = @out ?? throw new ArgumentNullException(nameof(@out));
         }
     }
 }
diff --git a/src/Spectre.Console/Extensions/RenderableExtensions.cs b/src/Spectre.Console/Extensions/RenderableExtensions.cs
index c7ca2ef..acc2fa5 100644
--- a/src/Spectre.Console/Extensions/RenderableExtensions.cs
+++ b/src/Spectre.Console/Extensions/RenderableExtensions.cs
@@ -27,7 +27,7 @@ namespace Spectre.Console
                 throw new ArgumentNullException(nameof(renderable));
             }
 
-            var context = new RenderContext(console.Profile.ColorSystem, console.Profile.Capabilities);
+            var context = new RenderContext(console.Profile.Capabilities);
             var renderables = console.Pipeline.Process(context, new[] { renderable });
 
             return GetSegments(console, context, renderables);
diff --git a/src/Spectre.Console/IAnsiConsoleOutput.cs b/src/Spectre.Console/IAnsiConsoleOutput.cs
new file mode 100644
index 0000000..395a469
--- /dev/null
+++ b/src/Spectre.Console/IAnsiConsoleOutput.cs
@@ -0,0 +1,37 @@
+using System.IO;
+using System.Text;
+
+namespace Spectre.Console
+{
+    /// <summary>
+    /// Represents console output.
+    /// </summary>
+    public interface IAnsiConsoleOutput
+    {
+        /// <summary>
+        /// Gets the <see cref="TextWriter"/> used to write to the output.
+        /// </summary>
+        TextWriter Writer { get; }
+
+        /// <summary>
+        /// Gets a value indicating whether or not the output is a terminal.
+        /// </summary>
+        bool IsTerminal { get; }
+
+        /// <summary>
+        /// Gets the output width.
+        /// </summary>
+        int Width { get; }
+
+        /// <summary>
+        /// Gets the output height.
+        /// </summary>
+        int Height { get; }
+
+        /// <summary>
+        /// Sets the output encoding.
+        /// </summary>
+        /// <param name="encoding">The encoding.</param>
+        void SetEncoding(Encoding encoding);
+    }
+}
diff --git a/src/Spectre.Console/IReadOnlyCapabilities.cs b/src/Spectre.Console/IReadOnlyCapabilities.cs
index e624a01..340f338 100644
--- a/src/Spectre.Console/IReadOnlyCapabilities.cs
+++ b/src/Spectre.Console/IReadOnlyCapabilities.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Spectre.Console
 {
     /// <summary>
@@ -5,6 +7,11 @@ namespace Spectre.Console
     /// </summary>
     public interface IReadOnlyCapabilities
     {
+        /// <summary>
+        /// Gets the color system.
+        /// </summary>
+        ColorSystem ColorSystem { get; }
+
         /// <summary>
         /// Gets a value indicating whether or not
         /// the console supports Ansi.
@@ -31,7 +38,8 @@ namespace Spectre.Console
         /// Gets a value indicating whether or not
         /// console output has been redirected.
         /// </summary>
-        bool Tty { get; }
+        [Obsolete("Use Profile.Out.IsTerminal instead")]
+        bool IsTerminal { get; }
 
         /// <summary>
         /// Gets a value indicating whether
diff --git a/src/Spectre.Console/Internal/Backends/Ansi/AnsiBuilder.cs b/src/Spectre.Console/Internal/Backends/Ansi/AnsiBuilder.cs
index 2f73547..de64b7b 100644
--- a/src/Spectre.Console/Internal/Backends/Ansi/AnsiBuilder.cs
+++ b/src/Spectre.Console/Internal/Backends/Ansi/AnsiBuilder.cs
@@ -29,7 +29,7 @@ namespace Spectre.Console
             {
                 codes = codes.Concat(
                     AnsiColorBuilder.GetAnsiCodes(
-                        _profile.ColorSystem,
+                        _profile.Capabilities.ColorSystem,
                         style.Foreground,
                         true));
             }
@@ -39,7 +39,7 @@ namespace Spectre.Console
             {
                 codes = codes.Concat(
                     AnsiColorBuilder.GetAnsiCodes(
-                        _profile.ColorSystem,
+                        _profile.Capabilities.ColorSystem,
                         style.Background,
                         false));
             }
diff --git a/src/Spectre.Console/Internal/Backends/Ansi/AnsiConsoleBackend.cs b/src/Spectre.Console/Internal/Backends/Ansi/AnsiConsoleBackend.cs
index 884e498..72addb5 100644
--- a/src/Spectre.Console/Internal/Backends/Ansi/AnsiConsoleBackend.cs
+++ b/src/Spectre.Console/Internal/Backends/Ansi/AnsiConsoleBackend.cs
@@ -59,8 +59,8 @@ namespace Spectre.Console
 
             if (builder.Length > 0)
             {
-                _console.Profile.Out.Write(builder.ToString());
-                _console.Profile.Out.Flush();
+                _console.Profile.Out.Writer.Write(builder.ToString());
+                _console.Profile.Out.Writer.Flush();
             }
         }
     }
diff --git a/src/Spectre.Console/Internal/Backends/Legacy/LegacyConsoleBackend.cs b/src/Spectre.Console/Internal/Backends/Legacy/LegacyConsoleBackend.cs
index 94de396..80d4b6c 100644
--- a/src/Spectre.Console/Internal/Backends/Legacy/LegacyConsoleBackend.cs
+++ b/src/Spectre.Console/Internal/Backends/Legacy/LegacyConsoleBackend.cs
@@ -44,7 +44,7 @@ namespace Spectre.Console
                     SetStyle(segment.Style);
                 }
 
-                _console.Profile.Out.Write(segment.Text.NormalizeNewLines(native: true));
+                _console.Profile.Out.Writer.Write(segment.Text.NormalizeNewLines(native: true));
             }
         }
 
@@ -55,13 +55,13 @@ namespace Spectre.Console
             System.Console.ResetColor();
 
             var background = Color.ToConsoleColor(style.Background);
-            if (_console.Profile.ColorSystem != ColorSystem.NoColors && (int)background != -1)
+            if (_console.Profile.Capabilities.ColorSystem != ColorSystem.NoColors && (int)background != -1)
             {
                 System.Console.BackgroundColor = background;
             }
 
             var foreground = Color.ToConsoleColor(style.Foreground);
-            if (_console.Profile.ColorSystem != ColorSystem.NoColors && (int)foreground != -1)
+            if (_console.Profile.Capabilities.ColorSystem != ColorSystem.NoColors && (int)foreground != -1)
             {
                 System.Console.ForegroundColor = foreground;
             }
diff --git a/src/Spectre.Console/Internal/DefaultInput.cs b/src/Spectre.Console/Internal/DefaultInput.cs
index 844db0f..0165c52 100644
--- a/src/Spectre.Console/Internal/DefaultInput.cs
+++ b/src/Spectre.Console/Internal/DefaultInput.cs
@@ -13,11 +13,6 @@ namespace Spectre.Console
 
         public ConsoleKeyInfo ReadKey(bool intercept)
         {
-            if (_profile.Capabilities.Tty)
-            {
-                throw new InvalidOperationException("Cannot read input from a TTY console.");
-            }
-
             if (!_profile.Capabilities.Interactive)
             {
                 throw new InvalidOperationException("Failed to read input in non-interactive mode.");
diff --git a/src/Spectre.Console/Internal/Text/Encoding/EncoderCapabilities.cs b/src/Spectre.Console/Internal/Text/Encoding/EncoderCapabilities.cs
new file mode 100644
index 0000000..88d5fae
--- /dev/null
+++ b/src/Spectre.Console/Internal/Text/Encoding/EncoderCapabilities.cs
@@ -0,0 +1,19 @@
+namespace Spectre.Console.Internal
+{
+    internal sealed class EncoderCapabilities : IReadOnlyCapabilities
+    {
+        public ColorSystem ColorSystem { get; }
+
+        public bool Ansi => false;
+        public bool Links => false;
+        public bool Legacy => false;
+        public bool IsTerminal => false;
+        public bool Interactive => false;
+        public bool Unicode => true;
+
+        public EncoderCapabilities(ColorSystem colors)
+        {
+            ColorSystem = colors;
+        }
+    }
+}
diff --git a/src/Spectre.Console/Internal/Text/Encoding/HtmlEncoder.cs b/src/Spectre.Console/Internal/Text/Encoding/HtmlEncoder.cs
index f3cc883..ac12c1a 100644
--- a/src/Spectre.Console/Internal/Text/Encoding/HtmlEncoder.cs
+++ b/src/Spectre.Console/Internal/Text/Encoding/HtmlEncoder.cs
@@ -9,7 +9,7 @@ namespace Spectre.Console.Internal
     {
         public string Encode(IAnsiConsole console, IEnumerable<IRenderable> renderables)
         {
-            var context = new RenderContext(ColorSystem.TrueColor, EncoderCapabilities.Default);
+            var context = new RenderContext(new EncoderCapabilities(ColorSystem.TrueColor));
             var builder = new StringBuilder();
 
             builder.Append("<pre style=\"font-size:90%;font-family:consolas,'Courier New',monospace\">\n");
diff --git a/src/Spectre.Console/Internal/Text/Encoding/TextEncoder.cs b/src/Spectre.Console/Internal/Text/Encoding/TextEncoder.cs
index 954ead2..92597bf 100644
--- a/src/Spectre.Console/Internal/Text/Encoding/TextEncoder.cs
+++ b/src/Spectre.Console/Internal/Text/Encoding/TextEncoder.cs
@@ -4,23 +4,11 @@ using Spectre.Console.Rendering;
 
 namespace Spectre.Console.Internal
 {
-    internal sealed class EncoderCapabilities : IReadOnlyCapabilities
-    {
-        public bool Ansi => false;
-        public bool Links => false;
-        public bool Legacy => false;
-        public bool Tty => false;
-        public bool Interactive => false;
-        public bool Unicode => true;
-
-        public static EncoderCapabilities Default { get; } = new EncoderCapabilities();
-    }
-
     internal sealed class TextEncoder : IAnsiConsoleEncoder
     {
         public string Encode(IAnsiConsole console, IEnumerable<IRenderable> renderables)
         {
-            var context = new RenderContext(ColorSystem.TrueColor, EncoderCapabilities.Default);
+            var context = new RenderContext(new EncoderCapabilities(ColorSystem.TrueColor));
             var builder = new StringBuilder();
 
             foreach (var renderable in renderables)
diff --git a/src/Spectre.Console/Profile.cs b/src/Spectre.Console/Profile.cs
index 0ef06ca..3a82c2b 100644
--- a/src/Spectre.Console/Profile.cs
+++ b/src/Spectre.Console/Profile.cs
@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.IO;
 using System.Text;
 
 namespace Spectre.Console
@@ -13,7 +12,7 @@ namespace Spectre.Console
         private readonly HashSet<string> _enrichers;
         private static readonly string[] _defaultEnricher = new[] { "Default" };
 
-        private TextWriter _out;
+        private IAnsiConsoleOutput _out;
         private Encoding _encoding;
         private Capabilities _capabilities;
         private int? _width;
@@ -38,15 +37,15 @@ namespace Spectre.Console
         /// <summary>
         /// Gets or sets the out buffer.
         /// </summary>
-        public TextWriter Out
+        public IAnsiConsoleOutput Out
         {
             get => _out;
             set
             {
                 _out = value ?? throw new InvalidOperationException("Output buffer cannot be null");
 
-                // Reset the width and height if not a TTY.
-                if (!Capabilities.Tty)
+                // Reset the width and height if this is a terminal.
+                if (value.IsTerminal)
                 {
                     _width = null;
                     _height = null;
@@ -67,12 +66,7 @@ namespace Spectre.Console
                     throw new InvalidOperationException("Encoding cannot be null");
                 }
 
-                // Need to update the output encoding for stdout?
-                if (_out.IsStandardOut() || _out.IsStandardError())
-                {
-                    System.Console.OutputEncoding = value;
-                }
-
+                _out.SetEncoding(value);
                 _encoding = value;
             }
         }
@@ -82,10 +76,10 @@ namespace Spectre.Console
         /// </summary>
         public int Width
         {
-            get => GetWidth();
+            get => _width ?? _out.Width;
             set
             {
-                if (_width <= 0)
+                if (value <= 0)
                 {
                     throw new InvalidOperationException("Console width must be greater than zero");
                 }
@@ -99,10 +93,10 @@ namespace Spectre.Console
         /// </summary>
         public int Height
         {
-            get => GetHeight();
+            get => _height ?? _out.Height;
             set
             {
-                if (_height <= 0)
+                if (value <= 0)
                 {
                     throw new InvalidOperationException("Console height must be greater than zero");
                 }
@@ -111,11 +105,6 @@ namespace Spectre.Console
             }
         }
 
-        /// <summary>
-        /// Gets or sets the color system.
-        /// </summary>
-        public ColorSystem ColorSystem { get; set; }
-
         /// <summary>
         /// Gets or sets the capabilities of the profile.
         /// </summary>
@@ -133,12 +122,12 @@ namespace Spectre.Console
         /// </summary>
         /// <param name="out">The output buffer.</param>
         /// <param name="encoding">The output encoding.</param>
-        public Profile(TextWriter @out, Encoding encoding)
+        public Profile(IAnsiConsoleOutput @out, Encoding encoding)
         {
             _enrichers = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
             _out = @out ?? throw new ArgumentNullException(nameof(@out));
             _encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
-            _capabilities = new Capabilities(this);
+            _capabilities = new Capabilities(_out);
         }
 
         /// <summary>
@@ -149,7 +138,7 @@ namespace Spectre.Console
         /// <returns><c>true</c> if the color system is supported, otherwise <c>false</c>.</returns>
         public bool Supports(ColorSystem colorSystem)
         {
-            return (int)colorSystem <= (int)ColorSystem;
+            return (int)colorSystem <= (int)Capabilities.ColorSystem;
         }
 
         internal void AddEnricher(string name)
@@ -161,35 +150,5 @@ namespace Spectre.Console
 
             _enrichers.Add(name);
         }
-
-        private int GetWidth()
-        {
-            if (_width != null)
-            {
-                return _width.Value;
-            }
-
-            if (!Capabilities.Tty)
-            {
-                return ConsoleHelper.GetSafeWidth(Constants.DefaultTerminalWidth);
-            }
-
-            return Constants.DefaultTerminalWidth;
-        }
-
-        private int GetHeight()
-        {
-            if (_height != null)
-            {
-                return _height.Value;
-            }
-
-            if (!Capabilities.Tty)
-            {
-                return ConsoleHelper.GetSafeHeight(Constants.DefaultTerminalHeight);
-            }
-
-            return Constants.DefaultTerminalHeight;
-        }
     }
 }
diff --git a/src/Spectre.Console/Rendering/RenderContext.cs b/src/Spectre.Console/Rendering/RenderContext.cs
index fd10a79..b4ffff1 100644
--- a/src/Spectre.Console/Rendering/RenderContext.cs
+++ b/src/Spectre.Console/Rendering/RenderContext.cs
@@ -12,7 +12,7 @@ namespace Spectre.Console.Rendering
         /// <summary>
         /// Gets the current color system.
         /// </summary>
-        public ColorSystem ColorSystem { get; }
+        public ColorSystem ColorSystem => _capabilities.ColorSystem;
 
         /// <summary>
         /// Gets a value indicating whether or not unicode is supported.
@@ -33,19 +33,17 @@ namespace Spectre.Console.Rendering
         /// <summary>
         /// Initializes a new instance of the <see cref="RenderContext"/> class.
         /// </summary>
-        /// <param name="colorSystem">The color system.</param>
         /// <param name="capabilities">The capabilities.</param>
         /// <param name="justification">The justification.</param>
-        public RenderContext(ColorSystem colorSystem, IReadOnlyCapabilities capabilities, Justify? justification = null)
-            : this(colorSystem, capabilities, justification, false)
+        public RenderContext(IReadOnlyCapabilities capabilities, Justify? justification = null)
+            : this(capabilities, justification, false)
         {
         }
 
-        private RenderContext(ColorSystem colorSystem, IReadOnlyCapabilities capabilities, Justify? justification = null, bool singleLine = false)
+        private RenderContext(IReadOnlyCapabilities capabilities, Justify? justification = null, bool singleLine = false)
         {
             _capabilities = capabilities ?? throw new ArgumentNullException(nameof(capabilities));
 
-            ColorSystem = colorSystem;
             Justification = justification;
             SingleLine = singleLine;
         }
@@ -57,7 +55,7 @@ namespace Spectre.Console.Rendering
         /// <returns>A new <see cref="RenderContext"/> instance.</returns>
         public RenderContext WithJustification(Justify? justification)
         {
-            return new RenderContext(ColorSystem, _capabilities, justification, SingleLine);
+            return new RenderContext(_capabilities, justification, SingleLine);
         }
 
         /// <summary>
@@ -72,7 +70,7 @@ namespace Spectre.Console.Rendering
         /// <returns>A new <see cref="RenderContext"/> instance.</returns>
         internal RenderContext WithSingleLine()
         {
-            return new RenderContext(ColorSystem, _capabilities, Justification, true);
+            return new RenderContext(_capabilities, Justification, true);
         }
     }
 }
diff --git a/src/Spectre.Console/Widgets/Progress/Renderers/DefaultProgressRenderer.cs b/src/Spectre.Console/Widgets/Progress/Renderers/DefaultProgressRenderer.cs
index 7122fa7..c764531 100644
--- a/src/Spectre.Console/Widgets/Progress/Renderers/DefaultProgressRenderer.cs
+++ b/src/Spectre.Console/Widgets/Progress/Renderers/DefaultProgressRenderer.cs
@@ -62,7 +62,7 @@ namespace Spectre.Console
                     _stopwatch.Start();
                 }
 
-                var renderContext = new RenderContext(_console.Profile.ColorSystem, _console.Profile.Capabilities);
+                var renderContext = new RenderContext(_console.Profile.Capabilities);
 
                 var delta = _stopwatch.Elapsed - _lastUpdate;
                 _lastUpdate = _stopwatch.Elapsed;