/**
 * Design tokens from style.md and components.md.
 * Mirrors variables.js — import once, then use var(--token-name).
 *
 * @example
 * @import "./variables.css";
 * .title { font-size: var(--font-view-title-size); }
 */

:root {
  /* Font family (shared) */
  --font-family: "SF Pro", "Inter", system-ui, sans-serif;

  /* Colors — style.md */
  --color-background: #214d93;
  --color-secondary: rgba(0, 145, 255, 0.6);
  --color-secondary-hex: #0091ff;
  --color-secondary-opacity: 0.6;
  --color-third: rgba(0, 145, 255, 0.4);
  --color-third-hex: #0091ff;
  --color-third-opacity: 0.4;
  --color-accent: #febc2e;
  --color-text: #f4f4f4;
  --color-text-secondary: rgba(255, 255, 255, 0.7);
  --color-text-secondary-hex: #ffffff;
  --color-text-secondary-opacity: 0.7;
  --color-extra: #131313;
}

/* Dark mode — deeper charcoal surfaces, same accent/text. Toggled from
   Settings > Preferences (localStorage `pathora.darkMode`); applied as
   data-theme="dark" on <html>. Default :root above stays the Pathora
   brand blue. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --color-background: #0b1220;
  --color-secondary: rgba(56, 90, 140, 0.55);
  --color-secondary-hex: #385a8c;
  --color-secondary-opacity: 0.55;
  --color-third: rgba(56, 90, 140, 0.35);
  --color-third-hex: #385a8c;
  --color-third-opacity: 0.35;
  --color-accent: #febc2e;
  --color-text: #f4f4f4;
  --color-text-secondary: rgba(255, 255, 255, 0.7);
  --color-text-secondary-hex: #ffffff;
  --color-text-secondary-opacity: 0.7;
  --color-extra: #070a10;
}

:root {
  /* Typography — viewTitle */
  --font-view-title-family: var(--font-family);
  --font-view-title-weight: 700;
  --font-view-title-size: 34px;
  --font-view-title-line-height: 41px;
  --font-view-title-letter-spacing: 0.4px;
  --font-view-title-align: left;
  --font-view-title-color: var(--color-text);

  /* Typography — tabBar */
  --font-tab-bar-family: var(--font-family);
  --font-tab-bar-weight: 500;
  --font-tab-bar-size: 15px;
  --font-tab-bar-line-height: 20px;
  --font-tab-bar-letter-spacing: -0.23px;
  --font-tab-bar-align: center;
  --font-tab-bar-color: var(--color-text);

  /* Typography — rowTitle */
  --font-row-title-family: var(--font-family);
  --font-row-title-weight: 400;
  --font-row-title-size: 17px;
  --font-row-title-line-height: 22px;
  --font-row-title-letter-spacing: -0.43px;
  --font-row-title-align: left;
  --font-row-title-color: var(--color-text);

  /* Typography — rowSubtitle */
  --font-row-subtitle-family: var(--font-family);
  --font-row-subtitle-weight: 400;
  --font-row-subtitle-size: 15px;
  --font-row-subtitle-line-height: 20px;
  --font-row-subtitle-letter-spacing: -0.23px;
  --font-row-subtitle-align: left;
  --font-row-subtitle-color: var(--color-text-secondary);

  /* Typography — sectionTitle (superseded viewSubtitle, which went unused) */
  --font-section-title-family: var(--font-family);
  --font-section-title-weight: 600;
  --font-section-title-size: 13px;
  --font-section-title-line-height: 18px;
  --font-section-title-letter-spacing: 0.6px;
  --font-section-title-align: left;
  --font-section-title-color: var(--color-text-secondary);
  --font-section-title-transform: uppercase;

  /* Typography — sectionHelper, a short helper line right under a
     sectionTitle, e.g. explaining that Phases expand -- the same idea as
     the helper text SwiftUI adds below a control. Smaller and unstyled (no
     uppercase/letter spacing) next to sectionTitle, so it reads as a
     caption, not a label */
  --font-section-helper-family: var(--font-family);
  --font-section-helper-weight: 400;
  --font-section-helper-size: 12px;
  --font-section-helper-line-height: 16px;
  --font-section-helper-align: left;
  --font-section-helper-color: var(--color-text-secondary);

  /* Typography — lightBold */
  --font-light-bold-weight: 600;

  /* Typography — addNewButton */
  --font-add-new-button-family: var(--font-family);
  --font-add-new-button-weight: 400;
  --font-add-new-button-size: 17px;
  --font-add-new-button-line-height: 22px;
  --font-add-new-button-letter-spacing: -0.43px;
  --font-add-new-button-align: left;
  --font-add-new-button-color: var(--color-text);

  /* Containers — components.md */
  --container-view-display: flex;
  --container-view-direction: column;
  --container-view-align: center;
  --container-view-justify: flex-start;
  --container-view-gap: 5.8vh;
  --container-view-padding-top: 1.3vh;
  --container-view-overflow: hidden;

  --container-body-display: flex;
  --container-body-direction: column;
  --container-body-height: 81.8vh;
  --container-body-width: 86.1vw;
  --container-body-align: flex-start;
  --container-body-justify: flex-start;
  --container-body-gap: 4.1vh;
  --container-body-padding-top: 1.3vh;
  --container-body-overflow: hidden;

  --container-list-display: flex;
  --container-list-direction: column;
  --container-list-height: fit-content;
  --container-list-max-height: 67.67vh;
  --container-list-width: 100%;
  --container-list-align: center;
  --container-list-justify: flex-start;
  --container-list-gap: 0;
  --container-list-padding: 0;
  --container-list-overflow: hidden;
  --container-list-border-radius: 30px;

  --container-leading-layout: vertical;
  --container-leading-display: flex;
  --container-leading-direction: column;
  --container-leading-height: 4.58vh;
  --container-leading-width: fit-content;
  --container-leading-max-width: 100%;
  --container-leading-align: flex-start;
  --container-leading-justify: center;
  --container-leading-gap: 0.29vh;
  --container-leading-padding: 0;
  --container-leading-overflow: hidden;

  --container-sheet-display: flex;
  --container-sheet-direction: column;
  --container-sheet-height: 90vh;
  --container-sheet-width: 100vw;
  --container-sheet-align: flex-start;
  --container-sheet-justify: flex-start;
  --container-sheet-gap: 4.1vh;
  --container-sheet-padding: 0 6.94vw;
  --container-sheet-overflow: hidden;
  --container-sheet-background: var(--color-background);
  --container-sheet-border-top-left-radius: 30px;
  --container-sheet-border-top-right-radius: 30px;

  --container-canvas-display: flex;
  --container-canvas-direction: column;
  --container-canvas-height: 81.8vh;
  --container-canvas-width: 86.1vw;
  --container-canvas-align: flex-start;
  --container-canvas-justify: flex-start;
  --container-canvas-gap: 0;
  --container-canvas-padding: 0;
  --container-canvas-overflow: hidden;

  --container-toolbar-display: flex;
  --container-toolbar-direction: row;
  --container-toolbar-height: 4.85vh;
  --container-toolbar-width: 100%;
  --container-toolbar-align: center;
  --container-toolbar-justify: flex-start;
  --container-toolbar-gap: 0;
  --container-toolbar-padding: 0;
  --container-toolbar-overflow: hidden;

  --container-blocks-display: flex;
  --container-blocks-direction: row;
  --container-blocks-height: 9.7vh;
  --container-blocks-width: 100%;
  --container-blocks-align: center;
  --container-blocks-justify: flex-start;
  --container-blocks-gap: 0;
  --container-blocks-padding: 0;
  --container-blocks-overflow: hidden;

  --container-pages-display: flex;
  --container-pages-direction: row;
  --container-pages-height: 6.8vh;
  --container-pages-width: 100%;
  --container-pages-align: center;
  --container-pages-justify: flex-start;
  --container-pages-gap: 0;
  --container-pages-padding: 0;
  --container-pages-overflow: hidden;

  --container-canvas-workspace-display: flex;
  --container-canvas-workspace-direction: row;
  --container-canvas-workspace-flex: 1 1 auto;
  --container-canvas-workspace-min-height: 0;
  --container-canvas-workspace-height: 100%;
  --container-canvas-workspace-width: 100%;
  --container-canvas-workspace-align: flex-start;
  --container-canvas-workspace-justify: flex-start;
  --container-canvas-workspace-gap: 0.69vw;
  --container-canvas-workspace-padding: 0;
  --container-canvas-workspace-overflow: hidden;

  --container-outline-display: flex;
  --container-outline-direction: column;
  --container-outline-flex-shrink: 0;
  --container-outline-width: 24vw;
  --container-outline-height: 100%;
  --container-outline-align: flex-start;
  --container-outline-justify: flex-start;
  --container-outline-gap: 0;
  --container-outline-padding: 0;
  --container-outline-overflow: hidden;
  --container-outline-background: var(--color-third);
  --container-outline-border-radius: 30px;

  --container-outline-list-display: flex;
  --container-outline-list-direction: column;
  --container-outline-list-flex: 1 1 auto;
  --container-outline-list-min-height: 0;
  --container-outline-list-width: 100%;
  --container-outline-list-align: flex-start;
  --container-outline-list-justify: flex-start;
  --container-outline-list-gap: 0;
  --container-outline-list-padding: 0;
  --container-outline-list-overflow: auto;

  --container-authoring-display: flex;
  --container-authoring-direction: column;
  --container-authoring-flex: 1 1 auto;
  --container-authoring-min-width: 0;
  --container-authoring-height: 100%;
  --container-authoring-width: auto;
  --container-authoring-align: flex-start;
  --container-authoring-justify: flex-start;
  --container-authoring-gap: 0;
  --container-authoring-padding: 0;
  --container-authoring-overflow: hidden;

  --animation-sheet-duration: 0.32s;
  --animation-sheet-easing: cubic-bezier(0.4, 0, 0.2, 1);
  --animation-sheet-transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
  --animation-sheet-backdrop-background: rgba(0, 0, 0, 0.4);
  --animation-sheet-backdrop-transition: opacity 0.32s cubic-bezier(0.4, 0, 0.2, 1),
    visibility 0.32s cubic-bezier(0.4, 0, 0.2, 1);

  /* Detail view (push navigation) — reuses the sheet's duration/easing */
  --component-detail-view-z-index: 90;
  --component-detail-view-background: var(--color-background);
  --component-detail-view-back-gap: 0.35vw;
  --animation-detail-view-transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1),
    visibility 0.32s cubic-bezier(0.4, 0, 0.2, 1);

  /* Components — tabBar */
  --component-tab-bar-display: flex;
  --component-tab-bar-direction: row;
  --component-tab-bar-height: 4.3vh;
  --component-tab-bar-width: fit-content;
  --component-tab-bar-max-width: 86.1vw;
  --component-tab-bar-align: center;
  --component-tab-bar-justify: center;
  --component-tab-bar-gap: 0;
  --component-tab-bar-padding: 0.39vh 0.27vw;
  --component-tab-bar-background: var(--color-secondary);
  --component-tab-bar-border-radius: 30px;

  /* Components — secondaryTabBar */
  --component-secondary-tab-bar-display: flex;
  --component-secondary-tab-bar-direction: row;
  --component-secondary-tab-bar-height: 4.3vh;
  --component-secondary-tab-bar-width: fit-content;
  --component-secondary-tab-bar-max-width: 86.1vw;
  --component-secondary-tab-bar-align: center;
  --component-secondary-tab-bar-justify: center;
  --component-secondary-tab-bar-gap: 0;
  --component-secondary-tab-bar-padding: 0.39vh 0.27vw;
  --component-secondary-tab-bar-background: var(--color-third);
  --component-secondary-tab-bar-border-radius: 30px;

  /* Components — tab */
  --component-tab-display: flex;
  --component-tab-direction: row;
  --component-tab-height: 3.5vh;
  --component-tab-width: fit-content;
  --component-tab-align: center;
  --component-tab-justify: center;
  --component-tab-gap: 0.7vw;
  --component-tab-padding: 0.78vh 1.94vw;
  --component-tab-border-radius: 30px;
  --component-tab-white-space: nowrap;
  --component-tab-background-standby: transparent;
  --component-tab-background-active: var(--color-background);

  /* Components — row */
  --component-row-display: flex;
  --component-row-direction: row;
  --component-row-height: 6.64vh;
  --component-row-width: 86.1vw;
  --component-row-align: center;
  --component-row-justify: flex-start;
  --component-row-gap: 0;
  --component-row-padding: 0;
  --component-row-background: var(--color-secondary);
  --component-row-border-top-width: 2px;
  --component-row-border-top-color: var(--color-background);
  --component-row-border-top: 2px solid var(--color-background);
  --component-row-cursor: pointer;
  --component-row-hover-filter: brightness(1.06);
  --component-row-active-filter: brightness(0.97);
  --component-row-transition: background-color 0.2s ease, filter 0.2s ease;
  --component-row-active-transition-duration: 0.1s;
  --component-row-chevron-color: var(--color-text);
  --component-row-chevron-transition: transform 0.2s ease;
  --component-row-chevron-hover-translate-x: 0.15vw;
  --component-row-focus-outline: 2px solid var(--color-text);

  /* Components — rowNew */
  --component-row-new-display: flex;
  --component-row-new-direction: row;
  --component-row-new-height: 5.64vh;
  --component-row-new-width: 86.1vw;
  --component-row-new-align: center;
  --component-row-new-justify: center;
  --component-row-new-gap: 0;
  --component-row-new-padding: 0;
  --component-row-new-background: transparent;
  --component-row-new-color: var(--color-text-secondary);
  --component-row-new-border: 2px dotted var(--color-text-secondary);
  --component-row-new-border-bottom-left-radius: 30px;
  --component-row-new-border-bottom-right-radius: 30px;
  --component-row-new-cursor: pointer;
  --component-row-new-transition: color 0.2s ease, border-color 0.2s ease;
  --component-row-new-hover-color: var(--color-text);
  --component-row-new-hover-border-color: var(--color-text);

  /* Components — outlineModule */
  --component-outline-module-display: flex;
  --component-outline-module-direction: column;
  --component-outline-module-width: 100%;
  --component-outline-module-gap: 0;
  --component-outline-module-padding: 0;
  --component-outline-module-overflow: hidden;

  --component-outline-module-row-display: flex;
  --component-outline-module-row-direction: row;
  --component-outline-module-row-height: 5.5vh;
  --component-outline-module-row-width: 100%;
  --component-outline-module-row-align: center;
  --component-outline-module-row-justify: flex-start;
  --component-outline-module-row-gap: 0;
  --component-outline-module-row-padding: 0;
  --component-outline-module-row-background: var(--color-secondary);
  --component-outline-module-row-border-top: 2px solid var(--color-background);
  --component-outline-module-row-cursor: pointer;
  --component-outline-module-row-transition: background-color 0.2s ease, filter 0.2s ease;
  --component-outline-module-row-hover-filter: brightness(1.06);
  --component-outline-module-row-active-filter: brightness(0.97);

  --component-outline-module-lessons-display: flex;
  --component-outline-module-lessons-direction: column;
  --component-outline-module-lessons-width: 100%;
  --component-outline-module-lessons-gap: 0;
  --component-outline-module-lessons-padding: 0;
  --component-outline-module-lessons-overflow: hidden;

  /* Components — outlineLesson */
  --component-outline-lesson-display: flex;
  --component-outline-lesson-direction: row;
  --component-outline-lesson-height: 4.8vh;
  --component-outline-lesson-width: 100%;
  --component-outline-lesson-align: center;
  --component-outline-lesson-justify: flex-start;
  --component-outline-lesson-gap: 0;
  --component-outline-lesson-padding: 0 0.68vw 0 1.94vw;
  --component-outline-lesson-background-standby: transparent;
  --component-outline-lesson-background-active: var(--color-background);
  --component-outline-lesson-cursor: pointer;
  --component-outline-lesson-transition: background-color 0.2s ease, filter 0.2s ease;
  --component-outline-lesson-hover-filter: brightness(1.06);
  --component-outline-lesson-active-filter: brightness(0.97);
  --component-outline-lesson-focus-outline: 2px solid var(--color-text);

  /* Components — outlineAddModule / outlineAddLesson (rowNew adapted to outline width) */
  --component-outline-add-module-height: 5vh;
  --component-outline-add-lesson-height: 4.5vh;
  --component-outline-add-width: 100%;
  --component-outline-add-border-radius: 0;
  --component-outline-add-module-border-bottom-left-radius: 30px;
  --component-outline-add-module-border-bottom-right-radius: 30px;

  /* Components — newUserForm */
  --component-new-user-form-display: grid;
  --component-new-user-form-width: 86.1vw;
  --component-new-user-form-column-gap: 2.08vw;
  --component-new-user-form-row-gap: 2.92vh;
  --component-new-user-form-padding: 2.92vh 2.08vw;
  --component-new-user-form-border-radius: 30px;
  --component-new-user-form-columns: calc(
      (100% - var(--component-new-user-form-column-gap)) / 2
    )
    calc(
      (100% - var(--component-new-user-form-column-gap)) / 2
    );
  --component-new-user-form-rows: repeat(3, auto);
  --component-new-user-form-background: var(--color-secondary);
  --component-new-user-form-field-background: var(--color-background);
  --component-new-user-form-field-padding: 0 2.08vw;
  --component-new-user-form-field-min-height: 4.5vh;
  --component-new-user-form-field-border-radius: 30px;
  --component-new-user-form-input-color: var(--color-text);
  --component-new-user-form-input-padding: 1.5vh 0;
  --component-new-user-form-input-min-height: 100%;
  --component-new-user-form-input-placeholder-color: var(--color-text-secondary);

  /* Components — checkbox (Deadline? toggle in the New Task wizard) */
  --component-checkbox-size: 20px;
  --component-checkbox-border-radius: 6px;
  --component-checkbox-border: 2px solid var(--color-text-secondary);
  --component-checkbox-checked-background: var(--color-accent);
  --component-checkbox-checked-color: var(--color-extra);

  /* Components — completeToggle (a Task row's "mark complete" circle,
     and the read-only version of it a Milestone row shows once every
     one of its Tasks is done -- see createTaskCompletionToggle/
     createMilestoneCompletionIndicator in public/views/projects.js).
     Same tokens as checkbox above, just circular -- this reads as
     "check this item off", not a plain on/off form field. */
  --component-complete-toggle-size: 20px;
  --component-complete-toggle-border: 2px solid var(--color-text-secondary);
  --component-complete-toggle-checked-background: var(--color-accent);
  --component-complete-toggle-checked-color: var(--color-extra);

  /* Components — calendarPanel (the New Task wizard's Due date picker,
     standing in for a native <input type="date">'s OS-drawn popup) */
  --component-calendar-nav-button-size: 3.2vh;
  --component-calendar-nav-button-color: var(--color-text);
  --component-calendar-day-size: 4.4vh;
  --component-calendar-day-border-radius: 50%;
  --component-calendar-day-color: var(--color-text);
  --component-calendar-day-muted-color: var(--color-text-secondary);
  --component-calendar-today-border: 2px solid var(--color-text-secondary);
  --component-calendar-selected-background: var(--color-accent);
  --component-calendar-selected-color: var(--color-extra);

  /* Components — gantt (Gantt Chart tab's timeline grid, see
     public/views/gantt-chart.js). A single scrolling grid with two frozen
     ("sticky") edges -- the outline label column on the left, the date
     scale on top -- so scrolling either axis keeps whichever one isn't
     being scrolled in place, without any JS scroll-syncing. cellWidth is
     a px value, not vw/vh like the rest of this file -- unlike the rest
     of this design system, which sizes to the viewport for a fixed
     single-screen app, a day column's width is decided by its own
     content (staying a stable, readable size), not by the viewport --
     the viewport only decides how much of the resulting grid is visible
     before it scrolls. */
  --component-gantt-outline-width: 22vw;
  --component-gantt-row-height: 5vh;
  --component-gantt-cell-width: 56px;
  --component-gantt-indent-step: 1.8vw;
  --component-gantt-background: var(--color-third);
  --component-gantt-border-radius: 30px;
  --component-gantt-seam: 2px solid var(--color-background);
  --component-gantt-outline-padding: 0 0.68vw;
  --component-gantt-phase-background: var(--color-third);
  --component-gantt-phase-accent-border: 4px solid var(--color-accent);
  /* A Milestone spans the full grid width as its own left-aligned
     divider bar (see .gantt-milestone-divider) -- backgroundColor
     itself (not a tint) is what reads as a distinct band against the
     card's own thirdColor fill, with a left border marking it the same
     way a Phase's own stripe does, just in a muted color instead of
     accentColor so the two are never confused. */
  --component-gantt-milestone-border-color: var(--color-text-secondary);
  --component-gantt-milestone-border: 2px solid var(--component-gantt-milestone-border-color);
  --component-gantt-lane-line-color: rgba(255, 255, 255, 0.12);
  --component-gantt-scale-color: var(--color-text-secondary);
  --component-gantt-today-color: var(--color-accent);
  --component-gantt-bar-height: 2.6vh;
  --component-gantt-bar-color: var(--color-accent);
  --component-gantt-bar-radius: 10px;
  --component-gantt-bar-inset: 3px;
  /* A completed Task's bar/pin (task.status === "done") -- same accent
     hue, tinted down to a low-opacity fill rather than swapped for an
     unrelated color, the same tinting technique --color-secondary/
     --color-third already use for their own muted surfaces. Reads as
     "this one's done, no longer needs attention" against the still-
     vivid solid accentColor every unfinished bar/pin keeps using. */
  --component-gantt-bar-complete-color: rgba(254, 188, 46, 0.32);
  --component-gantt-pin-size: 10px;
  --component-gantt-pin-color: var(--color-accent);
  --component-gantt-pin-complete-color: rgba(254, 188, 46, 0.32);

  /* Components — reportStats (Reports tab detail view's plain summary
     tiles -- see public/views/reports.js). A 2x2 grid of number+label
     tiles above the Milestone 2 breakdown charts. Reuses the same
     third/secondary fills and 30px radius every other filled surface
     in this app already uses (board columns, list containers). */
  --component-report-stats-display: grid;
  --component-report-stats-columns: repeat(2, minmax(0, 1fr));
  --component-report-stats-gap: 1.4vw;
  --component-report-stats-width: 100%;
  --component-report-stats-padding-bottom: 0;
  --component-report-stat-display: flex;
  --component-report-stat-direction: column;
  --component-report-stat-align: flex-start;
  --component-report-stat-justify: flex-start;
  --component-report-stat-gap: 0.6vh;
  --component-report-stat-padding: 2vh 1.4vw;
  --component-report-stat-background: var(--color-third);
  --component-report-stat-border-radius: 30px;
  --component-report-stat-value-color: var(--color-text);
  --component-report-stat-label-color: var(--color-text-secondary);

  /* Components — reportCharts (Reports tab breakdown bars under the
     summary tiles). Bars are plain divs sized with --report-bar-width,
     same approach .gantt-bar uses for --gantt-bar-width — no charting
     library. Track uses thirdColor; fill uses accentColor. */
  --component-report-charts-display: flex;
  --component-report-charts-direction: column;
  --component-report-charts-gap: 2.4vh;
  --component-report-charts-width: 100%;
  --component-report-charts-padding-bottom: 2vh;
  --component-report-chart-display: flex;
  --component-report-chart-direction: column;
  --component-report-chart-gap: 1.2vh;
  --component-report-chart-width: 100%;
  --component-report-chart-padding: 1.6vh 1.4vw;
  --component-report-chart-background: var(--color-third);
  --component-report-chart-border-radius: 30px;
  --component-report-bar-row-display: grid;
  --component-report-bar-row-columns: 12vw minmax(0, 1fr) 8vw;
  --component-report-bar-row-align: center;
  --component-report-bar-row-gap: 1vw;
  --component-report-bar-row-width: 100%;
  --component-report-bar-track-height: 1.6vh;
  --component-report-bar-track-background: var(--color-secondary);
  --component-report-bar-track-border-radius: 10px;
  --component-report-bar-fill-height: 100%;
  /* Resolved on the fill element itself (where JS sets --report-bar-width),
     not baked at :root — routing through a :root token made every bar 0%. */
  --component-report-bar-fill-background: var(--color-accent);
  --component-report-bar-fill-border-radius: 10px;
  --component-report-bar-label-color: var(--color-text);
  --component-report-bar-value-color: var(--color-text-secondary);
  --component-report-bar-value-align: right;

  /* Components — reportDonut / reportMode (Milestone 3) */
  --component-report-donut-size: 140px;
  --component-report-donut-gap: 1.4vw;
  --component-report-donut-center-size: 22px;
  --component-report-donut-center-line-height: 26px;
  --component-report-swatch-size: 0.9vh;
  --component-report-swatch-radius: 3px;
  --component-report-breakdown-display: grid;
  --component-report-breakdown-columns: minmax(0, 18vw) minmax(0, 1fr);
  --component-report-breakdown-gap: 1.6vw;
  --component-report-breakdown-align: center;

  /* Components — leading (inside row) */
  --component-leading-display: flex;
  --component-leading-direction: column;
  --component-leading-height: 100%;
  --component-leading-width: 70%;
  --component-leading-align: flex-start;
  --component-leading-justify: center;
  --component-leading-gap: 0.3vh;
  --component-leading-padding: 0.97vh 0.68vw;
  --component-leading-background: transparent;

  /* Components — trailing (inside row) */
  --component-trailing-display: flex;
  --component-trailing-direction: row;
  --component-trailing-height: 100%;
  --component-trailing-width: 30%;
  --component-trailing-align: center;
  --component-trailing-justify: flex-end;
  --component-trailing-gap: 2.92vw;
  --component-trailing-padding: 0.97vh 2.77vw 0.97vh 0.69vw;
  --component-trailing-background: transparent;

  /* Tab indicator animation */
  --animation-tab-indicator-duration: 0.28s;
  --animation-tab-indicator-easing: cubic-bezier(0.4, 0, 0.2, 1);
  --animation-tab-indicator-transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1),
    top 0.28s cubic-bezier(0.4, 0, 0.2, 1),
    width 0.28s cubic-bezier(0.4, 0, 0.2, 1),
    height 0.28s cubic-bezier(0.4, 0, 0.2, 1);

  /* Micro-animations — list entrance, icon actions, tab press */
  --animation-list-enter-duration: 0.45s;
  --animation-list-enter-easing: cubic-bezier(0.22, 1, 0.36, 1);
  --animation-list-enter-distance: 1.4vh;
  --animation-list-enter-stagger: 0.05s;
  --animation-title-enter-duration: 0.5s;
  --animation-icon-transition: transform 0.2s ease, opacity 0.2s ease;
  --animation-icon-hover-scale: 1.12;
  --animation-icon-active-scale: 0.92;
  --animation-tab-press-scale: 0.96;
  --animation-tab-press-transition: transform 0.15s ease;

  /* Kanban board card drop -- a direct result of the user's own drag/tap,
     so it uses the tab indicator's snappier timing (0.28s) rather than the
     list entrance's slower, staggered one (0.45s), which is meant for
     several rows appearing at once on a page load the user didn't
     trigger themselves. */
  --animation-card-drop-duration: 0.28s;
  --animation-card-drop-easing: cubic-bezier(0.4, 0, 0.2, 1);

  --animation-outline-chevron-transition: transform 0.2s ease;
  --animation-outline-chevron-rotation-collapsed: 0deg;
  --animation-outline-chevron-rotation-expanded: 90deg;
}

