/* Shell, topbar, and tab routing — mirrors app_modules/main_window.py */
.backdrop {
  position: fixed;
  inset: -24% -14% -8% -14%;
  background:
    var(--backdrop-gradient-layer),
    radial-gradient(circle at 10% 10%, rgba(74, 158, 255, 0.18), transparent 45%),
    radial-gradient(circle at 80% 20%, rgba(156, 124, 244, 0.12), transparent 50%),
    radial-gradient(circle at 50% 80%, rgba(78, 205, 196, 0.12), transparent 55%);
  opacity: 1;
  filter: blur(34px);
  /* 🔴 The three radial washes above are HARDCODED, not theme colours, so
     "background gradient: off" still left a blue/purple/teal tint on the page —
     and the plain white theme is a page that is #f8f8f8 and nothing else. The
     toggle now reaches them (applyDisplaySettingsToDocument stamps the class). */
  transform: scale(1.08);
  transform-origin: center;
  pointer-events: none;
  z-index: 0;
}

body.no-backdrop-gradient .backdrop {
  background: var(--backdrop-gradient-layer);
}

.app-shell {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  padding: 0;
}

/* Topbar */
.topbar {
  /* Never shrink below the height of its own content.
   *
   * `.topbar` is a wrapping flex row inside `.app-shell`, which is a flex
   * COLUMN. On a phone its sections are each `flex: 1 1 100%`, so
   * they wrap onto three lines — and because the default `flex-shrink: 1`
   * applies to `.topbar` as an item of the column, the shell then compressed
   * it back to roughly one line's height while the wrapped lines kept their
   * position. The third line (`.topbar-actions`) rendered *outside* its own
   * parent and straight through the guest banner and the calendar under it.
   *
   * Measured on a 412px phone before the fix: `.topbar` 103px tall,
   * `.topbar-actions` running 111px→195px, guest banner starting at 103px —
   * a 92px overlap. `overflow: hidden` would have clipped the buttons instead
   * of revealing them; this makes the parent honest about its content. */
  flex-shrink: 0;
  box-sizing: border-box;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  min-height: 52px;
  gap: 16px;
  padding: 12px 16px;
  border-radius: 0;
  background: var(--daily-goals-bg, var(--card));
  border: 0;
  margin-bottom: 0;
  position: sticky;
  top: 0;
  z-index: 30;
}

.topbar-left {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  min-width: 0;
}

.topbar-period-navigation {
  display: contents;
}

/* The Goal Tree names no period, so it gets no period label.
   It is every horizon at once — year down to day, all on screen together — and a
   single date at the top of it would be describing one column out of five. Every
   other view keeps the label, deliberately: on Year as much as on Day, because
   without it you cannot tell which year, quarter, month, week or day you are
   looking at.

   The arrows and Today stay on the tree. Its columns still step, and they step
   from here. `data-view` is stamped by `router.js` on the container the island
   mounts into — the label itself is React's. */
.topbar-period-navigation[data-view='goals-dashboard'] #btn-week-label {
  display: none;
}

.topbar-week-label {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 6px 8px;
  margin: 0;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text-primary);
  cursor: default;
  white-space: nowrap;
}

.tab-group {
  display: inline-flex;
  gap: 6px;
  padding: 4px;
  border-radius: 12px;
  background: var(--utility-surface-strong);
  border: 1px solid var(--border-subtle);
}

.tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 10px;
  font-size: 12px;
  color: var(--text-secondary);
  cursor: pointer;
  user-select: none;
  border: 1px solid transparent;
  background: transparent;
  transition: all 0.2s ease;
}

/* One glyph per tab (2026-08-12; it was the telescope alone before that, and
   `react-src/primary_navigation.tsx` records why the rule changed and what each
   mark is). Each inherits the tab's colour, so it lights with the tab rather
   than needing its own active rule. `flex-shrink: 0` because the strip is the
   widest row in the app and the glyph must not be the thing that squashes when
   it runs out of room. */
.tab-icon {
  flex-shrink: 0;
}

.tab.active,
.tab:hover {
  background: var(--tab-active-bg);
  color: var(--text-primary);
  border-color: var(--tab-active-border);
}

