/* The app's whole appearance (#110). Colours are the mockup's dark palette, quoted as it states
   them (.discovery/design/Ledger.dc.html); the layout is the tab-bar variant, which was chosen
   by deploying both to a real iPhone and installing them rather than in review. */

:root {
  color-scheme: dark;
  --bg: oklch(0.158 0.012 255);
  --bg2: oklch(0.196 0.013 255);
  --line: oklch(0.315 0.015 255);
  --t1: oklch(0.965 0.005 255);
  --t2: oklch(0.74 0.011 255);
  --t3: oklch(0.575 0.013 255);
  --ink: oklch(0.9 0.01 255);

  /* The three signals, each meaning one thing (#26). Blue and amber share a lightness on
     purpose. --alert is off that line deliberately: a red as light as the other two inherits
     the constructed non-alarming quality that disqualified amber for failure signalling. Its
     first use is the rail dot (#111); Broken rows are the second (#114). */
  --pos: oklch(0.74 0.135 225);
  --neg: oklch(0.74 0.135 55);
  --alert: oklch(0.62 0.19 27);

  /* The chart ramp, which Accounts over time and Today's mix both fill from. A hue ramp at a
     chroma under the three signals', hue stepping 108° a band and lightness alternating, so
     neighbours of nearly one size part on hue first; it passed the dataviz validator's
     adjacent-pair checks against --bg2 on 2026-09-14, protan at the 6.7 floor. The rest band is
     a grey off every token. */
  --ramp-0: oklch(0.56 0.11 245);
  --ramp-1: oklch(0.66 0.11 353);
  --ramp-2: oklch(0.58 0.11 101);
  --ramp-3: oklch(0.64 0.11 209);
  --ramp-4: oklch(0.56 0.11 317);
  --ramp-5: oklch(0.66 0.11 65);
  --ramp-6: oklch(0.58 0.11 173);
  --ramp-7: oklch(0.64 0.11 281);
  --ramp-8: oklch(0.56 0.11 29);
  --ramp-9: oklch(0.66 0.11 137);
  --ramp-rest: oklch(0.42 0.012 255);

  /* The chart's three measurements, held here because the frame reserves its room before any of
     it exists (#203) and a second copy of a number is one that drifts. */
  --plot-height: 10.625rem;
  --chart-gap: 0.875rem;
  /* The design's 44px floor for anything pressed on the phone. */
  --chip-height: 2.75rem;
}

* {
  box-sizing: border-box;
}

html {
  background: var(--bg);
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--t1);
  font:
    400 1rem/1.5 ui-sans-serif,
    -apple-system,
    system-ui,
    sans-serif;
}

#app {
  display: flex;
  flex-direction: column;
  /* Capped, not floored. Under `min-height` a long list grew the column past the viewport and
     carried the tab bar off the bottom of the phone; the panel scrolls inside instead, which is
     the mockup's own phone frame (.discovery/design/Ledger.dc.html). The cap is measured rather
     than taken from `dvh`, which iOS reports short on some launches (app/viewport.ts). */
  height: var(--app-height, 100dvh);
  overflow: hidden;
  /* The installed app opens under the status bar, because the tile is black-translucent and
     the viewport is `viewport-fit=cover` (index.html). Held here rather than on the panels, so
     that scrolled content stays out of it too, and so the rail's own head clears the clock on
     an iPad. The bottom inset is the tab bar's, below. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right) 0 env(safe-area-inset-left);
}

button {
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
}

/* Every signed figure in the app, coloured once (#207). `app/moved-figure.ts` sets the attribute.

   The `:root` prefix is weight rather than reach. The rule has to outrank the quiet colour a
   screen sets on its own class, and still lose to the hero's pipeline-down override. */
:root [data-sign='up'] {
  color: var(--pos);
}

:root [data-sign='down'] {
  color: var(--neg);
}

/* A band's step on the ramp. Accounts over time and Today's mix both fill from `--band-fill`. */
[data-band='0'] {
  --band-fill: var(--ramp-0);
}

[data-band='1'] {
  --band-fill: var(--ramp-1);
}

[data-band='2'] {
  --band-fill: var(--ramp-2);
}

[data-band='3'] {
  --band-fill: var(--ramp-3);
}

[data-band='4'] {
  --band-fill: var(--ramp-4);
}

[data-band='5'] {
  --band-fill: var(--ramp-5);
}

[data-band='6'] {
  --band-fill: var(--ramp-6);
}

[data-band='7'] {
  --band-fill: var(--ramp-7);
}

[data-band='8'] {
  --band-fill: var(--ramp-8);
}

[data-band='9'] {
  --band-fill: var(--ramp-9);
}

[data-band='rest'] {
  --band-fill: var(--ramp-rest);
}

/* The signed-out screen: one button, centred on the app background, and a line that stays
   empty unless the redirect refuses to leave. */

.signin {
  margin: auto;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  width: min(20rem, 100% - 2.5rem);
}

.signin-button {
  min-height: 2.75rem;
  padding: 0.75rem 1.5rem;
  border-radius: 0.5rem;
  background: var(--ink);
  color: var(--bg);
  font-size: 0.9375rem;
  font-weight: 500;
}

.signin-failure {
  margin: 0;
  color: var(--t2);
  font-size: 0.8125rem;
  text-align: center;
  word-break: break-word;
}

/* The frame (#203). What the document holds from first paint until one of the two screens above
   replaces it. Its nav carries the shell's own classes and takes the shell's own rules, so
   everything below this block is what draws it; the two blocks are all it adds. */

.frame-block {
  border-radius: 0.625rem;
  background: var(--bg2);
}

/* The hero's four lines at the height they render at, badge included: 138px measured, the same at
   every width because none of those type sizes moves. A phone narrow enough to wrap the two
   moves runs a line taller, which the block does not chase. */
.frame-hero {
  height: 8.625rem;
}

/* The chips, the gap under them and the plot, each off the rule that sets it, so the frame and
   the chart stay in step across the breakpoint. The composition list is left out: it is as long
   as the account holder has Categories, and a block promising rows that may not come is worse
   than one that stops. */