/* Typography utilities */
.font-view-title {
  font-family: var(--font-view-title-family);
  font-weight: var(--font-view-title-weight);
  font-size: var(--font-view-title-size);
  line-height: var(--font-view-title-line-height);
  letter-spacing: var(--font-view-title-letter-spacing);
  text-align: var(--font-view-title-align);
  color: var(--font-view-title-color);
}

.font-tab-bar {
  font-family: var(--font-tab-bar-family);
  font-weight: var(--font-tab-bar-weight);
  font-size: var(--font-tab-bar-size);
  line-height: var(--font-tab-bar-line-height);
  letter-spacing: var(--font-tab-bar-letter-spacing);
  text-align: var(--font-tab-bar-align);
  color: var(--font-tab-bar-color);
}

.font-row-title {
  font-family: var(--font-row-title-family);
  font-weight: var(--font-row-title-weight);
  font-size: var(--font-row-title-size);
  line-height: var(--font-row-title-line-height);
  letter-spacing: var(--font-row-title-letter-spacing);
  text-align: var(--font-row-title-align);
  color: var(--font-row-title-color);
}

.font-row-subtitle {
  font-family: var(--font-row-subtitle-family);
  font-weight: var(--font-row-subtitle-weight);
  font-size: var(--font-row-subtitle-size);
  line-height: var(--font-row-subtitle-line-height);
  letter-spacing: var(--font-row-subtitle-letter-spacing);
  text-align: var(--font-row-subtitle-align);
  color: var(--font-row-subtitle-color);
}