/* Reflect has no surface on views that plan no days (the Goal Tree, the wider
   goal horizons, Timer, Settings). It greys out there rather than disappearing:
   the strip is meant to be the one row that is always in the same place and
   always the same width, and a control that comes and goes reads as a fault. The
   hover rule above has to be cancelled too, or the dead tab still lights up
   under the pointer and promises a press. */
.tab.is-unavailable,
.tab.is-unavailable:hover {
  background: transparent;
  border-color: transparent;
  color: var(--text-secondary);
  cursor: default;
  opacity: 0.4;
}

/* Four tabs, and they are the app's primary control — big enough to hit without
   aiming, and a shared minimum width so "Long-Term Goals", "Calendar", "Do" and
   "Reflect" read as four steps of one cycle rather than four words of wildly
   different lengths ("Do" is two characters against "Long-Term Goals"' sixteen).

   `min-width` came down from 78px when the pair became four: the strip shares the
   topbar row with the period label, the prev/next/Today buttons and two
   dropdowns, and four 78px tabs pushed that row into wrapping at laptop widths.
   The hit area is held by the vertical padding instead. */
#primary-navigation .tab {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  min-width: 62px;
  padding: 7px 12px;
  text-align: center;
  white-space: nowrap;
}

/* The exception to the paragraph above: a tab whose surface has no view at the
   selected span leaves the row entirely (`app_surfaces.js`, 2026-08-09) — the
   Calendar at 3Y, Do at anything wider than a day. Greying those out would put
   two or three dead controls in the app's primary row, which reads worse than a
   shorter one. Stated explicitly because `.tab` sits in a flex row and any rule
   that later gives it a `display` would silently outrank the UA's `[hidden]`. */
#primary-navigation .tab[hidden] {
  display: none;
}

/* Out of room: each tab is its glyph alone, so the sections either side of the
   strip never run under it. js/app-shell/topbar_fit.js measures the row and
   sets the attribute; the tab keeps its name through `aria-label` and `title`
   (react-src/primary_navigation.tsx). */
.topbar[data-tabs-compact] #primary-navigation .tab {
  min-width: 0;
  padding: 7px 10px;
}

.topbar[data-tabs-compact] #primary-navigation .tab-label {
  display: none;
}

/* Set only for topbar_fit.js's synchronous read, never painted: the row may
   not wrap, so whatever does not fit runs past the topbar's right edge. */
.topbar[data-tabs-measuring] {
  flex-wrap: nowrap;
}

/* Beside the strip, each side section is at least as wide as what is in it.
   With `flex: 1 1 0` that keeps the strip centred while both halves hold their
   content and slides it toward the lighter side when one does not — instead of
   squashing the arrows and running the period label under the tabs. Past the
   point where the three no longer fit, topbar_fit.js takes the tabs' words and
   then stacks the row. Not on a phone: styles/mobile.css scrolls
   `.topbar-left` sideways, which needs it narrower than its content. */
html:not([data-device='phone']) .topbar:not(.auth-topbar) > :is(.topbar-left, .topbar-actions) {
  min-width: max-content;
}

/* Too wide even with glyph-only tabs (topbar_fit.js): the sections take a
   line each. The Views card and its submenus then hang off a button at the
   LEFT edge, so they open right. */
.topbar[data-topbar-stacked] {
  flex-direction: column;
  align-items: flex-start;
}

.topbar[data-topbar-stacked] .topbar-actions > .topbar-views-wrap .topbar-views-panel {
  left: 0;
  right: auto;
}

.topbar[data-topbar-stacked] .topbar-actions > .topbar-views-wrap .topbar-days-menu {
  left: 100%;
  right: auto;
  padding: 8px 8px 8px 16px;
}

.topbar[data-topbar-stacked] .topbar-actions > .topbar-views-wrap .topbar-days-option::after {
  left: 100%;
  right: auto;
}

.topbar-actions {
  flex: 1 1 0;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  min-width: 0;
}

.nav-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: 8px;
  background: transparent;
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
}

.nav-icon-button:hover {
  color: var(--text-primary);
  background: var(--utility-surface-strong);
  border-color: var(--tab-active-border);
}

/* Icon button with a text label beside the icon */
.nav-icon-button--labeled {
  width: auto;
  gap: 6px;
  padding: 0 10px;
}