.frame-chart {
  margin-top: 1.75rem;
  height: calc(var(--chip-height) + var(--chart-gap) + var(--plot-height));
}

/* The shell. One nav, laid out along the bottom on the phone and down the side past the
   breakpoint, the rail the design specifies, from the same markup. */

.shell {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}

/* Both flex items at `min-width: 0`: at the default `auto` a wide child, the holdings table
   (#318), stretches the page rather than scrolling inside the panel. */
.panels {
  display: flex;
  flex: 1;
  min-height: 0;
  min-width: 0;
}

.dest {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  padding: 1.5rem 1rem;
}

.dest[hidden] {
  display: none;
}

.dest-title {
  margin: 0 0 0.5rem;
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.reading {
  margin: 0 0 0.5rem;
  max-width: 34rem;
  color: var(--t1);
  font-size: 0.875rem;
}

/* Bordered rather than filled (#122): it is the only control on a destination of readings, and
   a filled button there reads as the thing to press. */

.signout {
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
}

.signout-button {
  min-height: 2.75rem;
  padding: 0 1.25rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg2);
  font-size: 0.9375rem;
}

.signout-failure {
  margin: 0.5rem 0 0;
  max-width: 34rem;
  color: var(--t2);
  font-size: 0.8125rem;
  word-break: break-word;
}

.signout-failure:empty {
  display: none;
}

/* Off the screen rather than out of the DOM, so a mark that is only a colour still has a name
   for a screen reader to read. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The hero (#111). The label is the sentence the figure completes, so it leads and sits quiet;
   the figure is the reason the app was opened.

   Nothing on the lines transitions or animates, and `test/browser/refresh.test.ts` fails if one
   does: when a Provisional lands, the money did not move, our knowledge of it did (#112). The
   Refresh button is the one exception, below. */

.hero {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}

.hero-lines {
  flex: 1;
  min-width: 0;
}

/* The Refresh, beside the stamp it acts on (#112). */
.hero-refresh {
  flex: none;
  width: 2.375rem;
  height: 2.375rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  color: var(--t2);
  font-size: 1rem;
}

/* Block, because an inline box does not transform. */
.hero-refresh-glyph {
  display: block;
}

/* Filled and spinning while the callable is in flight (#347). The spin says the press was taken
   and nothing about the money, which is why the hero's no-animation rule stops at this button.
   The stamp moving is the completion signal; the button gives none of its own. */
.hero-refresh[disabled] {
  border-color: var(--ink);
  background: var(--bg2);
}

.hero-refresh[disabled] .hero-refresh-glyph {
  animation: refresh-spin 0.9s linear infinite;
}

@keyframes refresh-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-refresh[disabled] .hero-refresh-glyph {
    animation: none;
  }
}

/* Alert for the reason `.add-status[data-state='failed']` is: a failure the account holder has
   to act on. */
.hero-refresh-status {
  margin: 0.5rem 0 0;
  color: var(--alert);
  font-size: 0.75rem;
  text-align: right;
  word-break: break-word;
}

.hero-refresh-status:empty {
  display: none;
}

/* At reading size and in the quiet colour, because nothing is wrong: no figure was ever read. */
.hero-empty {
  margin: 0.5rem 0 0;
  max-width: 22rem;
  color: var(--t2);
  font-size: 0.9375rem;
}

.hero-label {
  margin: 0;
  color: var(--t2);
  font-size: 0.875rem;
  font-weight: 400;
}

.hero-figure {
  margin: 0.25rem 0 0;
  font-size: 2.75rem;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.03em;
  /* So a figure that changes on a Provisional does not shuffle the digits beside it. */
  font-variant-numeric: tabular-nums;
}

/* Pipeline-down, one of its four channels (#26). The weight drops and the colour goes with it:
   a weight change alone is not visible at arm's length on a figure this size. */
.hero-figure[data-weight='reduced'] {
  color: var(--t3);
  font-weight: 400;
}

/* The two moves (#206), in the figure's block rather than on the meta line: facts about the
   money, not about the read. They wrap to their own lines on a narrow phone rather than
   shrinking, and the row gap is what keeps that readable. */
.hero-moves {
  margin: 0.625rem 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.375rem 1.25rem;
  font-size: 0.875rem;
}

.hero-move {
  display: flex;
  align-items: baseline;
  gap: 0.4375rem;
  color: var(--t2);
}

.hero-move-figure,
.hero-move-percent {
  font-variant-numeric: tabular-nums;
}

/* Pipeline-down takes the colour off both moves. A saturated --pos under a figure dropped to
   --t3 would be the loudest thing in the hero on the day it should be the quietest. */
.hero-moves[data-weight='reduced'] .hero-move {
  color: var(--t3);
}

.hero-move-label {
  color: var(--t3);
  font-size: 0.8125rem;
}

.hero-meta {
  margin: 0.625rem 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  color: var(--t3);
  font-size: 0.8125rem;
}

/* Informational, and deliberately not a signal colour. A Carry is the frequent case, and the
   count is there so the stamp cannot flatter the figure. */
.hero-badge {
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.0625rem 0.5rem;
  color: var(--t2);
}

/* The second of pipeline-down's four channels, carrying the same signal as the rail dot and
   saying in words what the dot says in colour. */
.hero-age {
  border: 1px solid var(--alert);
  border-radius: 999px;
  padding: 0.0625rem 0.5rem;
  color: var(--alert);
}

/* The chart (#113). */

.chart {
  margin-top: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: var(--chart-gap);
}

.range-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}

.range-chip {
  min-height: var(--chip-height);
  padding: 0 0.9375rem;
  border: 1px solid var(--line);
  border-radius: 0.625rem;
  color: var(--t3);
  font-size: 0.8125rem;
}

.range-chip[aria-pressed='true'] {
  background: var(--bg2);
  color: var(--t1);
}

/* The plot's room, held while the body is empty. The chart is built from the hero's read and
   fills from its own one (#113), so this is the gap between the frame coming down and the plot
   arriving, and without it the chips sit on nothing and the plot drops in under them. */