.font-section-title {
  font-family: var(--font-section-title-family);
  font-weight: var(--font-section-title-weight);
  font-size: var(--font-section-title-size);
  line-height: var(--font-section-title-line-height);
  letter-spacing: var(--font-section-title-letter-spacing);
  text-align: var(--font-section-title-align);
  color: var(--font-section-title-color);
  text-transform: var(--font-section-title-transform);
}

/* Collapses to nothing when left empty (e.g. the Milestone Task view,
   which has no helper text) instead of leaving a stray gap from
   sectionHeader's own gap between its children. */
.font-section-helper {
  font-family: var(--font-section-helper-family);
  font-weight: var(--font-section-helper-weight);
  font-size: var(--font-section-helper-size);
  line-height: var(--font-section-helper-line-height);
  text-align: var(--font-section-helper-align);
  color: var(--font-section-helper-color);
}

.font-section-helper:empty {
  display: none;
}

.font-add-new-button {
  font-family: var(--font-add-new-button-family);
  font-weight: var(--font-add-new-button-weight);
  font-size: var(--font-add-new-button-size);
  line-height: var(--font-add-new-button-line-height);
  letter-spacing: var(--font-add-new-button-letter-spacing);
  text-align: var(--font-add-new-button-align);
  color: var(--font-add-new-button-color);
}

/* Container utilities */
.view-container {
  display: var(--container-view-display);
  flex-direction: var(--container-view-direction);
  align-items: var(--container-view-align);
  justify-content: var(--container-view-justify);
  gap: var(--container-view-gap);
  padding-top: var(--container-view-padding-top);
  overflow: var(--container-view-overflow);
}

.body-container {
  display: var(--container-body-display);
  flex-direction: var(--container-body-direction);
  height: var(--container-body-height);
  width: var(--container-body-width);
  align-items: var(--container-body-align);
  justify-content: var(--container-body-justify);
  gap: var(--container-body-gap);
  padding-top: var(--container-body-padding-top);
  overflow: var(--container-body-overflow);
}

/* Wraps a sectionHeader (sectionTitle + sectionHelper) and the
   listContainer beneath it as one unit, with a tight gap between the two --
   like a SwiftUI List Section header hugging its rows. This is what gets
   the bodyContainer's normal gap from the viewTitle above, not the
   sectionHeader itself. */
.list-section {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.8vh;
  width: 100%;
}

/* Groups a sectionTitle with its sectionHelper right underneath it, with a
   much tighter gap than listSection uses before the rows themselves --
   the helper reads as a caption on the title above it, not another
   section of its own. */
.section-header {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.2vh;
  width: 100%;
}

.list-container {
  display: var(--container-list-display);
  flex-direction: var(--container-list-direction);
  height: var(--container-list-height);
  max-height: var(--container-list-max-height);
  width: var(--container-list-width);
  align-items: var(--container-list-align);
  justify-content: var(--container-list-justify);
  gap: var(--container-list-gap);
  padding: var(--container-list-padding);
  overflow: var(--container-list-overflow);
  border-radius: var(--container-list-border-radius);
}

/* Wraps a Project's Phases when it has any -- each Phase gets its own
   .list-container (header row, plus its Milestones underneath when
   expanded) instead of every Phase sharing one long list, so each Phase
   reads as its own separate card. */
.phase-groups {
  display: flex;
  flex-direction: column;
  gap: 1.6vh;
  width: 100%;
}

/* Expandable Row modifiers -- used for a Project's Phases, each one an
   ordinary Row that expands in place to reveal its Milestones directly
   underneath (Notion toggle-list style) instead of navigating to a new
   view. .row--phase sets the Phase row itself apart from its nested
   Milestone rows -- a distinct background (reusing colors.thirdColor, the
   same tier already used to set outlineContainer apart from the page
   behind it) and a bold title, so the two levels of hierarchy are
   visually distinct, not just structurally nested. Nesting itself is
   conveyed by the shared listContainer and row--phase's background, not
   by extra indent -- a Phase's title and its Milestones' titles share the
   same, more generous Leading padding (see below) so they line up. */
.accordion-chevron {
  color: var(--component-row-chevron-color);
  font-size: 17px;
  line-height: 1;
  transition: var(--animation-outline-chevron-transition);
  transform: rotate(var(--animation-outline-chevron-rotation-collapsed));
}

.row--expanded .accordion-chevron {
  transform: rotate(var(--animation-outline-chevron-rotation-expanded));
}

/* .row.row--phase (not just .row--phase) so this reliably beats .row's own
   background-color further down the file regardless of source order. The
   left accent stripe reuses colors.accentColor -- the same amber already
   used for the active Phase bubble in the New Project wizard -- so a
   Phase reads as a Phase everywhere in the app, not just here, without
   adding a color that means nothing anywhere else. */
.row.row--phase {
  background-color: var(--color-third);
  border-left: 4px solid var(--color-accent);
}

.row--phase .row-leading,
.row--nested .row-leading {
  padding-left: 1.8vw;
}

/* Wraps a Phase's revealed Milestones (see togglePhase in
   public/views/projects.js) so expanding/collapsing is a genuine smooth
   slide -- height and opacity animating together -- instead of the rows
   just popping straight to their full height the instant they're
   inserted or removed. Reuses the sheet/detailView's own duration and
   easing (0.32s, cubic-bezier(0.4, 0, 0.2, 1)) rather than a third
   near-identical "panel sliding" token, since that's exactly what this
   is too. JS drives max-height/opacity directly (not a class toggle),
   since the target height has to be measured per Phase (however many
   Milestones it has) rather than being a fixed value. Not a
   .list-container itself, and not a direct child of one either (it sits
   inside a Phase's own .list-container, after the header row) -- so
   listEnter's ".list-container > .row" selector doesn't reach the rows
   inside it, and they never replay listEnter just because a Phase was
   toggled open again. */