.nav-icon-button-label {
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

/* Labeled topbar toggles (Weekly goals + Brain dump): accent icon + label so
   they stand out, and a filled Today-button look while their panel is open. The
   icon (stroke="currentColor") and label both inherit the button color. Both
   share one style so they read as a matched set. The Daily goals and Undated
   daily toggles were in this set until 2026-08-12, when those panels were
   deleted; their ids are gone from index.html, so selectors for them here would
   be dead weight that reads like a live rule. */
#btn-weekly-goals-toggle,
#btn-brain-dump-toggle {
  color: var(--accent);
}

#btn-weekly-goals-toggle:hover,
#btn-brain-dump-toggle:hover {
  color: var(--accent-hover);
  border-color: var(--accent);
  background: transparent;
}

#btn-weekly-goals-toggle.is-active,
#btn-brain-dump-toggle.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: #0b0f16;
}

#btn-weekly-goals-toggle.is-active:hover,
#btn-brain-dump-toggle.is-active:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: #0b0f16;
}

/* Today button follows the same principle: accent text by default, filled
   (its old look) only while the displayed period is the current one. */
#btn-today {
  background: transparent;
  border-color: var(--accent);
  color: var(--accent);
}

#btn-today:hover {
  background: transparent;
  border-color: var(--accent-hover);
  color: var(--accent-hover);
}

#btn-today.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: #0b0f16;
}

#btn-today.is-active:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: #0b0f16;
}

.nav-icon-button:focus-visible {
  outline: 2px solid var(--tab-active-border);
  outline-offset: 2px;
}

/* Guest-first chrome: top-bar download/login controls + guest banner */
.topbar-user-chip {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  padding: 6px 10px;
  border-radius: 8px;
  background: var(--utility-surface-strong);
  border: 1px solid var(--border-subtle);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.primary-button.small {
  padding: 6px 12px;
  font-size: 12px;
}

.linklike {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  font-size: 12px;
  color: var(--text-secondary);
  text-decoration: underline;
  cursor: pointer;
}

.linklike:hover {
  color: var(--text-primary);
}

.guest-banner {
  box-sizing: border-box;
  background: linear-gradient(90deg, rgba(80, 129, 255, 0.16), rgba(95, 201, 132, 0.12));
  border-bottom: 1px solid var(--border-subtle);
}

.guest-banner[hidden] {
  display: none;
}

.guest-banner-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 8px 16px;
}

.guest-banner-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: rgba(120, 160, 255, 0.95);
  box-shadow: 0 0 0 4px rgba(80, 129, 255, 0.16);
  flex: 0 0 auto;
}

.guest-banner-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.guest-banner-meta {
  font-size: 13px;
  color: var(--text-secondary);
  flex: 1 1 240px;
  min-width: 0;
}

.guest-banner-msg {
  font-size: 12px;
  color: var(--text-secondary);
  flex-basis: 100%;
}

.guest-banner-msg:empty {
  display: none;
}

/* View container */
#view-container {
  animation: fadeUp 0.25s ease both;
}

#view-container:not([data-view="settings"]) {
  padding-bottom: max(42vh, 320px);
}

/* The scroll pad above is room to work under an editing surface — somewhere to
   put the caret when the last row sits at the bottom of the screen. The three
   Calendar overviews are read-only grids with no caret to place, so there it is
   just a screen of dead space under the last row of a month. */
#view-container[data-view="month"],
#view-container[data-view="quarter"],
#view-container[data-view="year"] {
  padding-bottom: 24px;
}

/* The Focus Timer is a full-height surface: a bar, then a stage that sizes
   itself against the window (`views/do/do.css`). The scroll pad would be a
   third of a screen of nothing under a box that already ends at the bottom
   edge — which is exactly what it looked like. NOT 24px like the overviews
   above: this route has no gap of its own to leave. */
#view-container[data-view="do"] {
  padding-bottom: 0;
}

/* Views dropdown in topbar (Calendar, Daily Goals, Long-Term Goals tabs) —
   sits just right of the week label. router.js stamps data-view with the
   active tab; week-only options hide themselves on the other tabs. */
.topbar-views-wrap {
  position: relative;
  display: flex;
  align-items: center;
  margin-left: 8px;
}