.chart-body {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  min-height: var(--plot-height);
}

/* Stretched to the column, so the stroke widths below are held by `vector-effect` rather than
   by the viewBox. */
.chart-plot {
  display: block;
  width: 100%;
  height: var(--plot-height);
}

/* Every scrubbable plot, whichever screen it is on (app/scrub.ts adds the class). `pan-y` keeps
   the plot scrollable, since it is where a thumb lands near the top of both screens. What a
   claimed scrub does about that is in app/scrub.ts. */
.scrub-plot {
  touch-action: pan-y;
}

/* The UA draws its own ring on a focusable SVG the moment a drag starts, which boxes the plot
   for the whole gesture. The keyboard ring is the one worth having, so it is the only one. */
.scrub-plot:focus {
  outline: none;
}

.scrub-plot:focus-visible {
  outline: 2px solid var(--t2);
  outline-offset: 2px;
}

.scrub-rule {
  stroke: var(--t2);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.scrub-dot {
  fill: var(--ink);
}

/* Fixed under the plot, never floating: a tooltip under a fingertip is unreadable. It holds a
   line whether or not anything is being scrubbed, so the chart below it never shifts. */
.chart-readout {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.625rem;
  min-height: 1.25rem;
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

.readout-date {
  color: var(--t2);
}

.readout-figure {
  color: var(--t1);
}

.readout-hint {
  color: var(--t3);
}

/* A series named beside its figure (app/readout.ts, namedLines), the pair held together so a
   wrap on the phone never parts them. */
.readout-line {
  white-space: nowrap;
}

.readout-name {
  color: var(--t2);
}

.chart-line {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

/* The week's own min and max. Quiet enough that the close still reads as the figure. */
.chart-band {
  fill: var(--t2);
  fill-opacity: 0.22;
}

/* The figure the range opened on, so the height of the line above it is the change (#26). */
.chart-base {
  stroke: var(--t3);
  stroke-width: 1;
  stroke-dasharray: 2 4;
  vector-effect: non-scaling-stroke;
}

.chart-hatch-line {
  stroke: var(--t3);
  stroke-width: 1;
}

.chart-hatch {
  opacity: 0.55;
}

.chart-scale,
.chart-empty {
  margin: 0;
  color: var(--t3);
  font-size: 0.75rem;
}

.chart-scale {
  display: flex;
  gap: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.chart-empty {
  color: var(--t2);
  font-size: 0.875rem;
}

/* The list states changes, so it says so. Without the heading a row reads as a balance, and a
   Category that closed reads as a Category worth minus forty thousand dollars. */
.composition-title {
  margin: 0.75rem 0 0;
  color: var(--t3);
  font-size: 0.75rem;
  font-weight: 400;
}

.composition {
  margin: 0.25rem 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}

.composition-row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.875rem;
}

.composition-label {
  color: var(--t2);
}

.composition-figure {
  font-variant-numeric: tabular-nums;
}

/* The Portfolio destination (#314). The chips sit under the hero at the chart's distance, so the
   two screens' first rows land at one height. */
.hero + .range-chips {
  margin-top: 1.75rem;
}

/* The whole of the screen below the chips before the first Finalize: one line, at the measure
   the other empty sentences use, and no section to apologise for itself. */
.portfolio-first {
  margin: 1.75rem 0 0;
  max-width: 34rem;
  color: var(--t2);
  font-size: 0.875rem;
  line-height: 1.5;
}

/* The Return index chart (#315). The plot, the rule at 100, the readout and the scale row are
   the chart's rules above; what is its own is the lines and the toggles. */
.index-chart {
  margin-top: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}

/* The heading's rule is with the other block headings, below; only the gap to the plot is here. */
.index-chart > .index-title {
  margin-bottom: 0.125rem;
}

/* One rule per line, on the plot and on its toggle's swatch alike. Your line is the chart's own
   weight; every Benchmark is quieter and dashed, no two the same, so a line is named by its dash
   and never by a colour the palette keeps for a signal (#26). The dashes are screen pixels under
   `non-scaling-stroke`, and the round caps eat 0.6px of each gap, so no gap is under 4. */
.index-line {
  fill: none;
  stroke: var(--t2);
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.index-line[data-line='index'] {
  stroke: var(--ink);
  stroke-width: 1.6;
}

.index-line[data-line='sp500'] {
  stroke-dasharray: 9 4;
}

.index-line[data-line='us-stock'] {
  stroke-dasharray: 4 4;
}

.index-line[data-line='foreign-stock'] {
  stroke-dasharray: 9 4 2 4;
}

.index-line[data-line='us-bond'] {
  stroke-dasharray: 1 4;
}

.index-line[data-line='blend-90-10'] {
  stroke-dasharray: 15 4;
}

.index-line[data-line='blend-80-20'] {
  stroke-dasharray: 15 4 4 4;
}

.index-line[data-line='blend-60-40'] {
  stroke-dasharray: 15 4 1 4 1 4;
}

/* The toggles wrap under the plot, one per line at the canvas's 36px rather than the range
   chips' 44px: eight of them at the taller height are five rows on a phone, and the shorter row
   is the one reviewed on the devices (#229). A toggle that is off goes quiet whole, move
   included, so the row reads as what is drawn. */
.index-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin-top: 0.25rem;
}

.index-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.25rem;
  padding: 0 0.625rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  color: var(--t3);
  font-size: 0.8125rem;
  white-space: nowrap;
}

.index-toggle[aria-pressed='true'] {
  background: var(--bg2);
  color: var(--t1);
}

.index-toggle[aria-pressed='false'] .index-line {
  stroke: var(--t3);
}

.index-toggle[aria-pressed='false'] .index-toggle-move {
  color: var(--t3);
}

/* One CSS pixel per user unit (app/index-chart.ts, SWATCH), so a dash pattern is the plot's. */
.index-swatch {
  flex: none;
  width: 2rem;
  height: 0.25rem;
  overflow: visible;
}

.index-toggle-move {
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

/* The panel grid (#316), settled on the devices in #256 and #229: fitted tracks rather than
   named columns, so a phone gets one, an iPad two either way and a desktop three with no
   breakpoint of its own, and an odd count wraps without stretching. A card stretches to the
   tallest in its row. */
.panel-grid {
  margin-top: 1.75rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: 1rem;
}

.panel-card {
  min-width: 0;
  padding: 1rem;
  border: 1px solid var(--line);
  border-radius: 0.625rem;
  background: var(--bg2);
}

/* Today's mix as #354's variant B: the bar, then a keyed row per Asset class. */
.mix-bar {
  display: flex;
  gap: 1px;
  height: 0.625rem;
  margin: 0.25rem 0 0.75rem;
}

.mix-segment {
  display: block;
  height: 100%;
}

.mix-row {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  padding: 0.3125rem 0;
  font-size: 0.78125rem;
}

.mix-name {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 0.4375rem;
  min-width: 0;
  color: var(--t2);
}

.mix-swatch {
  flex: none;
  width: 0.5625rem;
  height: 0.5625rem;
}

.mix-segment,
.mix-swatch {
  background: var(--band-fill);
}

.mix-percent,
.mix-figure,
.mix-sub-figure {
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

.mix-percent {
  width: 3.5rem;
  color: var(--t2);
}

.mix-figure {
  width: 6rem;
}

.mix-sub {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-top: 0.375rem;
  padding-left: 1rem;
  color: var(--t2);
  font-size: 0.71875rem;
}

.mix-sub-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mix-sub-figure {
  margin-left: auto;
  font-size: inherit;
}

.mix-tag {
  flex: none;
  padding: 0 0.375rem;
  border: 1px solid var(--line);
  border-radius: 0.25rem;
  color: var(--t3);
  font-size: 0.65625rem;
}

.mix-tag-flag {
  border-color: var(--neg);
  color: var(--neg);
}

/* The sentence and the bar under it at the measure the other sentences keep, inside a card that
   is the whole row on a phone. */
.saved {
  max-width: 34rem;
}

.saved-sentence {
  margin: 0;
  color: var(--t2);
  font-size: 0.8125rem;
  line-height: 1.5;
}

.saved-bar {
  display: flex;
  height: 0.5rem;
  margin-top: 0.875rem;
  background: var(--line);
}

.saved-bar-in {
  background: var(--ink);
}

.saved-bar-market {
  flex: 1;
  background: var(--t3);
}

.saved-bar-keys {
  display: flex;
  justify-content: space-between;
  margin-top: 0.4375rem;
  color: var(--t3);
  font-size: 0.65625rem;
}

.pace-line,
.fee-line {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5625rem;
}

.pace-figure,
.fee-figure {
  font-size: 1.625rem;
  font-variant-numeric: tabular-nums;
}

.pace-note,
.fee-note {
  color: var(--t3);
  font-size: 0.78125rem;
}

/* Fee drag's rate under its dollars (#319), at the sentence's size. */
.fee-rate {
  margin: 0.625rem 0 0;
  color: var(--t2);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

/* Accounts over time (#317) and Mix over time (#363). The plot, the readout and the scrub are
   the chart's rules above; what is their own is the band fills and the key row (`share-stack.ts`). */
.accounts-over-time,
.mix-over-time {
  margin-top: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}

.accounts-over-time > .accounts-over-time-title,
.mix-over-time > .mix-over-time-title {
  margin-bottom: 0.125rem;
}

/* One fill per band, on the plot and on its key's swatch alike. */
.share-band {
  fill: var(--band-fill);
  stroke: var(--bg);
  stroke-width: 0.5;
  vector-effect: non-scaling-stroke;
}

/* The key wraps under the readout, one swatch and name per band. */
.share-key {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3125rem 1rem;
  margin-top: 0.25rem;
  color: var(--t2);
  font-size: 0.75rem;
}

.share-key-item {
  display: flex;
  align-items: center;
  gap: 0.4375rem;
}

.share-swatch {
  flex: none;
  width: 0.5625rem;
  height: 0.5625rem;
}

/* Mix over time's stretch before a stored Composition, as #354's variant B drew it. */
.mix-over-time-held {
  fill: var(--bg);
  opacity: 0.45;
}

.mix-over-time-held-rule {
  stroke: var(--t2);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  vector-effect: non-scaling-stroke;
}

/* The holdings table (#318), settled on the devices in #256: a flex row per Holding with every
   cell in the DOM, and the width deciding which show. The phone is the base: ticker, the name
   wrapping, and value over Gain in one right-hand cell. The header row is off there because
   the block head names the terms, and its buttons keep the 44px floor where it is on. */
.holdings {
  margin-top: 1.75rem;
}

/* The title's own bottom margin is the gap to the table. */
.holdings-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.75rem;
}

.holdings-count,
.holdings-as-of {
  color: var(--t3);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
}

.holdings-row {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.625rem 0;
  border-bottom: 1px solid var(--line);
}

.holdings-head {
  display: none;
}

.holdings-cell {
  flex: none;
  font-size: 0.78125rem;
  font-variant-numeric: tabular-nums;
}

.holdings-ticker {
  width: 3.5rem;
}

.holdings-description {
  flex: 1;
  min-width: 0;
}

.holdings-name {
  display: block;
  color: var(--t2);
}

.holdings-description-line {
  display: block;
  margin-top: 0.125rem;
  color: var(--t3);
  font-size: 0.65625rem;
}

.holdings-description-line:empty {
  display: none;
}

.holdings-shares,
.holdings-price,
.holdings-fee {
  display: none;
  color: var(--t3);
  text-align: right;
}

.holdings-figures {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.125rem;
}

.holdings-gain {
  display: flex;
  justify-content: flex-end;
  gap: 0.375rem;
  color: var(--t3);
  text-align: right;
}

.holdings-gain-percent {
  opacity: 0.7;
}

.holdings-sort {
  display: block;
  width: 100%;
  min-height: var(--chip-height);
  padding: 0;
  color: var(--t3);
  font-size: 0.625rem;
  letter-spacing: 0.09em;
  text-align: inherit;
  text-transform: uppercase;
}

.holdings-sort[aria-pressed='true'] {
  color: var(--t1);
}

/* Both iPads: the header row on, the fee column in, value beside Gain and the name on one line. */
@media (min-width: 48rem) {
  .holdings-row {
    align-items: center;
    gap: 0.75rem;
  }

  .holdings-head {
    display: flex;
    padding: 0;
  }

  .holdings-ticker {
    width: 4.5rem;
    font-size: 0.8125rem;
  }

  .holdings-name,
  .holdings-description-line {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .holdings-figures {
    flex-direction: row;
    align-items: center;
    gap: 0.75rem;
  }

  .holdings-value {
    width: 6.5rem;
    text-align: right;
    font-size: 0.8125rem;
  }

  /* Wide enough for the widest Holding's Gain on one line: at 6.75rem VTI's "▲ $91,145 10.7%"
     wrapped on both iPads. */
  .holdings-gain {
    width: 8.75rem;
  }

  .holdings-fee {
    display: block;
    width: 3.5rem;
    font-size: 0.71875rem;
  }
}

/* Shares and price only where room is left after the rail takes its 12.5rem, which is above the
   64rem the rail opens at rather than at it: at 1024 the iPad in landscape has 760px of content
   and six columns squeezed the name to nothing. */
@media (min-width: 78rem) {
  .holdings-shares {
    display: block;
    width: 5.5rem;
  }

  .holdings-price {
    display: block;
    width: 4.5rem;
  }
}

.nav {
  order: 2;
  display: flex;
  flex: none;
  border-top: 1px solid var(--line);
  background: var(--bg2);
  padding-bottom: env(safe-area-inset-bottom);
}

.nav-head {
  display: none;
}

.nav-item {
  position: relative;
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  /* The one measurement not to trim: overriding it made a control unpressable on the phone. */
  min-height: 3.5rem;
  border-top: 2px solid transparent;
  color: var(--t3);
  font-size: 0.625rem;
}

.nav-item[aria-current] {
  border-top-color: var(--ink);
  color: var(--t1);
}

/* There is no rail to widen on the phone, and a fourth thing in the tab bar is a fourth tab. */
.nav-toggle {
  display: none;
}

.nav-glyph {
  font-size: 0.875rem;
}

/* Rides beside the glyph rather than in the tab's corner, where it would read as belonging to
   the tab bar rather than to this destination. Positioned rather than laid out, so the tab bar's
   column and the rail's row do not each need a slot for something usually absent. */
.nav-dot {
  position: absolute;
  top: 0.5rem;
  left: calc(50% + 0.4375rem);
  width: 0.4375rem;
  height: 0.4375rem;
  border-radius: 50%;
  background: var(--alert);
}

/* The rail starts here and starts narrow. The design draws it at 72px with the labels off in
   tablet portrait and at 200px with them on everywhere wider, and shell.ts hands that decision
   to whoever wants the room back for a chart. */
@media (min-width: 48rem) {
  .shell {
    flex-direction: row;
  }

  .nav {
    order: 0;
    width: 4.5rem;
    flex-direction: column;
    padding: 1.125rem 0 0.875rem;
    border-top: 0;
    border-right: 1px solid var(--line);
    background: var(--bg);
    transition: width 120ms ease;
  }

  @media (prefers-reduced-motion: reduce) {
    .nav {
      transition: none;
    }
  }

  .nav-head {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.625rem;
    padding: 0 0 1.375rem;
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.02em;
  }

  /* Off the screen rather than out of the DOM, so a glyph a person reads as an icon is still
     the destination's name to a screen reader. */
  .nav-wordmark,
  .nav-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .nav-diamond {
    width: 0.6875rem;
    height: 0.6875rem;
    flex: none;
    border: 1.5px solid var(--t2);
    transform: rotate(45deg);
  }

  .nav-item {
    flex: none;
    flex-direction: row;
    justify-content: center;
    gap: 0.6875rem;
    min-height: 2.625rem;
    padding: 0;
    border-top: 0;
    border-left: 2px solid transparent;
    font-size: 0.84375rem;
    text-align: left;
  }

  .nav-item[aria-current] {
    border-top-color: transparent;
    border-left-color: var(--ink);
  }

  .nav-toggle {
    display: flex;
    margin-top: auto;
    color: var(--t3);
  }

  .nav-glyph {
    width: 0.875rem;
    flex: none;
    text-align: center;
    font-size: 0.8125rem;
  }

  /* The rail's items are full-width rows, so the dot goes to the trailing edge and stays there
     whether or not the labels are showing. */
  .nav-dot {
    top: 50%;
    left: auto;
    right: 0.625rem;
    margin-top: -0.21875rem;
  }

  .dest {
    padding: 2rem 1.5rem;
  }

  .dest-title {
    font-size: 1.5rem;
  }

  /* The room the rail gives back goes to the plot, which is the one thing here that reads
     better tall (shell.ts). */
  :root {
    --plot-height: 15rem;
  }

  /* Open, which shell.ts sets from the viewport and the toggle overrides for the session. */
  .shell[data-rail='labels'] .nav {
    width: 12.5rem;
  }

  .shell[data-rail='labels'] .nav-head {
    justify-content: flex-start;
    padding: 0 1.125rem 1.375rem;
  }

  .shell[data-rail='labels'] .nav-wordmark,
  .shell[data-rail='labels'] .nav-label {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
  }

  .shell[data-rail='labels'] .nav-item {
    justify-content: flex-start;
    padding: 0 1.125rem;
  }

  /* The frame's rail, open at the width the shell will open at (#203). Restated rather than
     shared, because the shell reads the breakpoint in script and sets `data-rail` off it
     (RAIL_LABELS in shell.ts), and the frame has no script to read it with. */
  @media (min-width: 64rem) {
    .frame .nav {
      width: 12.5rem;
    }

    .frame .nav-head {
      justify-content: flex-start;
      padding: 0 1.125rem 1.375rem;
    }

    .frame .nav-wordmark,
    .frame .nav-label {
      position: static;
      width: auto;
      height: auto;
      overflow: visible;
      clip-path: none;
    }

    .frame .nav-item {
      justify-content: flex-start;
      padding: 0 1.125rem;
    }
  }
}

/* The accounts destination (#114). The Feed and the observation sit on one line under the name,
   at one weight and one colour for all three Feeds, for the reason accounts.ts gives about #63. */

/* The header (#115). The `+` is the only control the list carries, so it sits on the title's own
   line rather than in a bar of its own. */
.accounts-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.accounts-add {
  margin-left: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg2);
  font-size: 1.125rem;
  line-height: 1;
}

/* What the last Add did (accounts.ts). */
.accounts-note {
  margin: 0.25rem 0 0;
  color: var(--t2);
  font-size: 0.8125rem;
}

.accounts-note:empty {
  display: none;
}

/* Which window the changes below cover, in words. Why it survived the chips is accounts.ts. */
.accounts-range {
  margin: 0;
  color: var(--t3);
  font-size: 0.75rem;
}

/* Tighter to the line above than to the rows below, so the chips read as belonging to the head
   rather than to the first Category. */
.accounts-listing .range-chips {
  margin-top: 0.625rem;
}

.category-group {
  margin-top: 1.5rem;
}

/* The mockup's heading: the total is the brightest thing on the line and the label is a quiet
   tag beside it, because a heading over a list of figures is scanned for its number. */
.category-head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin: 0;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--line);
  font-weight: 400;
}

.category-label {
  color: var(--t2);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.category-count {
  color: var(--t3);
  font-size: 0.65625rem;
}

.category-total {
  margin-left: auto;
  color: var(--t1);
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
}

.account-rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

.account-row {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 0.875rem;
  text-align: left;
  /* The design's 54px row, which is what keeps a list of six scannable rather than dense. */
  min-height: 3.375rem;
  padding: 0.6875rem 0;
  border-bottom: 1px solid var(--line);
}

.row-text {
  flex: 1;
  min-width: 0;
}

.row-name {
  display: block;
  font-size: 0.9375rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.row-meta {
  margin: 0.1875rem 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4375rem;
  color: var(--t3);
  font-size: 0.65625rem;
}

/* The mockup's `isWide` element. There is no room for it beside a name and a figure on a phone,
   so it is drawn and hidden rather than gated in script: a rotation is not a re-render. */
.row-spark {
  display: none;
  flex: none;
  overflow: visible;
}

.row-spark-line {
  fill: none;
  stroke: var(--t3);
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.row-figures {
  flex: none;
  text-align: right;
}

.row-figure {
  display: block;
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
}

.row-change {
  display: block;
  margin-top: 0.1875rem;
  color: var(--t3);
  font-size: 0.65625rem;
  font-variant-numeric: tabular-nums;
}

/* The one state that asks for action, and the only row that takes a colour. */
.row-flag {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3125rem;
  color: var(--alert);
}

.row-flag-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 0.875rem;
  height: 0.875rem;
  border: 1px solid currentColor;
  border-radius: 50%;
  font-size: 0.625rem;
  font-weight: 600;
  line-height: 1;
}

@media (min-width: 48rem) {
  .row-spark {
    display: block;
  }
}

/* Adding an Account (#115), pushed over the list the way an Account's own screen is. */

.add-account[hidden] {
  display: none;
}

.add-bar {
  margin-bottom: 0.75rem;
}

.add-cancel {
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  padding-right: 0.75rem;
  color: var(--t3);
  font-size: 0.6875rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.add-title {
  margin: 0;
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  margin-top: 1.25rem;
}

.field-label {
  color: var(--t2);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.field-input,
.field-select,
.position-note,
.balance-note {
  /* The note is a `textarea` (app/positions.ts), so it takes the same box as a field rather
     than the browser's default one. */
  resize: none;
  min-height: 2.75rem;
  max-width: 100%;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg);
  color: var(--t1);
  /* Inherited at 1rem and never set smaller: iOS zooms the viewport on any field under 16px
     and does not zoom back out. */
  font: inherit;
}

/* The example, still an example: a native select paints its option at the text colour, and at
   full brightness the closed field reads as a Category somebody already chose. */
.field-select:invalid {
  color: var(--t3);
}

/* The Feed (#135). The chosen half is carried by fill rather than by a colour: nothing on this
   screen is a state to get out of. */
.feed-choices {
  display: flex;
  gap: 0.5rem;
}

.feed-choice {
  flex: 1;
  min-height: 2.75rem;
  padding: 0.625rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg);
  color: var(--t2);
  font-size: 0.875rem;
}

.feed-choice[aria-pressed='true'] {
  border-color: var(--t3);
  background: var(--bg2);
  color: var(--t1);
  font-weight: 500;
}

/* Side by side at one weight (ADR-0007, and add-account.ts). */
.add-actions {
  display: flex;
  gap: 0.75rem;
  margin-top: 1.5rem;
}

.add-action {
  flex: 1;
  min-height: 2.75rem;
  padding: 0.625rem 1rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg2);
  font-size: 0.875rem;
  font-weight: 500;
}

.add-action[disabled] {
  opacity: 0.35;
}

.add-status {
  margin: 0.75rem 0 0;
  color: var(--t2);
  font-size: 0.8125rem;
}

.add-status:empty {
  display: none;
}

/* A refused write is the third thing in the app allowed the alert colour: it is a failure the
   account holder has to act on, reported where the button that asked for it is (#26). */
.add-status[data-state='failed'] {
  color: var(--alert);
}

/* An Account's own screen (#116), pushed over the list inside the same destination. The bar is
   the only chrome it adds: back at the left, and `⋯` at the right where the actions are. */

.detail-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.detail-back {
  display: flex;
  align-items: center;
  /* The touch target the design asks of every control, on a label this small. */
  min-height: 2.75rem;
  padding-right: 0.75rem;
  color: var(--t3);
  font-size: 0.6875rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.detail-menu {
  position: relative;
}

.detail-more {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  color: var(--t2);
  font-size: 1.25rem;
  line-height: 1;
}

/* Over the screen rather than in it, so opening the menu moves nothing underneath. */
.detail-action-list {
  position: absolute;
  right: 0;
  z-index: 5;
  margin: 0;
  padding: 0.25rem;
  min-width: 12rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg2);
  list-style: none;
}

.detail-action-list[hidden] {
  display: none;
}

.detail-action {
  display: block;
  width: 100%;
  min-height: 2.75rem;
  padding: 0 0.75rem;
  border-radius: 0.375rem;
  text-align: left;
  font-size: 0.875rem;
}

.detail-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
}

/* Flexed rather than shrink-to-fit, so the name and the figure stay on one line on the phone
   and a long name ellipsises instead of pushing the figure onto its own row. */
.detail-text {
  flex: 1 1 10rem;
  min-width: 0;
}

.detail-name {
  margin: 0;
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.detail-meta {
  margin: 0.3125rem 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4375rem;
  color: var(--t3);
  font-size: 0.6875rem;
}

.detail-figures {
  margin: 0;
  margin-left: auto;
  text-align: right;
}

.detail-figure {
  display: block;
  font-size: 1.75rem;
  font-variant-numeric: tabular-nums;
}

/* The mockup's three parts on one line: the move, the same move as a percent, and the window
   they are over. The window is quiet, because it is the only one of the three that is not a
   measurement. */
.detail-moved {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: baseline;
  gap: 0.5rem;
  margin-top: 0.25rem;
  color: var(--t3);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.detail-over {
  color: var(--t3);
}

/* The sentence the screen exists to state: how this Account's value is derived, in its own
   terms (#116). It sits above the history, because it is what the history is of. */
.account-detail .range-chips {
  margin-top: 1rem;
}

/* The note the position editor left on the snapshot in force (#117), under the figure it
   explains. One line, and absent entirely where no note was written. */
.detail-entered {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.75rem 0 0;
  font-size: 0.8125rem;
}

.detail-entered-when {
  color: var(--t3);
}

.detail-entered-note {
  color: var(--t2);
}

.detail-history {
  margin-top: 1.75rem;
}

.detail-heading,
.carries-heading,
.data-heading,
.index-title,
.accounts-over-time-title,
.mix-over-time-title,
.holdings-title,
.panel-label {
  margin: 0 0 0.75rem;
  color: var(--t3);
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.detail-plot {
  display: block;
  width: 100%;
  height: 10rem;
  overflow: visible;
}

/* The same line the chart carries, in the same words (app/readout.ts), so the two plots answer a
   drag identically. Reserved whether or not anything is held, so nothing below it shifts. */
.detail-readout {
  margin: 0.5rem 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.625rem;
  min-height: 1.25rem;
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

/* The week's own min and max, at the net worth chart's weight (`.chart-band`). */
.detail-band {
  fill: var(--t2);
  fill-opacity: 0.22;
}

.detail-line {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.5;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.detail-ticks,
.detail-scale {
  margin: 0.4375rem 0 0;
  display: flex;
  justify-content: space-between;
  color: var(--t3);
  font-size: 0.65625rem;
  font-variant-numeric: tabular-nums;
}

.detail-empty,
.carries-empty {
  margin: 0;
  max-width: 34rem;
  color: var(--t3);
  font-size: 0.8125rem;
  line-height: 1.5;
}

/* The way to the Carried history (#204). A row rather than a link, because the count on its
   right is the answer for most Accounts and the screen behind it is worth opening when it is
   not. */
.detail-carries {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.875rem;
  width: 100%;
  margin-top: 1.75rem;
  padding: 0.875rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  color: var(--t1);
  font-size: 0.8125rem;
  text-align: left;
}

.detail-carries-count {
  color: var(--t3);
}

/* Its chips are the shared ones, so the screen opens on the range you were already reading. */
.carries-screen .range-chips {
  margin: 1rem 0;
}

.carries {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* One row per Carry, at the row height the list uses, with the observation date carrying the
   same weight as the days it stood for: neither is the answer on its own. */
.carry {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.875rem;
  padding: 0.6875rem 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.8125rem;
}

.carry-observed {
  color: var(--t3);
  font-size: 0.75rem;
}

/* The Link session (#118), pushed over the list the way Adding and an Account's own screen are.
   Its choices are cards rather than list rows: each one is a decision with a consequence stated
   under it, and a 44px row has nowhere to put the consequence. */

.link-session[hidden] {
  display: none;
}

.link-bar {
  margin-bottom: 0.75rem;
}

.link-cancel {
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  padding-right: 0.75rem;
  color: var(--t3);
  font-size: 0.6875rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.link-title {
  margin: 0 0 0.75rem;
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.link-lead {
  margin: 0 0 1rem;
  max-width: 34rem;
  color: var(--t2);
  font-size: 0.875rem;
}

.link-dim {
  margin: 0.75rem 0 0;
  max-width: 34rem;
  color: var(--t3);
  font-size: 0.8125rem;
}

.link-group {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 1.25rem;
}

.link-group-label {
  color: var(--t2);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* One class for a source and a pick, so the two steps of the flow are visibly the same act. */
.link-choice,
.link-pick {
  display: grid;
  gap: 0.1875rem;
  width: 100%;
  padding: 0.75rem 0.875rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg2);
  color: inherit;
  font: inherit;
  text-align: left;
}

.link-choice-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.link-choice-name {
  font-weight: 500;
}

.link-choice-sub {
  color: var(--t3);
  font-size: 0.75rem;
}

.link-picks {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

/* The chosen one, marked twice. The ring is the signal at a glance and the tick is the one that
   survives greyscale, which is the rule #26 sets for every state this app shows. */
.link-pick[aria-pressed='true'] {
  border-color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}

/* Shown and unpickable, because hiding it makes its absence the next question (#118). Keyed on
   the reason, not on `disabled`, which a call in flight also sets (#242). */
.link-pick[data-held] {
  opacity: 0.45;
}

/* Dead until a call answers, which is not the same statement: it dims less and goes dashed, so a
   tap that does nothing does not look like a tap that missed. The ring goes with it, or its inset
   line fills the dashes back in, and the tick alone marks the picked row (#242). */
.link-choice[data-waiting],
.link-pick[data-waiting] {
  border-style: dashed;
  box-shadow: none;
  opacity: 0.7;
}

.link-tick {
  color: var(--t3);
  font-size: 0.875rem;
}

.link-pick[aria-pressed='true'] .link-tick {
  color: var(--t1);
}

.link-pick-figure {
  margin-left: auto;
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
}

/* Side by side like the Add's, and the primary one is weighted here because these two are not
   one act with one continuation: `Not now` and `Link it` end the session differently. */
.link-actions {
  display: flex;
  gap: 0.75rem;
  margin-top: 1.5rem;
}

.link-action {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  padding: 0.625rem 1rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg2);
  color: inherit;
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
}

.link-action[data-weight='primary'] {
  border-color: var(--ink);
}

.link-action[disabled] {
  opacity: 0.35;
}

.link-status {
  margin: 0.75rem 0 0;
  color: var(--t2);
  font-size: 0.8125rem;
}

.link-status:empty {
  display: none;
}

/* The alert colour, on the same terms the Add's status has it: a failure the account holder has
   to act on, reported where the button that asked for it is (#26). */
.link-status[data-state='failed'] {
  color: var(--alert);
}

/* The chrome the three screens pushed over an Account share: the editor (#117), the balance
   entry (#146) and the Carried history (#204). Then the editor's own, two inputs to a row: the
   ticker at a fixed width because four characters is what they nearly all are, and the count
   taking the rest, which is the half being keyed against a broker's screen. */

.position-editor[hidden],
.balance-entry[hidden],
.carries-screen[hidden] {
  display: none;
}

.position-bar,
.balance-bar,
.carries-bar {
  margin-bottom: 0.75rem;
}

.position-back,
.balance-back,
.carries-back {
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  padding-right: 0.75rem;
  color: var(--t3);
  font-size: 0.6875rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.position-title,
.balance-title,
.carries-title {
  margin: 0 0 1rem;
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.position-rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

.position-row {
  padding: 0.375rem 0;
}

.position-fields {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.position-input {
  min-height: 2.75rem;
  min-width: 0;
  flex: 1;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg);
  color: var(--t1);
  /* 1rem, for the reason `.field-input` gives: iOS zooms any field under 16px and stays there. */
  font: inherit;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.position-ticker {
  flex: none;
  width: 7rem;
  text-align: left;
  letter-spacing: 0.03em;
}

.position-drop {
  flex: none;
  width: 2.75rem;
  min-height: 2.75rem;
  color: var(--t3);
  font-size: 1.125rem;
}

/* The close each row is valued at, under the boxes it belongs to: the price left, under the
   ticker, and what the row comes to right, under the count. */
.position-priced {
  display: flex;
  align-items: baseline;
  gap: 0.4375rem;
  margin: 0.25rem 0 0;
  color: var(--t3);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.position-priced:empty {
  display: none;
}

.position-value {
  margin-left: auto;
  color: var(--t2);
}

.position-said,
.balance-said {
  margin: 0.25rem 0 0;
  color: var(--t2);
  font-size: 0.8125rem;
}

.position-said:empty,
.balance-said:empty {
  display: none;
}

.position-add {
  min-height: 2.75rem;
  margin-top: 0.5rem;
  padding: 0 0.75rem;
  color: var(--t2);
  font-size: 0.875rem;
}

/* The safety net, at the weight of the thing being matched against a broker's own screen. */
.position-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 1.25rem;
  padding-top: 0.875rem;
  border-top: 1px solid var(--line);
}

.position-total-label {
  color: var(--t2);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.position-total-figure {
  font-size: 1.5rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.position-total-said {
  margin: 0.375rem 0 0;
  color: var(--t2);
  font-size: 0.8125rem;
}

.position-total-said:empty {
  display: none;
}

.position-save,
.balance-save {
  width: 100%;
  min-height: 2.75rem;
  margin-top: 1.5rem;
  padding: 0.625rem 1rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg2);
  font-size: 0.875rem;
  font-weight: 500;
}

.position-save[disabled],
.balance-save[disabled] {
  opacity: 0.35;
}

.position-status,
.balance-status {
  margin: 0.75rem 0 0;
  color: var(--t2);
  font-size: 0.8125rem;
}

.position-status:empty,
.balance-status:empty {
  display: none;
}

.position-status[data-state='failed'],
.balance-status[data-state='failed'] {
  color: var(--alert);
}

/* The balance entry screen (#146). Everything it shares with the position editor is on that
   editor's rules above; this is the one thing it does not share. The field carries the weight of
   the figure it is about to become, because it is the whole of what a `manual` Feed reports. */

.balance-input {
  min-height: 2.75rem;
  width: 100%;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: 0.5rem;
  background: var(--bg);
  color: var(--t1);
  font: inherit;
  /* Well past the 1rem `.field-input` needs to keep iOS from zooming, which it also satisfies. */
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* The data destination (#119). Four sections of readings at one weight, because the screen's job
   is to be complete rather than to raise its voice. Nothing here is a banner and nothing here
   carries a signal colour. */

.data-section {
  margin-top: 1.75rem;
}

.data-rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The row shape the Carries list uses (.carry), so the two lists of readings scan alike. */
.data-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.875rem;
  padding: 0.6875rem 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.8125rem;
}

.data-label {
  color: var(--t2);
}

/* Stacked and right-aligned, so a Run's two dates read as one reading rather than as two rows
   with the label on only one of them. */
.data-lines {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.1875rem;
  text-align: right;
}

.data-value {
  font-variant-numeric: tabular-nums;
}

.data-quiet {
  color: var(--t3);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.data-value .data-quiet {
  margin-left: 0.5rem;
}

/* Monospaced and quiet. A code names an outage rather than asking for anything, and the row that
   offers the fix is on the accounts destination (#114). */
.data-code {
  color: var(--t2);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.75rem;
}

.data-empty {
  padding: 0.6875rem 0;
  color: var(--t3);
  font-size: 0.8125rem;
}