.accordion-panel {
  display: flex;
  flex-direction: column;
  width: 100%;
  overflow: hidden;
  transition: max-height var(--animation-sheet-duration) var(--animation-sheet-easing),
    opacity var(--animation-sheet-duration) var(--animation-sheet-easing);
}

/* New Task wizard options (Deadline?, Due date, Priority, Owner, Show
   every day) -- each its own standalone field, not Rows sharing one
   listContainer, so they read as form fields rather than a navigation
   list. .option-fields stacks them with a real gap between each one
   (unlike Rows, which sit flush against each other); .option-field-group
   is the rounded, clipped card a single field's trigger (and, once
   expanded, its panel) share -- the same role a Phase's own listContainer
   plays for its accordionRow + accordionPanel, just for one field instead
   of a whole list. */
.option-fields {
  display: flex;
  flex-direction: column;
  gap: 1.2vh;
  width: 100%;
}

.option-field-group {
  width: 100%;
  border-radius: 30px;
  overflow: hidden;
}

/* A field's own trigger -- its label on one side, its current value and
   an accordionChevron on the other (Due date, Priority, Owner, Show every
   day), or a checkbox (Deadline?). Carries its own background (rather
   than the group above it) so hovering/pressing it, like any other
   tappable surface in the app, actually has something to darken. */
.option-field {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 5.2vh;
  width: 100%;
  padding: 0 2.08vw;
  background-color: var(--color-secondary);
  box-sizing: border-box;
  cursor: pointer;
  transition: var(--component-row-transition);
}

.option-field:hover {
  filter: var(--component-row-hover-filter);
}

.option-field:active {
  filter: var(--component-row-active-filter);
  transition-duration: var(--component-row-active-transition-duration);
}

.option-field:focus-visible {
  outline: var(--component-row-focus-outline);
  outline-offset: -2px;
}

.option-field-trailing {
  display: flex;
  align-items: center;
  gap: 0.5vw;
}

.option-field-value {
  color: var(--color-text-secondary);
}

/* A field's revealed choices (Priority/Owner/Show every day) or its
   calendarPanel (Due date) -- sits directly beneath the trigger inside
   the same option-field-group, a thin border-top (the same "2px
   backgroundColor" seam a Row uses against the one above it) marking
   where the trigger ends and the choices begin. Its own bottom corners
   round to match option-field-group's -- that group's overflow: hidden
   already clips this panel to the same shape whenever it happens to be
   the group's last child (which it always is here), but a field this
   short makes that 30px clip easy to miss entirely against a flat run of
   choices, reading as square even though it technically isn't. Rounding
   the panel itself removes any doubt. */
.option-field-panel {
  display: flex;
  flex-direction: column;
  width: 100%;
  background-color: var(--color-secondary);
  border-top: 2px solid var(--color-background);
  border-bottom-left-radius: 30px;
  border-bottom-right-radius: 30px;
  box-sizing: border-box;
}

.option-field-choice {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1vh 2.08vw;
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.option-field-choice:hover {
  background-color: var(--color-third);
}

.option-field-choice--selected {
  color: var(--color-accent);
}

/* Deadline?'s on/off toggle -- a plain checkbox rather than a Yes/No
   field like Priority or Show every day, since flipping it also decides
   whether the Due date field beneath it exists at all. */
.checkbox {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--component-checkbox-size);
  height: var(--component-checkbox-size);
  border-radius: var(--component-checkbox-border-radius);
  border: var(--component-checkbox-border);
  box-sizing: border-box;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

.checkbox--checked {
  background-color: var(--component-checkbox-checked-background);
  border-color: var(--component-checkbox-checked-background);
}

.checkbox-icon {
  display: none;
  font-size: 12px;
  line-height: 1;
  color: var(--component-checkbox-checked-color);
}

/* A Task row's "mark complete" circle (createTaskCompletionToggle) and a
   Milestone row's read-only version of the same look
   (createMilestoneCompletionIndicator), both in public/views/projects.js.
   Same shape/states as .checkbox above -- an outline that fills with
   accentColor and grows a checkmark once checked -- just round instead
   of a rounded square, and sized to sit inline with a Row's other
   trailing icons (see .attachment-indicator) rather than a form field's
   own line height. */
.complete-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--component-complete-toggle-size);
  height: var(--component-complete-toggle-size);
  border-radius: 50%;
  border: var(--component-complete-toggle-border);
  box-sizing: border-box;
  background: none;
  padding: 0;
  cursor: pointer;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

/* The static (Milestone row) version is never clicked -- a plain span,
   not a <button>, so this just drops the pointer cursor .checkbox itself
   never had to worry about (a checkbox field's label is the click
   target, not the box). */
.complete-toggle--static {
  cursor: default;
}

.complete-toggle--checked {
  background-color: var(--component-complete-toggle-checked-background);
  border-color: var(--component-complete-toggle-checked-background);
}

.complete-toggle-icon {
  display: none;
  font-size: 11px;
  line-height: 1;
  color: var(--component-complete-toggle-checked-color);
}

.complete-toggle--checked .complete-toggle-icon {
  display: block;
}

/* A single Task row leaving a To-do List in place once it's checked off
   (see removeTodoTaskRow in public/views/todo-lists.js) -- a plain
   opacity fade, not the list's own entrance animation played backwards,
   and scoped to just this one row rather than the whole list re-
   rendering. */
.row--removing {
  transition: opacity 0.2s ease;
  opacity: 0;
  pointer-events: none;
}

.checkbox--checked .checkbox-icon {
  display: block;
}

/* The New Task wizard's Due date picker -- a small calendar living inside
   an open Due date field, standing in for a native <input type="date">'s
   OS-drawn (and entirely unstylable) popup. Its header and weekday row
   span the full field width, but the day grid itself is capped to 7
   fixed-size cells (--component-calendar-day-size each) and centered,
   rather than stretching those cells to fill the field's full width --
   that stretching (via 1fr columns + aspect-ratio) was what made the
   calendar too tall to fit on screen without scrolling. */
.calendar-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.8vh;
  width: 100%;
  padding: 1.2vh 2.08vw 1.6vh;
  box-sizing: border-box;
}

.calendar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.calendar-nav-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--component-calendar-nav-button-size);
  height: var(--component-calendar-nav-button-size);
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 50%;
  background-color: transparent;
  color: var(--component-calendar-nav-button-color);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: background-color 0.2s ease;
}

.calendar-nav-button:hover {
  background-color: var(--color-third);
}

.calendar-month-label {
  text-align: center;
}

.calendar-weekdays,
.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, var(--component-calendar-day-size));
  justify-content: center;
  gap: 0.2vh 0.3vw;
  width: auto;
}

.calendar-weekday {
  text-align: center;
}

.calendar-day {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--component-calendar-day-size);
  height: var(--component-calendar-day-size);
  margin: 0;
  padding: 0;
  border: none;
  border-radius: var(--component-calendar-day-border-radius);
  background-color: transparent;
  color: var(--component-calendar-day-color);
  font-size: 13px;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.calendar-day:hover:not(:disabled) {
  background-color: var(--color-third);
}

.calendar-day--muted {
  color: var(--component-calendar-day-muted-color);
  opacity: 0.4;
  cursor: default;
}

.calendar-day--today {
  border: var(--component-calendar-today-border);
}

.calendar-day--selected,
.calendar-day--selected:hover {
  background-color: var(--component-calendar-selected-background);
  color: var(--component-calendar-selected-color);
}

.leading-container {
  display: var(--container-leading-display);
  flex-direction: var(--container-leading-direction);
  height: var(--container-leading-height);
  width: var(--container-leading-width);
  max-width: var(--container-leading-max-width);
  align-items: var(--container-leading-align);
  justify-content: var(--container-leading-justify);
  gap: var(--container-leading-gap);
  padding: var(--container-leading-padding);
  overflow: var(--container-leading-overflow);
}

.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  background-color: var(--animation-sheet-backdrop-background);
  opacity: 0;
  visibility: hidden;
  transition: var(--animation-sheet-backdrop-transition);
}

.sheet-backdrop.is-open {
  opacity: 1;
  visibility: visible;
}

.sheet-container {
  position: fixed;
  left: 0;
  bottom: 0;
  z-index: 101;
  display: var(--container-sheet-display);
  flex-direction: var(--container-sheet-direction);
  height: var(--container-sheet-height);
  width: var(--container-sheet-width);
  align-items: var(--container-sheet-align);
  justify-content: var(--container-sheet-justify);
  gap: var(--container-sheet-gap);
  padding: var(--container-sheet-padding);
  overflow: var(--container-sheet-overflow);
  background-color: var(--container-sheet-background);
  border-top-left-radius: var(--container-sheet-border-top-left-radius);
  border-top-right-radius: var(--container-sheet-border-top-right-radius);
  box-sizing: border-box;
  transform: translateY(100%);
  transition: var(--animation-sheet-transition);
}

.sheet-container.is-open {
  transform: translateY(0);
}

/* Holds the current wizard step. Fills whatever height is left in the
   sheet after its title and error message, so a step's own scroll area
   (see .wizard-step-content) has a bounded height to scroll within
   instead of being clipped by the sheet's fixed height + hidden overflow. */
.sheet-step-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  overflow: hidden;
}

.detail-view {
  position: fixed;
  inset: 0;
  z-index: var(--component-detail-view-z-index);
  display: var(--container-view-display);
  flex-direction: var(--container-view-direction);
  align-items: var(--container-view-align);
  justify-content: var(--container-view-justify);
  gap: var(--container-view-gap);
  padding-top: var(--container-view-padding-top);
  overflow: var(--container-view-overflow);
  background-color: var(--component-detail-view-background);
  box-sizing: border-box;
  visibility: hidden;
  transform: translateX(100%);
  transition: var(--animation-detail-view-transition);
  height: 100dvh;
}

.detail-view.is-open {
  visibility: visible;
  transform: translateX(0);
}

.detail-view-back {
  display: inline-flex;
  align-items: center;
  gap: var(--component-detail-view-back-gap);
  padding: 0;
  border: none;
  margin: 0;
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: var(--animation-icon-transition);
}

.detail-view-back:hover {
  transform: translateX(calc(var(--component-row-chevron-hover-translate-x) * -1));
}

.detail-view-back-chevron {
  transform: rotate(180deg);
}

/* Wraps Back together with anything else that shares its own top row --
   currently just Menu below, for the Project Detail view (see
   openProjectDetail in public/views/projects.js) -- rather than Back
   sitting alone against bodyContainer's own flex-start, with nowhere
   for a second control to land except stacked underneath it. Plain
   width: 100% + space-between is what pushes that second control all
   the way to the opposite edge from Back. */
.detail-view-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

/* Anchors detailViewMenuPanel below itself -- see that rule's own
   comment on why this floats over the title/list beneath it instead of
   an in-flow expanding panel. */
.detail-view-menu {
  position: relative;
}