/* On a desktop js/app-shell/views_placement.js moves this button to the
   right-hand end of the topbar (first in `.topbar-actions`, which packs to the
   right) and brings the route's Filter rows into its card. Hanging off the
   right edge, the card and its submenus open LEFT; when the topbar stacks
   (`data-topbar-stacked`, above) the button starts its line at the left edge
   and they are mirrored back. */
.topbar-actions > .topbar-views-wrap {
  margin-left: 0;
}

/* A button whose control is on loan to the Views card (the Calendar's Days
   picker, either Filter). Hidden by class, not by `display`: router.js's inline
   `display` is its per-route verdict, and views_placement.js reads it back. */
.topbar-lent-to-views-wrap {
  display: none !important;
}

.topbar-actions > .topbar-views-wrap .topbar-views-summary {
  font-size: 12px;
}

.topbar-actions > .topbar-views-wrap .topbar-views-panel {
  left: auto;
  right: 0;
  min-width: 232px;
}

.topbar-actions > .topbar-views-wrap .topbar-days-menu {
  left: auto;
  right: 100%;
  padding: 8px 16px 8px 8px;
}

.topbar-actions > .topbar-views-wrap .topbar-days-option::after {
  left: auto;
  right: 100%;
}

/* Something in the Filter rows it holds is hidden — the same warning the Filter
   button itself gives (`.topbar-area-filter-wrap--active`, area-filter.css).
   Not in the Co-work view: it hides the area rows and ignores their filter
   (views/week/shared/cowork-session-popup.css), and `:has` still sees them. */
html:not([data-cowork-mode='on']) .topbar-views-wrap:has(.topbar-area-filter-panel.is-filtering) > .topbar-views-summary {
  border-color: var(--tab-active-border);
  color: var(--tab-active-border);
}

/* The Calendar's day-count control used to be hidden here, per-tab, like the
   period-count rows below — it was a row in this panel. It is its own topbar
   dropdown as of 2026-08-09 (`views/week/widgets/day-count.css`,
   `#days-toggle-wrap` in index.html) and router.js writes its `display`
   directly, the same way it does for this wrap. */

/* Each period-count control (period_windows.js) only applies to its own goals
   tab, and the dashboard's column toggles only to the Long-Term Goals tab.

   Reflect is excused from all four board rules and answered by the block below
   instead. It is the surface that draws FOUR grids — each reflecting one of
   these boards and rendering from that board's own window
   (`js/reflect/reflect_window.js`) — so the row it needs follows the SCALE, and
   `data-view` cannot tell them apart. Without the exemption every rule here
   matches on Reflect and the dropdown comes up empty.

   `.rfl-route`, not `[data-view="reflect"]`: `router.js` puts the class on for
   every route in its `REFLECT_ROUTES` list (one today; the group readout was a
   second until 2026-09-18), and the view module stamps `data-reflect-scale`. */
.topbar-views-panel[data-view]:not([data-view="daily-long-term-goals"]):not(.rfl-route) #dlg-days-control-wrap,
.topbar-views-panel[data-view]:not([data-view="weekly-long-term-goals"]):not(.rfl-route) #wlg-weeks-control-wrap,
.topbar-views-panel[data-view]:not([data-view="monthly-long-term-goals"]):not(.rfl-route) #mlg-months-control-wrap,
.topbar-views-panel[data-view]:not([data-view="quarterly-long-term-goals"]):not(.rfl-route) #qlg-quarters-control-wrap,
.topbar-views-panel[data-view]:not([data-view="yearly-long-term-goals"]) #ylg-years-control-wrap,
.topbar-views-panel[data-view]:not([data-view="goals-dashboard"]) #gd-columns-control-wrap,
/* …and the capture-mode rows (daily_capture_mode_control.js) only to the Daily
   Goals tab, whose column layout is what the choice visibly changes. */
.topbar-views-panel[data-view]:not([data-view="daily-long-term-goals"]) #dlg-capture-mode-wrap {
  display: none;
}

/* On Reflect the row is chosen by the SCALE on screen, stamped by
   `reflect_view.js` when it mounts a grid. Each scale reflects one board and
   shares that board's window, so it wants that board's row and no other:
   a week of days → Days, a month of weeks → Weeks, and so on up. The yearly
   row never appears because Reflect has no year-of-years grid — the year is the
   top of the goal chain. */
.topbar-views-panel.rfl-route:not([data-reflect-scale="daily"]) #dlg-days-control-wrap,
.topbar-views-panel.rfl-route:not([data-reflect-scale="weekly"]) #wlg-weeks-control-wrap,
.topbar-views-panel.rfl-route:not([data-reflect-scale="monthly"]) #mlg-months-control-wrap,
.topbar-views-panel.rfl-route:not([data-reflect-scale="quarterly"]) #qlg-quarters-control-wrap {
  display: none;
}

/* One labelled group of pick-one-of-N rows, set off from the switches above it
   so the radios do not read as three more independent toggles. */
#dlg-capture-mode-wrap {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border-subtle);
  /* Last in the panel whatever order the controls mounted in — the tab mints
     this group before the "Days" row, and a divided group wedged between two
     plain rows reads as a divider in the wrong place. */
  order: 1;
}

.dlg-capture-mode-heading {
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  opacity: 0.75;
}

/* Container for the dashboard's five column-toggle rows — matches the panel's
   own vertical rhythm. */
.gd-columns-control {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.topbar-views-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 12px;
  cursor: pointer;
  user-select: none;
  color: var(--text-primary);
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--border-subtle);
  white-space: nowrap;
}

.topbar-views-summary::-webkit-details-marker {
  display: none;
}

.topbar-views-summary::after {
  content: "▾";
  font-size: 11px;
  color: var(--text-secondary);
}

.topbar-views-wrap[open] .topbar-views-summary::after {
  content: "▴";
}

.topbar-views-panel {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 220px;
  padding: 10px;
  border-radius: 14px;
  border: 1px solid var(--border-subtle);
  background: var(--panel-bg, rgba(18, 24, 38, 0.98));
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.24);
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 20;
}

.topbar-view-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* 🔴 `display: flex` above BEATS the `hidden` attribute, which is only ever a
   UA `display: none` — so a row that hides itself with `el.hidden = true` (the
   Accountability row does, while you are not a member) would stay on screen.
   Stated once, for the class, rather than per row. */
.topbar-view-option[hidden] {
  display: none;
}

.topbar-reflection-label {
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
}

.topbar-reflection-row {
  position: relative;
  cursor: pointer;
}

.topbar-reflection-row input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.topbar-toggle {
  display: block;
  width: 44px;
  height: 22px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.12);
  position: relative;
  transition: background 0.2s ease;
}

.topbar-toggle::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--text-primary);
  transition: transform 0.2s ease;
}

.topbar-reflection-row input:checked + .topbar-toggle {
  background: var(--tab-active-border);
}

.topbar-reflection-row input:checked + .topbar-toggle::after {
  transform: translateX(22px);
}

body.theme-light .topbar-toggle {
  background: rgba(15, 23, 42, 0.12);
}

body.theme-light .topbar-views-summary {
  background: rgba(15, 23, 42, 0.05);
}

body.theme-light .topbar-views-panel {
  background: rgba(255, 255, 255, 0.98);
}

/* The goal HORIZON picker that was styled here — a `<details>` of five routes,
   paired with an undated-scope twin under `.topbar-goal-controls` — went on
   2026-08-08 with both islands. The horizon is the navigation rail's five
   letters plus its Goal Tree row (`styles/nav-rail.css`); the topbar's copy was
   the same choice in a second shape a few pixels away, and the scope half asked
   a storage question in a navigation control's clothes. */

/* The "Sidebar" dropdown that used to be styled here — a popover of rows, each
   with a pin toggle deciding whether that panel ALSO kept a topbar button — is
   the right-hand rail now: `styles/panel-rail.css`, built by
   js/sidebar/panel_rail.js. Pinning went with it, and so did `.is-unpinned`:
   every panel toggle is hidden from the topbar while the rail is on screen. */

/* Reflection view placeholder goal list */
.reflection-placeholder-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  list-style: none;
  padding: 0;
  margin: 0;
  border: 0;
}

.reflection-placeholder-list:empty {
  display: none;
}

.reflection-placeholder-list .goal-item--reflection-placeholder .goal-text {
  opacity: 0.5;
  cursor: default;
}

.reflection-placeholder-list .goal-item--reflection-placeholder .goal-drag,
.reflection-placeholder-list .goal-item--reflection-placeholder .goal-check {
  pointer-events: none;
  opacity: 0.25;
}