.detail-view-menu-button {
  padding: 0;
  border: none;
  margin: 0;
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

/* The first floating (as opposed to in-flow, like optionField's own)
   panel in this app -- a Project Detail view's Menu button opens this
   right-anchored underneath itself (see createHeaderMenu in
   public/views/projects.js), overlaying the title/list rather than
   pushing them down every time it opens. */
/* width: max-content (with min-width as a floor) rather than leaving
   width to auto -- the wrapper this is anchored in (detail-view-menu)
   is only as wide as the kebab button itself, so an auto/shrink-to-fit
   width resolves against that narrow containing block and can wrap
   "Delete Project" instead of sizing to its own content. */
.detail-view-menu-panel {
  position: absolute;
  top: calc(100% + 0.8vh);
  right: 0;
  min-width: 180px;
  width: max-content;
  display: flex;
  flex-direction: column;
  background-color: var(--color-secondary);
  border-radius: 20px;
  box-shadow: 0 0.4vh 1.6vh rgba(0, 0, 0, 0.25);
  padding: 0.4vh;
  box-sizing: border-box;
  z-index: 20;
}

/* Without this, the [hidden] attribute createHeaderMenu toggles does
   nothing -- an author style's display: flex above overrides the UA
   stylesheet's own [hidden] { display: none }, so the panel (and its
   "Delete Project" item) would sit visible under the Menu button at
   all times instead of only after a click. Same fix tab-panel[hidden]
   needed elsewhere in this file. */
.detail-view-menu-panel[hidden] {
  display: none;
}

/* Icon + label, same left-to-right order and baseline alignment
   row-title-row already uses elsewhere (see attachmentIndicator) --
   the icon inherits this button's own color (stroke="currentColor" in
   the markup), so a destructive item's icon and text tint together.
   white-space: nowrap keeps the label on one line regardless -- see
   detailViewMenuPanel's own width: max-content comment. */
.detail-view-menu-item {
  display: flex;
  align-items: center;
  gap: 0.6vw;
  width: 100%;
  text-align: left;
  white-space: nowrap;
  padding: 1vh 1.2vw;
  border: none;
  border-radius: 10px;
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.detail-view-menu-item svg {
  flex-shrink: 0;
}

.detail-view-menu-item:hover {
  background-color: var(--color-background);
}

/* Same accentColor every other primary/confirm action in this app uses
   (see openDeleteListConfirm's own "Delete List" wizard button) rather
   than a dedicated danger color -- no such token exists yet, and this
   app has drawn "this is the thing you're confirming" from context
   (a dedicated confirm sheet, here and there) rather than color so far. */
.detail-view-menu-item--destructive {
  color: var(--color-accent);
}

/* An item that opens a submenu in this same panel (e.g. "Move to
   Section", see createHeaderMenu) instead of acting directly --
   space-between pushes its trailing row-chevron to the opposite edge
   from the label, same "there's more this way" affordance a Row's own
   chevron gives, just reused here instead of introducing a second icon
   convention. */
.detail-view-menu-item--parent {
  justify-content: space-between;
}

/* The "‹ Menu" row atop a submenu (see createHeaderMenu's own comment on
   why this is an in-place swap, not a second floating panel) -- leading
   reversed chevron (.detail-view-back-chevron, same rotate as
   detailViewBack itself) then the destination label, both muted like the
   rest of this menu until hovered, same as every other item here. */
.detail-view-menu-item--back {
  gap: 0.4vw;
}

/* Detail drill-down views (Project Detail, Milestone Tasks, etc.). The
   back control, viewTitle, and sectionHeader stay pinned at the top;
   only the listContainer scrolls once it runs out of room -- same
   bounded flex + overflow-y pattern the New Project wizard uses for
   its step content (.wizard-step-content). A Project with Phases gives
   its listContainer both list-container and phase-groups (see
   renderDetailRows) so this one rule covers Phases and plain Milestones
   alike. */
.detail-view .body-container {
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  overflow: hidden;
}

.detail-view .list-section {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  overflow: hidden;
}

.detail-view .section-header {
  flex: 0 0 auto;
}

.detail-view .list-section > .list-container {
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  max-height: none;
  overflow-y: auto;
  width: 100%;
  align-items: stretch;
}

/* Each Phase gets its own .list-container "card" (see renderPhaseAccordion)
   nested inside the outer scrolling one above. Two overrides needed, not
   just one:
   - height: the base .list-container rule's "fit-content" (not "auto")
     clamps a flex item's height to whatever space is actually available,
     shrinking it below its own content's natural size once the outer
     flex column runs out of room -- "auto" doesn't clamp like that, so a
     card always renders at its full natural height instead of being
     squeezed to fit.
   - max-height: the base rule's 67.67vh cap, which would otherwise still
     clip an unusually tall single Phase (many Milestones) even once
     "fit-content" isn't shrinking it anymore.
   - flex-shrink: 0, since flex items default to shrinking (flex-shrink:
     1) when a flex container runs out of room -- without this a card
     would still get compressed rather than pushing the outer list to
     actually overflow (and scroll).
   A nested card has no scrollbar of its own -- it always renders at its
   full natural height (collapsed or expanded), and the outer list's
   scrollbar is what handles the overall Project Detail View getting
   longer than the screen. */
.detail-view .list-container.phase-groups > .list-container {
  height: auto;
  max-height: none;
  flex-shrink: 0;
}

/* Plain Rows (a Project's top-level Milestones with no Phases, or any
   other list in a detailView, e.g. a Milestone's Tasks) need the same
   "don't shrink to fit, let the list overflow and scroll instead"
   behavior -- rows are also flex items of a flex-column .list-container. */
.detail-view .list-section > .list-container > .row,
.detail-view .list-section > .list-container > .row-new {
  flex-shrink: 0;
}

.canvas-container {
  display: var(--container-canvas-display);
  flex-direction: var(--container-canvas-direction);
  height: var(--container-canvas-height);
  width: var(--container-canvas-width);
  align-items: var(--container-canvas-align);
  justify-content: var(--container-canvas-justify);
  gap: var(--container-canvas-gap);
  padding: var(--container-canvas-padding);
  overflow: var(--container-canvas-overflow);
}

.toolbar-container {
  display: var(--container-toolbar-display);
  flex-direction: var(--container-toolbar-direction);
  height: var(--container-toolbar-height);
  width: var(--container-toolbar-width);
  align-items: var(--container-toolbar-align);
  justify-content: var(--container-toolbar-justify);
  gap: var(--container-toolbar-gap);
  padding: var(--container-toolbar-padding);
  overflow: var(--container-toolbar-overflow);
}

.blocks-container {
  display: var(--container-blocks-display);
  flex-direction: var(--container-blocks-direction);
  height: var(--container-blocks-height);
  width: var(--container-blocks-width);
  align-items: var(--container-blocks-align);
  justify-content: var(--container-blocks-justify);
  gap: var(--container-blocks-gap);
  padding: var(--container-blocks-padding);
  overflow: var(--container-blocks-overflow);
}

.pages-container {
  display: var(--container-pages-display);
  flex-direction: var(--container-pages-direction);
  height: var(--container-pages-height);
  width: var(--container-pages-width);
  align-items: var(--container-pages-align);
  justify-content: var(--container-pages-justify);
  gap: var(--container-pages-gap);
  padding: var(--container-pages-padding);
  overflow: var(--container-pages-overflow);
}

.canvas-workspace-container {
  display: var(--container-canvas-workspace-display);
  flex-direction: var(--container-canvas-workspace-direction);
  flex: var(--container-canvas-workspace-flex);
  min-height: var(--container-canvas-workspace-min-height);
  height: var(--container-canvas-workspace-height);
  width: var(--container-canvas-workspace-width);
  align-items: var(--container-canvas-workspace-align);
  justify-content: var(--container-canvas-workspace-justify);
  gap: var(--container-canvas-workspace-gap);
  padding: var(--container-canvas-workspace-padding);
  overflow: var(--container-canvas-workspace-overflow);
  box-sizing: border-box;
}

.outline-container {
  display: var(--container-outline-display);
  flex-direction: var(--container-outline-direction);
  flex-shrink: var(--container-outline-flex-shrink);
  width: var(--container-outline-width);
  height: var(--container-outline-height);
  align-items: var(--container-outline-align);
  justify-content: var(--container-outline-justify);
  gap: var(--container-outline-gap);
  padding: var(--container-outline-padding);
  overflow: var(--container-outline-overflow);
  background-color: var(--container-outline-background);
  border-radius: var(--container-outline-border-radius);
  box-sizing: border-box;
}

.outline-list {
  display: var(--container-outline-list-display);
  flex-direction: var(--container-outline-list-direction);
  flex: var(--container-outline-list-flex);
  min-height: var(--container-outline-list-min-height);
  width: var(--container-outline-list-width);
  align-items: var(--container-outline-list-align);
  justify-content: var(--container-outline-list-justify);
  gap: var(--container-outline-list-gap);
  padding: var(--container-outline-list-padding);
  overflow: var(--container-outline-list-overflow);
  box-sizing: border-box;
}

.authoring-container {
  display: var(--container-authoring-display);
  flex-direction: var(--container-authoring-direction);
  flex: var(--container-authoring-flex);
  min-width: var(--container-authoring-min-width);
  height: var(--container-authoring-height);
  width: var(--container-authoring-width);
  align-items: var(--container-authoring-align);
  justify-content: var(--container-authoring-justify);
  gap: var(--container-authoring-gap);
  padding: var(--container-authoring-padding);
  overflow: var(--container-authoring-overflow);
  box-sizing: border-box;
}

.outline-module {
  display: var(--component-outline-module-display);
  flex-direction: var(--component-outline-module-direction);
  width: var(--component-outline-module-width);
  gap: var(--component-outline-module-gap);
  padding: var(--component-outline-module-padding);
  overflow: var(--component-outline-module-overflow);
  box-sizing: border-box;
}

.outline-module-row {
  display: var(--component-outline-module-row-display);
  flex-direction: var(--component-outline-module-row-direction);
  height: var(--component-outline-module-row-height);
  width: var(--component-outline-module-row-width);
  align-items: var(--component-outline-module-row-align);
  justify-content: var(--component-outline-module-row-justify);
  gap: var(--component-outline-module-row-gap);
  padding: var(--component-outline-module-row-padding);
  background-color: var(--component-outline-module-row-background);
  border-top: var(--component-outline-module-row-border-top);
  cursor: var(--component-outline-module-row-cursor);
  transition: var(--component-outline-module-row-transition);
  box-sizing: border-box;
}

.outline-module-row:hover {
  filter: var(--component-outline-module-row-hover-filter);
}

.outline-module-row:active {
  filter: var(--component-outline-module-row-active-filter);
}

.outline-module-row:focus-visible {
  outline: var(--component-outline-lesson-focus-outline);
  outline-offset: -2px;
}

.outline-module-lessons {
  display: var(--component-outline-module-lessons-display);
  flex-direction: var(--component-outline-module-lessons-direction);
  width: var(--component-outline-module-lessons-width);
  gap: var(--component-outline-module-lessons-gap);
  padding: var(--component-outline-module-lessons-padding);
  overflow: var(--component-outline-module-lessons-overflow);
  box-sizing: border-box;
}

.outline-module--collapsed .outline-module-lessons {
  display: none;
}

.outline-module-chevron {
  color: var(--component-row-chevron-color);
  font-size: var(--font-row-title-size);
  line-height: 1;
  display: inline-block;
  transition: var(--animation-outline-chevron-transition);
}

.outline-module-row:hover .outline-module-chevron {
  transform: translateX(var(--component-row-chevron-hover-translate-x));
}

.outline-module--expanded .outline-module-chevron {
  transform: rotate(var(--animation-outline-chevron-rotation-expanded));
}

.outline-module--expanded .outline-module-row:hover .outline-module-chevron {
  transform: rotate(var(--animation-outline-chevron-rotation-expanded))
    translateX(var(--component-row-chevron-hover-translate-x));
}

.outline-lesson {
  display: var(--component-outline-lesson-display);
  flex-direction: var(--component-outline-lesson-direction);
  height: var(--component-outline-lesson-height);
  width: var(--component-outline-lesson-width);
  align-items: var(--component-outline-lesson-align);
  justify-content: var(--component-outline-lesson-justify);
  gap: var(--component-outline-lesson-gap);
  padding: var(--component-outline-lesson-padding);
  background-color: var(--component-outline-lesson-background-standby);
  cursor: var(--component-outline-lesson-cursor);
  transition: var(--component-outline-lesson-transition);
  box-sizing: border-box;
  border: none;
  margin: 0;
  text-align: left;
  -webkit-appearance: none;
  appearance: none;
}

.outline-lesson:hover {
  filter: var(--component-outline-lesson-hover-filter);
}

.outline-lesson:active {
  filter: var(--component-outline-lesson-active-filter);
}

.outline-lesson--active,
.outline-lesson.is-active,
.outline-lesson[aria-selected="true"] {
  background-color: var(--component-outline-lesson-background-active);
}

.outline-lesson:focus-visible {
  outline: var(--component-outline-lesson-focus-outline);
  outline-offset: -2px;
}

.outline-add-module,
.outline-add-lesson {
  display: var(--component-row-new-display);
  flex-direction: var(--component-row-new-direction);
  width: var(--component-outline-add-width);
  align-items: var(--component-row-new-align);
  justify-content: var(--component-row-new-justify);
  gap: var(--component-row-new-gap);
  padding: var(--component-row-new-padding);
  background-color: var(--component-row-new-background);
  color: var(--component-row-new-color);
  border: var(--component-row-new-border);
  cursor: var(--component-row-new-cursor);
  transition: var(--component-row-new-transition);
  box-sizing: border-box;
  border-radius: var(--component-outline-add-border-radius);
}

.outline-add-module {
  height: var(--component-outline-add-module-height);
  border-bottom-left-radius: var(--component-outline-add-module-border-bottom-left-radius);
  border-bottom-right-radius: var(--component-outline-add-module-border-bottom-right-radius);
}

.outline-add-lesson {
  height: var(--component-outline-add-lesson-height);
}

.outline-add-module:hover,
.outline-add-lesson:hover {
  color: var(--component-row-new-hover-color);
  border-color: var(--component-row-new-hover-border-color);
}

.outline-add-module .font-add-new-button,
.outline-add-lesson .font-add-new-button {
  color: inherit;
}

/* Component utilities */
.tab-bar {
  position: relative;
  display: var(--component-tab-bar-display);
  flex-direction: var(--component-tab-bar-direction);
  height: var(--component-tab-bar-height);
  width: var(--component-tab-bar-width);
  max-width: var(--component-tab-bar-max-width);
  box-sizing: border-box;
  align-items: var(--component-tab-bar-align);
  justify-content: var(--component-tab-bar-justify);
  gap: var(--component-tab-bar-gap);
  padding: var(--component-tab-bar-padding);
  background-color: var(--component-tab-bar-background);
  border-radius: var(--component-tab-bar-border-radius);
}

.tab-bar-inner {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 0;
  width: 100%;
  max-width: 100%;
  overflow: hidden;
}

.tab--overflow-hidden {
  display: none !important;
}

.tab-more {
  position: relative;
  flex-shrink: 0;
}

.tab-indicator {
  position: absolute;
  z-index: 0;
  top: 0;
  left: 0;
  pointer-events: none;
  visibility: hidden;
  background-color: var(--component-tab-background-active);
  border-radius: var(--component-tab-border-radius);
  transition: var(--animation-tab-indicator-transition);
}

.tab-bar.is-ready .tab-indicator {
  visibility: visible;
}

.tab-indicator--instant {
  transition: none !important;
}

/* First tab uses active background until the indicator is ready */
.tab-bar:not(.is-ready) .tab:first-of-type:not(.tab-more-trigger) {
  background-color: var(--component-tab-background-active);
}

.tab {
  position: relative;
  z-index: 1;
  display: var(--component-tab-display);
  flex-direction: var(--component-tab-direction);
  height: var(--component-tab-height);
  width: var(--component-tab-width);
  align-items: var(--component-tab-align);
  justify-content: var(--component-tab-justify);
  gap: var(--component-tab-gap);
  padding: var(--component-tab-padding);
  border: none;
  border-radius: var(--component-tab-border-radius);
  white-space: var(--component-tab-white-space);
  background-color: var(--component-tab-background-standby);
  cursor: pointer;
  margin: 0;
  box-sizing: border-box;
  -webkit-appearance: none;
  appearance: none;
}

.tab-bar.is-ready .tab.tab--active,
.tab-bar.is-ready .tab.is-active,
.tab-bar.is-ready .tab[aria-selected="true"] {
  background-color: transparent;
}

/* Static active state (when not using the sliding indicator) */
.tab-bar:not(:has(.tab-indicator)) .tab--active,
.tab-bar:not(:has(.tab-indicator)) .tab.is-active,
.tab-bar:not(:has(.tab-indicator)) .tab[aria-selected="true"] {
  background-color: var(--component-tab-background-active);
}

/* Component utilities — secondaryTabBar */
.secondary-tab-bar {
  position: relative;
  display: var(--component-secondary-tab-bar-display);
  flex-direction: var(--component-secondary-tab-bar-direction);
  height: var(--component-secondary-tab-bar-height);
  width: var(--component-secondary-tab-bar-width);
  max-width: var(--component-secondary-tab-bar-max-width);
  box-sizing: border-box;
  align-items: var(--component-secondary-tab-bar-align);
  justify-content: var(--component-secondary-tab-bar-justify);
  gap: var(--component-secondary-tab-bar-gap);
  padding: var(--component-secondary-tab-bar-padding);
  background-color: var(--component-secondary-tab-bar-background);
  border-radius: var(--component-secondary-tab-bar-border-radius);
}

.secondary-tab-bar-inner {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 0;
  width: 100%;
  max-width: 100%;
  overflow: hidden;
}

.secondary-tab-more {
  position: relative;
  flex-shrink: 0;
}

.secondary-tab-indicator {
  position: absolute;
  z-index: 0;
  top: 0;
  left: 0;
  pointer-events: none;
  visibility: hidden;
  background-color: var(--component-tab-background-active);
  border-radius: var(--component-tab-border-radius);
  transition: var(--animation-tab-indicator-transition);
}

.secondary-tab-bar.is-ready .secondary-tab-indicator {
  visibility: visible;
}

.secondary-tab-indicator--instant {
  transition: none !important;
}

/* Shown by createProjectSectionTabBar (public/views/projects.js) in place
   of a container's own rows when none of them belong to the currently
   selected section -- same plain caption + padding treatment as
   todo-list-empty (public/styles.css)'s own empty state, just for this
   component instead of a To-do List. */
.section-tab-bar-empty {
  padding: 2vh 0.68vw;
}

.secondary-tab-bar:not(.is-ready) .tab:first-of-type:not(.secondary-tab-more-trigger) {
  background-color: var(--component-tab-background-active);
}

.secondary-tab-bar.is-ready .tab.tab--active,
.secondary-tab-bar.is-ready .tab.is-active,
.secondary-tab-bar.is-ready .tab[aria-selected="true"] {
  background-color: transparent;
}

.secondary-tab-bar:not(:has(.secondary-tab-indicator)) .tab--active,
.secondary-tab-bar:not(:has(.secondary-tab-indicator)) .tab.is-active,
.secondary-tab-bar:not(:has(.secondary-tab-indicator)) .tab[aria-selected="true"] {
  background-color: var(--component-tab-background-active);
}

.row {
  display: var(--component-row-display);
  flex-direction: var(--component-row-direction);
  height: var(--component-row-height);
  width: var(--component-row-width);
  align-items: var(--component-row-align);
  justify-content: var(--component-row-justify);
  gap: var(--component-row-gap);
  padding: var(--component-row-padding);
  background-color: var(--component-row-background);
  border-top: var(--component-row-border-top);
  box-sizing: border-box;
  cursor: var(--component-row-cursor);
  transition: var(--component-row-transition);
}

/* Same fix detail-view-menu-panel[hidden] and tab-panel[hidden] already
   need, for the same reason: this rule's own "display: flex" above is an
   author style, which beats the UA stylesheet's plain [hidden] {
   display: none } even at equal specificity -- so without this,
   createProjectSectionTabBar's own row.hidden = true/false (public/views/
   projects.js) would toggle the attribute without it ever visually
   hiding anything. */
.row[hidden] {
  display: none;
}

.row:hover {
  filter: var(--component-row-hover-filter);
}

.row:active {
  filter: var(--component-row-active-filter);
  transition-duration: var(--component-row-active-transition-duration);
}

.row--active:hover {
  filter: var(--component-row-hover-filter);
}

.row:focus-visible {
  outline: var(--component-row-focus-outline);
  outline-offset: -2px;
}

.row-new {
  display: var(--component-row-new-display);
  flex-direction: var(--component-row-new-direction);
  height: var(--component-row-new-height);
  width: var(--component-row-new-width);
  align-items: var(--component-row-new-align);
  justify-content: var(--component-row-new-justify);
  gap: var(--component-row-new-gap);
  padding: var(--component-row-new-padding);
  background-color: var(--component-row-new-background);
  color: var(--component-row-new-color);
  border: var(--component-row-new-border);
  border-bottom-left-radius: var(--component-row-new-border-bottom-left-radius);
  border-bottom-right-radius: var(--component-row-new-border-bottom-right-radius);
  cursor: var(--component-row-new-cursor);
  transition: var(--component-row-new-transition);
  box-sizing: border-box;
}

.row-new:hover {
  color: var(--component-row-new-hover-color);
  border-color: var(--component-row-new-hover-border-color);
}

.row-new .font-add-new-button {
  color: inherit;
}

.new-user-form {
  display: var(--component-new-user-form-display);
  grid-template-columns: var(--component-new-user-form-columns);
  grid-template-rows: var(--component-new-user-form-rows);
  width: var(--component-new-user-form-width);
  column-gap: var(--component-new-user-form-column-gap);
  row-gap: var(--component-new-user-form-row-gap);
  margin: 0;
  padding: var(--component-new-user-form-padding);
  border: none;
  border-radius: var(--component-new-user-form-border-radius);
  overflow: hidden;
  background-color: var(--component-new-user-form-background);
  box-sizing: border-box;
}

.new-user-form-field {
  display: flex;
  align-items: stretch;
  min-height: var(--component-new-user-form-field-min-height);
  padding: var(--component-new-user-form-field-padding);
  border-radius: var(--component-new-user-form-field-border-radius);
  background-color: var(--component-new-user-form-field-background);
  box-sizing: border-box;
}

.new-user-form-field--full {
  grid-column: 1 / -1;
}

.new-user-form-input {
  box-sizing: border-box;
  flex: 1;
  width: 100%;
  min-height: var(--component-new-user-form-input-min-height);
  margin: 0;
  padding: var(--component-new-user-form-input-padding);
  border: none;
  background-color: transparent;
  color: var(--component-new-user-form-input-color);
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}

.new-user-form-input::placeholder {
  color: var(--component-new-user-form-input-placeholder-color);
}

.new-user-form-input:focus,
.new-user-form-input:focus-visible {
  outline: none;
}

/* A newUserForm field tall enough to hold a multi-line note instead of a
   single line -- used by the New Task wizard's "Notes" field, the one
   field there still a plain text field rather than a Row (see .row-option
   below) -- free text has no fixed set of choices to expand into. */
.new-user-form-field--textarea {
  align-items: flex-start;
  min-height: 9vh;
}

textarea.new-user-form-input {
  resize: none;
  padding-top: 1.5vh;
  font-family: inherit;
  font-size: inherit;
}

/* The New Task wizard's Notes field stands on its own now (Priority,
   Owner, Show every day and Due date all moved to standalone
   option-field-group fields below), not one grid cell among several
   inside a .new-user-form -- so it needs its own full width instead of
   inheriting a grid cell's, and its own secondaryColor background
   instead of newUserForm's usual backgroundColor -- that shade is meant
   to pop as a lighter cutout against a newUserForm's own secondaryColor
   fill, but standing alone here, directly on the sheet (which is
   backgroundColor itself), it would be invisible. secondaryColor matches
   every option-field-group above it instead. */
.notes-field {
  width: 100%;
  flex-shrink: 0;
  background-color: var(--color-secondary);
}

.row-chevron {
  color: var(--component-row-chevron-color);
  font-size: var(--font-row-title-size);
  line-height: 1;
  display: inline-block;
  transition: var(--component-row-chevron-transition);
}

.row:hover .row-chevron {
  transform: translateX(var(--component-row-chevron-hover-translate-x));
}

/* Sits in a Row's Leading, right after its title (see .row-title-row
   below) -- a plain document glyph shown only when a Task has at least
   one attachment (see createAttachmentIndicator in
   public/views/projects.js). Muted like the chevron (same
   secondaryTextColor role, not an accent) since this is a passive
   signal, not something to tap. */
.attachment-indicator {
  display: inline-flex;
  align-items: center;
  color: var(--color-text-secondary);
}

/* Wraps a Row's title together with an inline icon that belongs right
   next to it on the same line -- currently just attachmentIndicator
   above -- rather than that icon stacking below the title (row-leading's
   own flex-direction: column) or sitting in the Trailing (see
   createEntityRow's titleIcon param in public/views/projects.js). Same
   baseline-aligned pairing .board-card-title-row already uses for the
   Kanban card's own title + attachmentIndicator. */
.row-title-row {
  display: flex;
  align-items: baseline;
  gap: 0.4vw;
}

.row-leading,
.leading {
  display: var(--component-leading-display);
  flex-direction: var(--component-leading-direction);
  height: var(--component-leading-height);
  width: var(--component-leading-width);
  align-items: var(--component-leading-align);
  justify-content: var(--component-leading-justify);
  gap: var(--component-leading-gap);
  padding: var(--component-leading-padding);
  background-color: var(--component-leading-background);
  box-sizing: border-box;
}

.row-trailing,
.trailing {
  display: var(--component-trailing-display);
  flex-direction: var(--component-trailing-direction);
  height: var(--component-trailing-height);
  width: var(--component-trailing-width);
  align-items: var(--component-trailing-align);
  justify-content: var(--component-trailing-justify);
  gap: var(--component-trailing-gap);
  padding: var(--component-trailing-padding);
  background-color: var(--component-trailing-background);
  box-sizing: border-box;
}

/* Typography utilities — lightBold */
.font-light-bold {
  font-weight: var(--font-light-bold-weight);
}

/* Component utilities — gantt (Gantt Chart tab's project timeline, see
   public/views/gantt-chart.js). Only two real grid columns exist --
   outline, timeline -- the timeline column's own day cells (in the
   header) and day gridlines (in each row's lane) are laid out with flex
   and a repeating background respectively, not as further grid columns,
   so the grid's own shape never has to change as the date range grows or
   shrinks; only --gantt-timeline-width (set per-project in JS) does.
   .gantt-scroll is the one scrolling element (both axes) -- the sticky
   header/outline cells below are positioned relative to it. */
/* flex: 1 1 auto (not height: 100%) + margin-bottom, same technique
   .board-columns (Kanban Board's own equivalent, public/styles.css)
   uses -- without it, this card's own rounded corners would sit flush
   against body-container's bottom edge, the same problem that rule's
   own comment describes. margin-bottom (not padding-bottom) here
   specifically because this card itself is the visible rounded box
   (unlike .board-columns, a plain wrapper one level above the actual
   visible .board-column boxes) -- the gap needs to be outside it, not
   eaten out of its own background. */
.gantt-timeline {
  display: flex;
  flex-direction: column;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  margin-bottom: 2vh;
  background-color: var(--component-gantt-background);
  border-radius: var(--component-gantt-border-radius);
  overflow: hidden;
  box-sizing: border-box;
}

.gantt-scroll {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  overflow: auto;
}

/* width: max-content is the actual fix that makes horizontal sticky
   (outline column, header corner/scale) work across the *entire*
   scroll range, not just its first viewport-width or so -- with no
   explicit width, a grid container defaults to filling its available
   space (.gantt-scroll's own clientWidth) rather than sizing to its
   own (wider) column tracks, even though those tracks still visually
   paint/scroll beyond that box. A sticky descendant's "room to stick"
   is bounded by that formal box, not by whatever visually overflows
   it, so once scrolled far enough to approach that box's edge, sticky
   elements gradually un-stick and start scrolling with the content --
   exactly like a sticky header running out of room at the bottom of a
   short scrolling section, just triggered here well before the real
   (visual) end of the scrollable content. max-content sizes the grid
   to the true sum of its column tracks instead, matching
   .gantt-scroll's actual scrollWidth.

   The timeline column's own minmax(..., 1fr) matters for a short
   project whose date range doesn't need horizontal scrolling at all:
   without the 1fr, the two columns (outline + timeline) would only sum
   to their own content width, leaving the grid's min-width: 100% forcing
   a *wider* box than that sum -- and a Phase/Milestone divider spanning
   grid-column: 1 / -1 only spans its two tracks' own sizes, not
   whatever extra empty space min-width added beyond them, so it would
   fall short of the card's actual right edge. 1fr lets the timeline
   column absorb exactly that leftover space (and only that -- see
   .gantt-grid's own width comment above for why there's none left to
   absorb once a project's date range is wide enough to need scrolling
   in the first place). */
.gantt-grid {
  display: grid;
  grid-template-columns: var(--component-gantt-outline-width) minmax(var(--gantt-timeline-width, max-content), 1fr);
  grid-auto-rows: var(--component-gantt-row-height);
  width: max-content;
  min-width: 100%;
}

/* Frozen corner (row 1, col 1) needs both top and left stickiness at
   once, and the highest z-index of the three sticky pieces, since it
   sits above both the header row scrolling past it vertically and the
   outline column scrolling past it horizontally. Sticky directly on
   this grid item itself, same reasoning .gantt-outline-cell is (not a
   wrapper inside it) -- see that rule's own comment for why.
   transform/will-change promote it to its own GPU layer -- without
   that, sticky elements can visibly lag a frame or two behind a fast
   (trackpad momentum) scroll gesture in some browsers before snapping
   back into place, which reads as "it scrolled too" even though it
   settles correctly once scrolling stops. */
.gantt-header-corner {
  position: sticky;
  top: 0;
  left: 0;
  z-index: 3;
  background-color: var(--component-gantt-background);
  transform: translateZ(0);
  will-change: transform;
}

.gantt-header-scale {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: stretch;
  background-color: var(--component-gantt-background);
  box-sizing: border-box;
  transform: translateZ(0);
  will-change: transform;
}

.gantt-header-cell {
  flex: 0 0 var(--component-gantt-cell-width);
  width: var(--component-gantt-cell-width);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1vh;
  border-left: 1px solid var(--color-background);
  box-sizing: border-box;
  color: var(--component-gantt-scale-color);
}

.gantt-header-cell:first-child {
  border-left: none;
}

.gantt-header-month {
  font-size: 11px;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

.gantt-header-day {
  font-size: 13px;
  line-height: 1;
}

.gantt-header-cell--today .gantt-header-day {
  color: var(--component-gantt-today-color);
  font-weight: 700;
}

/* Sticky to the left edge of .gantt-scroll, same role the frozen corner
   plays for the header -- an outline label stays put while its own
   lane (and every other row's) scrolls past underneath it. This has to
   be position: sticky directly on `cell` (the grid item) itself, not on
   a wrapper inside it -- a sticky descendant of a grid item that isn't
   itself the grid item only sticks within that (column-width-wide)
   parent's own box, so it stops sticking the moment that narrow parent
   scrolls fully past, which looks identical to not being sticky at all.
   (.gantt-milestone-divider's title can get away with being a sticky
   child because its own parent already spans the entire scrollable
   width, not just one column -- that's the exception, not the rule.)
   transform/will-change promote it to its own GPU layer, same reasoning
   .gantt-header-corner's own comment gives -- avoids a visible lag
   behind a fast momentum-scroll gesture in some browsers. */
.gantt-outline-cell {
  position: sticky;
  left: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--component-gantt-outline-padding);
  border-top: var(--component-gantt-seam);
  background-color: var(--color-secondary);
  box-sizing: border-box;
  overflow: hidden;
  transform: translateZ(0);
  will-change: transform;
}

.gantt-outline-cell .font-row-title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A Phase spans both grid columns (grid-column: 1 / -1) instead of
   sitting only in the outline column like a Task -- same full-width
   divider shape as .gantt-milestone-divider below (see that rule's own
   comment for why `divider` itself carries none of the visual styling).
   Its own background/border are the same thirdColor/accentColor pair
   rowPhase and .gantt-outline-cell--phase already used, so a Phase
   still reads as a Phase everywhere in the app, not just in the
   Project Detail View, and stays visually distinct from a Milestone's
   own muted border. */
.gantt-phase-divider {
  grid-column: 1 / -1;
  display: flex;
  align-items: stretch;
  border-top: var(--component-gantt-seam);
  background-color: var(--component-gantt-phase-background);
  box-sizing: border-box;
}

/* Sticky at the left edge of .gantt-scroll -- see
   .gantt-milestone-divider-sticky's own comment for why the border
   lives here rather than on `.gantt-phase-divider` itself. Uppercase is
   the one style difference from a Milestone's own title -- keeps a
   Phase heading visually distinguishable now that both share the same
   full-width shape. */
.gantt-phase-divider-sticky {
  position: sticky;
  left: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  padding: var(--component-gantt-outline-padding);
  border-left: var(--component-gantt-phase-accent-border);
  box-sizing: border-box;
  transform: translateZ(0);
  will-change: transform;
}

.gantt-phase-divider-sticky .font-row-title {
  text-transform: uppercase;
}

/* A Milestone spans both grid columns (grid-column: 1 / -1) instead of
   sitting only in the outline column like a Task -- a full-width
   divider bar reads unambiguously as "everything below this, until
   the next one, belongs to this Milestone." `divider`
   itself is NOT sticky and carries none of the visual styling (border,
   padding) -- just the top seam and the background band, both of which
   already span the entire scrollable width so they need no help
   staying visible. Its sticky child (.gantt-milestone-divider-sticky,
   below) carries everything that has to stay anchored at the left
   edge -- the title AND the left border together, so the border no
   longer scrolls off separately from the text it's supposed to be
   marking. */
.gantt-milestone-divider {
  grid-column: 1 / -1;
  display: flex;
  align-items: stretch;
  border-top: var(--component-gantt-seam);
  background-color: var(--color-background);
  box-sizing: border-box;
}

/* Sticky at the left edge of .gantt-scroll, same as every other sticky
   outline label -- but here it wraps the left border too (see
   .gantt-milestone-divider's own comment for why), not just the title,
   so the whole "this is a Milestone" marker moves as one unit while a
   wide project's date range scrolls underneath it. Safe to be a sticky
   *child* here (unlike .gantt-outline-cell, which must be sticky
   itself) specifically because its parent already spans the entire
   scrollable width, not just one column. padding matches
   .gantt-outline-cell's own so the title still lines up with every
   other row's title. transform/will-change promote it to its own GPU
   layer, same reasoning .gantt-header-corner's own comment gives --
   avoids a visible lag behind a fast momentum-scroll gesture in some
   browsers. */
.gantt-milestone-divider-sticky {
  position: sticky;
  left: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  padding: var(--component-gantt-outline-padding);
  border-left: var(--component-gantt-milestone-border);
  box-sizing: border-box;
  transform: translateZ(0);
  will-change: transform;
}

/* A Task row's note on which date(s) it's missing (see
   public/views/gantt-chart.js's decision on undated tasks) -- a
   dedicated small size rather than font-row-subtitle, which was sized
   for the taller (6.64vh) .row, not this grid's more compact 5vh rows. */
.gantt-outline-note {
  font-size: 11px;
  line-height: 13px;
  color: var(--color-text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A day's vertical gridline, drawn as a repeating background instead of
   one real element per day -- a project spanning months would otherwise
   mean hundreds of extra nodes per row just to draw lines. position:
   relative anchors a Task row's bar/pin (both absolutely positioned). */
.gantt-lane {
  position: relative;
  border-top: var(--component-gantt-seam);
  background-image: repeating-linear-gradient(
    to right,
    transparent 0,
    transparent calc(var(--component-gantt-cell-width) - 1px),
    var(--component-gantt-lane-line-color) calc(var(--component-gantt-cell-width) - 1px),
    var(--component-gantt-lane-line-color) var(--component-gantt-cell-width)
  );
  background-size: var(--component-gantt-cell-width) 100%;
  box-sizing: border-box;
}

/* A Task with both Start Date and Due Date -- spans the exact days
   between them. --gantt-bar-left/--gantt-bar-width are set inline per
   task (see createTaskMarker), the day-offset math only; the inset that
   keeps a bar from touching the gridline it starts/ends on lives here,
   in the one shared token, instead of being repeated in JS per bar. */
.gantt-bar {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  height: var(--component-gantt-bar-height);
  left: calc(var(--gantt-bar-left) + var(--component-gantt-bar-inset));
  width: calc(var(--gantt-bar-width) - (var(--component-gantt-bar-inset) * 2));
  background-color: var(--component-gantt-bar-color);
  border-radius: var(--component-gantt-bar-radius);
  box-sizing: border-box;
}

/* A completed Task's bar (task.status === "done", see createTaskMarker)
   -- the same shape/position, just the muted fill instead of the vivid
   one every still-open Task's bar keeps using. */
.gantt-bar--complete {
  background-color: var(--component-gantt-bar-complete-color);
}

/* A Task with only one of Start Date/Due Date set -- a dot at the one
   known date instead of a bar, so it can't be mistaken for a real
   (known) duration. --gantt-pin-left is set inline per task, already
   centered on its day cell (see createTaskMarker). */
.gantt-pin {
  position: absolute;
  top: 50%;
  left: var(--gantt-pin-left);
  width: var(--component-gantt-pin-size);
  height: var(--component-gantt-pin-size);
  border-radius: 50%;
  background-color: var(--component-gantt-pin-color);
  box-shadow: 0 0 0 2px var(--component-gantt-background);
  transform: translate(-50%, -50%);
  box-sizing: border-box;
}

/* A completed Task's pin -- same reasoning .gantt-bar--complete's own
   comment gives. */
.gantt-pin--complete {
  background-color: var(--component-gantt-pin-complete-color);
}

.gantt-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 100%;
  padding: 4vh 2vw;
  box-sizing: border-box;
  text-align: center;
}

/* =============================================================================
   Micro-animations — shared across all role flows
   ============================================================================= */

@keyframes pathora-fade-rise {
  from {
    opacity: 0;
    transform: translateY(var(--animation-list-enter-distance));
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* View title and section title (and its helper, once it has text) ease in
   on page load */
.font-view-title,
.font-section-title,
.font-section-helper:not(:empty) {
  animation: pathora-fade-rise var(--animation-title-enter-duration)
    var(--animation-list-enter-easing) backwards;
}

/* List rows cascade in, one after the other */
.list-container > .row,
.list-container > .row-new {
  animation: pathora-fade-rise var(--animation-list-enter-duration)
    var(--animation-list-enter-easing) backwards;
}

.list-container > :nth-child(1) { animation-delay: calc(var(--animation-list-enter-stagger) * 0); }
.list-container > :nth-child(2) { animation-delay: calc(var(--animation-list-enter-stagger) * 1); }
.list-container > :nth-child(3) { animation-delay: calc(var(--animation-list-enter-stagger) * 2); }
.list-container > :nth-child(4) { animation-delay: calc(var(--animation-list-enter-stagger) * 3); }
.list-container > :nth-child(5) { animation-delay: calc(var(--animation-list-enter-stagger) * 4); }
.list-container > :nth-child(6) { animation-delay: calc(var(--animation-list-enter-stagger) * 5); }
.list-container > :nth-child(7) { animation-delay: calc(var(--animation-list-enter-stagger) * 6); }
.list-container > :nth-child(8) { animation-delay: calc(var(--animation-list-enter-stagger) * 7); }
.list-container > :nth-child(n + 9) { animation-delay: calc(var(--animation-list-enter-stagger) * 8); }

/* Tabs compress slightly while pressed */
.tab {
  transition: var(--animation-tab-press-transition);
}

.tab:active {
  transform: scale(var(--animation-tab-press-scale));
}

/* Icon action buttons inside row trailing areas */
.row-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--color-text);
  -webkit-appearance: none;
  appearance: none;
  transition: var(--animation-icon-transition);
}

.row-action:hover {
  transform: scale(var(--animation-icon-hover-scale));
}

.row-action:active {
  transform: scale(var(--animation-icon-active-scale));
}

.row-action:focus-visible {
  outline: var(--component-row-focus-outline);
  outline-offset: 2px;
  border-radius: 4px;
}

.row-action .row-icon {
  display: block;
}

/* Status icons (progress rings, completion marks) inside row trailing areas */
.row-status-icon {
  display: block;
  color: var(--color-text);
}

/* A Row's leading title swapped for an editable, borderless input --
   color is set explicitly (not "inherit") because native <input> elements
   get their own UA-stylesheet color (effectively black) rather than truly
   inheriting it, which made typed Project/Phase/Milestone/Task names show
   up black instead of the brand's text color. */
.row-input {
  width: 100%;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  outline: none;
  color: var(--color-text);
  -webkit-appearance: none;
  appearance: none;
}

.row-input::placeholder {
  color: var(--color-text-secondary);
}

/* Project creation wizard (New Project sheet). The sheet's own title and
   error message never scroll -- only a step's own middle content does, so
   long lists (e.g. Milestones across several Phases) scroll in place
   without the sheet's header moving. See .sheet-step-body below. */
.wizard-step-content {
  display: flex;
  flex-direction: column;
  gap: var(--container-body-gap);
  width: 100%;
  height: 100%;
  min-height: 0;
  box-sizing: border-box;
}

/* Whichever element holds a step's actual fields -- a plain list (Name,
   Choose Phases, Add Phases), the Milestones step's Phase groups, or the
   New Task step's own fields (its options list plus the Notes field
   beneath it, wrapped together in a wizard-step-fields so the two stack
   naturally instead of the options list alone growing to fill the step)
   -- grows to fill the space between the step heading and the nav
   buttons, and scrolls on its own once it runs out of room. This keeps
   the nav buttons pinned at the bottom of the sheet no matter how many
   rows (Phases, Milestones, form fields) a step ends up with. */
.wizard-step-content > .list-container,
.wizard-step-content > .wizard-groups,
.wizard-step-content > .new-user-form,
.wizard-step-content > .wizard-step-fields {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* The New Task Options step's own fields -- its options list-container
   plus the Notes field -- stacked together so wizard-step-fields (not the
   options list alone) is what grows/scrolls; see above. */
.wizard-step-fields {
  display: flex;
  flex-direction: column;
  gap: var(--container-body-gap);
  width: 100%;
}

.wizard-step-fields > .list-container {
  flex: 0 0 auto;
}

.wizard-step-content > .wizard-nav {
  flex: 0 0 auto;
}

/* Tight gap between a step's heading and an optional short description
   below it, e.g. "Organize with Phases?" -- same gap a Row uses between
   its title and subtitle. */
.wizard-step-heading {
  display: flex;
  flex-direction: column;
  gap: var(--container-leading-gap);
  width: 100%;
}

.wizard-groups {
  display: flex;
  flex-direction: column;
  gap: var(--container-body-gap);
  width: 100%;
}

/* One Phase's bubble + its milestone list, grouped tightly like a
   segmented control sitting right above the content it controls. */
.wizard-phase-panel {
  display: flex;
  flex-direction: column;
  gap: 1vh;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
}

.phase-bubbles {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 0.7vw;
  width: 100%;
  flex: 0 0 auto;
}

.phase-bubble {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 3.5vh;
  padding: 0.78vh 1.94vw;
  border-radius: 30px;
  background-color: var(--color-secondary);
  color: var(--color-text);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.phase-bubble:hover {
  filter: var(--component-row-hover-filter);
}

.phase-bubble--active {
  background-color: var(--color-accent);
  color: var(--color-extra);
}

.phase-bubble:focus-visible {
  outline: var(--component-row-focus-outline);
  outline-offset: -2px;
}

.wizard-nav {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;
  gap: var(--component-new-user-form-column-gap);
  width: 100%;
  padding-bottom: 2vh;
  box-sizing: border-box;
}

.wizard-error {
  color: var(--color-accent);
}

/* First generic action-button component — existing components only cover
   navigational rows and tabs. Reuses newUserForm field sizing and tabPress
   motion rather than introducing new tokens. */
.wizard-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: var(--component-new-user-form-field-min-height);
  padding: var(--component-new-user-form-field-padding);
  margin: 0;
  border: none;
  border-radius: var(--component-new-user-form-field-border-radius);
  cursor: pointer;
  box-sizing: border-box;
  -webkit-appearance: none;
  appearance: none;
  transition: var(--animation-tab-press-transition);
}

.wizard-button:active {
  transform: scale(var(--animation-tab-press-scale));
}

.wizard-button:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

.wizard-button--primary {
  background-color: var(--color-accent);
  color: var(--color-extra);
}

.wizard-button--secondary {
  background-color: transparent;
  color: var(--color-text);
  border: 2px solid var(--color-text-secondary);
}

@media (prefers-reduced-motion: reduce) {
  .font-view-title,
  .font-section-title,
  .font-section-helper,
  .list-container > .row,
  .list-container > .row-new {
    animation: none;
  }

  .accordion-panel {
    transition: none;
  }

  .tab,
  .row-action {
    transition: none;
  }

  .tab:active,
  .row-action:hover,
  .row-action:active {
    transform: none;
  }
}

/* Before the indicator is ready, highlight the tab marked active in the HTML
   (falls back to the first tab only when none is marked). */
.tab-bar:not(.is-ready) .tab:first-of-type:not(.tab-more-trigger):not(.tab--active):not(:only-of-type) {
  background-color: var(--component-tab-background-standby);
}

.tab-bar:not(.is-ready) .tab.tab--active {
  background-color: var(--component-tab-background-active);
}

.secondary-tab-bar:not(.is-ready) .tab:first-of-type:not(.secondary-tab-more-trigger):not(.tab--active):not(:only-of-type) {
  background-color: var(--component-tab-background-standby);
}

.secondary-tab-bar:not(.is-ready) .tab.tab--active {
  background-color: var(--component-tab-background-active);
}
