/*
 * Design tokens
 */

:root {
  /* Brand palette (Oli2): Watermelon as the primary accent, kept distinct
     from Bootstrap's own --bs-danger so destructive actions stay visually
     different from primary ones. Jungle Green/Orange/Sky Aqua below cover
     the success/warning/info roles from the same palette. */
  /* --bs-primary/-rgb stay the same in both themes - they back solid
     surfaces (navbar, .btn-primary) paired with white text, which already
     has enough contrast against either theme. Only -text-emphasis (used
     wherever the accent appears as text on the body background, e.g.
     links and the sidebar's active item) varies per theme, since a color
     dark enough to read on a light page is too dark to read on a dark
     one. */
  --bs-primary: #e84855;
  --bs-primary-rgb: 232, 72, 85;
  --bs-primary-text-emphasis: #8a2632;
  --bs-primary-bg-subtle: #fbdde0;
  --bs-primary-border-subtle: #f0aab1;
  --bs-link-color: var(--bs-primary-text-emphasis);
  /* Derived, not hardcoded: these were fixed shades of Watermelon, so links
     on the other three accents hovered dark red. The -rgb pair only feeds
     rgba() link variants, so tracking --bs-primary-rgb is close enough and,
     unlike a literal, follows the theme. */
  --bs-link-color-rgb: var(--bs-primary-rgb);
  --bs-link-hover-color: color-mix(in srgb, var(--bs-primary-text-emphasis) 70%, black);
  --bs-link-hover-color-rgb: var(--bs-primary-rgb);

  /* Success (Jungle Green) - same consolidation pattern as primary above,
     so it's usable both as a solid surface (calendar's "Available"
     blocks) and as theme-aware text/border via -text-emphasis. */
  --bs-success: #44af69;
  --bs-success-rgb: 68, 175, 105;
  --bs-success-text-emphasis: #317e4c;
  --bs-success-bg-subtle: #dcf0e3;
  --bs-success-border-subtle: #a8dcb9;

  /* Warning (Orange) */
  --bs-warning: #fcab10;
  --bs-warning-rgb: 252, 171, 16;
  --bs-warning-text-emphasis: #8b5e09;
  --bs-warning-bg-subtle: #fef0d6;
  --bs-warning-border-subtle: #fbd58e;

  /* Info (Sky Aqua) - used for the calendar's "today"/now-indicator
     styling (see calendar-widget-script.html), kept separate from primary
     so "today" reads as a distinct marker rather than the brand color. */
  --bs-info: #54c6eb;
  --bs-info-rgb: 84, 198, 235;
  --bs-info-text-emphasis: #1c6a82;
  --bs-info-bg-subtle: #e0f6fc;
  --bs-info-border-subtle: #a6e3f2;

  /* Spacing (4/8 grid) */
  --space-1: .25rem;
  --space-2: .5rem;
  --space-3: .75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;

  /* Radius & elevation */
  --radius-sm: .25rem;
  --radius-md: .5rem;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .08);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, .12);

  /* Type scale */
  --text-xs: .75rem;
  --text-sm: .8125rem;
  --text-base: .875rem;
  --text-lg: 1rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;

  /* Motion */
  --duration-fast: 100ms;
  --duration-base: 200ms;
  --ease-out: cubic-bezier(0, 0, .2, 1);

  /* Categorical meeting-type colors */
  --color-solo: #75674b;
  --color-solo-external: #295675;
  --color-internal: #a34726;
  --color-group-external: #54877d;
  --color-hold: #495057;
}

[data-bs-theme="dark"] {
  --bs-primary-text-emphasis: #f5a6ac;
  --bs-primary-bg-subtle: #2e0f12;
  --bs-primary-border-subtle: #8a2632;
  --bs-link-color: var(--bs-primary-text-emphasis);
  /* As in light mode, but hover goes LIGHTER here - on a dark page the link
     colour is already pale, so darkening it would reduce contrast. */
  --bs-link-color-rgb: var(--bs-primary-rgb);
  --bs-link-hover-color: color-mix(in srgb, var(--bs-primary-text-emphasis) 70%, white);
  --bs-link-hover-color-rgb: var(--bs-primary-rgb);

  --bs-success-text-emphasis: #8fd6a8;
  --bs-success-bg-subtle: #0f2417;
  --bs-success-border-subtle: #317e4c;

  --bs-warning-text-emphasis: #f7c869;
  --bs-warning-bg-subtle: #2b1d05;
  --bs-warning-border-subtle: #8b5e09;

  --bs-info-text-emphasis: #8fdcf0;
  --bs-info-bg-subtle: #0d2933;
  --bs-info-border-subtle: #1c6a82;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, .5);

  /* Lightened so meeting-type labels/borders stay readable on a dark
     surface - the light-theme values are already dark enough to fail
     contrast against a dark body background. */
  --color-solo: #c1bbae;
  --color-solo-external: #9fb3c1;
  --color-internal: #d6ac9d;
  --color-group-external: #b2c9c5;
  --color-hold: #adb5bd;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-fast: 0ms;
    --duration-base: 0ms;
  }
}

/* Every one of .btn-primary's colors is baked into Bootstrap's compiled
   CSS as a literal hex from its default blue - none of them actually read
   var(--bs-primary) at runtime, despite that variable existing and
   resolving correctly (confirmed via getComputedStyle: --bs-primary is
   #a82e3b on the element, but --bs-btn-bg is still the literal #0d6efd).
   So every color needs overriding by hand, not just the ones that looked
   theme-dependent. */
.btn-primary {
  --bs-btn-bg: var(--bs-primary);
  --bs-btn-border-color: var(--bs-primary);
  /* Darkened from --bs-primary rather than hardcoded. These were literal
     shades of Watermelon (#8f2430 and friends), so on any other accent the
     button turned dark red on hover - green button, red hover. The
     percentages reproduce the weight of those original hand-picked shades,
     so Watermelon looks as it always did while the other themes now darken
     their own colour. */
  --bs-btn-hover-bg: color-mix(in srgb, var(--bs-primary) 62%, black);
  --bs-btn-hover-border-color: color-mix(in srgb, var(--bs-primary) 58%, black);
  --bs-btn-active-bg: color-mix(in srgb, var(--bs-primary) 53%, black);
  --bs-btn-active-border-color: color-mix(in srgb, var(--bs-primary) 49%, black);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
  --bs-btn-disabled-bg: var(--bs-primary);
  --bs-btn-disabled-border-color: var(--bs-primary);
}

body {
  font-size: .875rem;
}

.feather {
  width: 16px;
  height: 16px;
  vertical-align: text-bottom;
}

/*
 * Sidebar
 */

.sidebar {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  z-index: 100; /* Behind the navbar */
  padding: 48px 0 0; /* Height of navbar */
  box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
}

.sidebar-sticky {
  position: relative;
  top: 0;
  height: calc(100vh - 48px);
  padding-top: .5rem;
  overflow-x: hidden;
  overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
}

@supports ((position: -webkit-sticky) or (position: sticky)) {
  .sidebar-sticky {
    position: -webkit-sticky;
    position: sticky;
  }
}

.sidebar .nav-link {
  font-weight: 500;
  color: var(--bs-body-color);
}

.sidebar .nav-link .feather {
  margin-right: 4px;
  color: var(--bs-secondary-color);
}

.sidebar .nav-link.active {
  color: var(--bs-primary-text-emphasis);
}

.sidebar .nav-link:hover .feather,
.sidebar .nav-link.active .feather {
  color: inherit;
}

.sidebar .nav-link:focus-visible {
  outline: 2px solid var(--bs-primary-text-emphasis);
  outline-offset: 2px;
}

.navbar :is(.nav-link, .navbar-brand, #theme-toggle):focus-visible {
  outline: 2px solid var(--bs-white);
  outline-offset: 2px;
}

.sidebar-heading {
  font-size: .75rem;
  text-transform: uppercase;
}

/*
 * Content
 */

[role="main"] {
  padding-top: 88px; /* Space for fixed navbar */
}

@media (min-width: 576px) {
  [role="main"] {
    padding-top: 48px; /* Space for fixed navbar */
  }
}

/*
 * Navbar
 */

.navbar-brand {
  padding-top: .75rem;
  padding-bottom: .75rem;
  padding-left: var(--space-4);
  padding-right: var(--space-4);
  font-size: 1rem;
}

/* Truly centered regardless of how wide the brand/nav-items either side
   are (a flex layout alone can't do that without them being equal width) -
   .navbar is already position:fixed, which is enough of a containing
   block for this without adding position:relative separately. The badge
   and the video button live inside this group so they stay adjacent and
   move together as a single centered unit. */
.navbar-up-next-group {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

.navbar-up-next {
  background-color: rgba(255, 255, 255, .15);
  font-size: .8125rem;
  white-space: nowrap;
}

.navbar-up-next:hover {
  background-color: rgba(255, 255, 255, .25);
  color: #fff;
}

.navbar-up-next-video {
  width: 28px;
  height: 28px;
  background-color: rgba(255, 255, 255, .15);
  flex-shrink: 0;
}

.navbar-up-next-video svg {
  width: 14px;
  height: 14px;
}

.navbar-up-next-video:hover {
  background-color: rgba(255, 255, 255, .25);
  color: #fff;
}

.bg-accent {
  background-color: var(--bs-primary) !important;
}

.border-accent {
  border-color: var(--bs-primary) !important;
}

/* Text-on-body-surface use of the accent, e.g. icon-only buttons that
   represent a primary (not destructive) action - uses the theme-aware
   emphasis token rather than --bs-primary itself, for the same contrast
   reason .btn-outline-primary below does. */
.text-accent {
  color: var(--bs-primary-text-emphasis) !important;
}

/* Same story as .btn-primary above: .dropdown-item.active's background is
   a literal hardcoded hex in Bootstrap's compiled CSS, not var(--bs-primary). */
.dropdown-item.active,
.dropdown-item:active {
  background-color: var(--bs-primary);
}

/* And again for form checkboxes/radios - --bs-form-check-input-checked-*
   are also literal hex, not derived from --bs-primary. */
.form-check-input:checked {
  background-color: var(--bs-primary);
  border-color: var(--bs-primary);
}

.form-check-input:focus {
  border-color: var(--bs-primary-text-emphasis);
  box-shadow: 0 0 0 .25rem rgba(var(--bs-primary-rgb), .25);
}

/* Same story again: .form-control/.form-select's focus ring is Bootstrap's
   literal hardcoded blue, not var(--bs-primary). */
.form-control:focus,
.form-select:focus {
  border-color: var(--bs-primary-text-emphasis);
  box-shadow: 0 0 0 .25rem rgba(var(--bs-primary-rgb), .25);
}

/* .btn-outline-primary's base (unfilled) state pairs --bs-primary with the
   body background as text/border, which is legible in light mode but not
   dark (see the --bs-primary comment above) - so it uses the emphasis
   token instead. Hover/active still fill with the constant --bs-primary,
   which is fine since white hover text on it is what needs the contrast,
   not the page's body background. */
.btn-outline-primary {
  --bs-btn-color: var(--bs-primary-text-emphasis);
  --bs-btn-border-color: var(--bs-primary-text-emphasis);
  --bs-btn-hover-bg: var(--bs-primary);
  --bs-btn-hover-border-color: var(--bs-primary);
  --bs-btn-active-bg: var(--bs-primary);
  --bs-btn-active-border-color: var(--bs-primary);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
  --bs-btn-disabled-color: var(--bs-primary-text-emphasis);
  --bs-btn-disabled-border-color: var(--bs-primary-text-emphasis);
}

/* Same fix, same reason, for Bootstrap's other outline variants - each
   pairs its raw theme color with the body background for text/border,
   which has the identical dark-mode contrast problem primary did. Bootstrap
   already ships theme-aware *-text-emphasis tokens for these (unlike
   primary, which is this app's own addition), so no new tokens needed. */
.btn-outline-danger {
  --bs-btn-color: var(--bs-danger-text-emphasis);
  --bs-btn-border-color: var(--bs-danger-text-emphasis);
  --bs-btn-disabled-color: var(--bs-danger-text-emphasis);
  --bs-btn-disabled-border-color: var(--bs-danger-text-emphasis);
}

.btn-outline-success {
  --bs-btn-color: var(--bs-success-text-emphasis);
  --bs-btn-border-color: var(--bs-success-text-emphasis);
  --bs-btn-disabled-color: var(--bs-success-text-emphasis);
  --bs-btn-disabled-border-color: var(--bs-success-text-emphasis);
}

.btn-outline-warning {
  --bs-btn-color: var(--bs-warning-text-emphasis);
  --bs-btn-border-color: var(--bs-warning-text-emphasis);
  --bs-btn-disabled-color: var(--bs-warning-text-emphasis);
  --bs-btn-disabled-border-color: var(--bs-warning-text-emphasis);
}

.navbar .form-control {
  padding: .75rem 1rem;
  border-width: 0;
  border-radius: 0;
}

.brand {
  font-size: 3rem;
  color: #dc3545;
}

/*
 * Account page - theme colour picker
 */

.theme-color-swatch {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  border: 2px solid transparent;
  padding: 0;
  cursor: pointer;
  position: relative;
}

.theme-color-swatch.active {
  border-color: var(--bs-body-color);
}

.theme-color-swatch.active::after {
  content: "\2713";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: .9rem;
  font-weight: 700;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
}

.form-control-dark {
  color: #fff;
  background-color: rgba(255, 255, 255, .1);
  border-color: rgba(255, 255, 255, .1);
}

.form-control-dark:focus {
  border-color: transparent;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
}


.hr-divider {
    position: relative;
    font-size: 12px;
    line-height: 20px;
    text-align: center;
    text-transform: uppercase
}

.hr-divider:before {
    position: absolute;
    top: 50%;
    display: block;
    content: "";
    width: 100%;
    height: 1px;
    background-color: var(--bs-border-color)
}

.hr-divider-content {
    position: relative;
    z-index: 2;
    display: inline-block;
    padding-left: 1em;
    padding-right: 1em;
    color: var(--bs-secondary-color);
    vertical-align: middle;
    background-color: var(--bs-body-bg);
    font-weight: 300;
}

.hr-divider-heading {
    margin-top: 0;
    margin-bottom: 0;
    font-size: 130%;
    color: inherit
}


.border-SOLO {
  border: 1px var(--color-solo) solid;
  border-left: 8px var(--color-solo) solid;;
}
.border-SOLO-EXTERNAL {
  border: 1px var(--color-solo-external) solid;
  border-left: 8px var(--color-solo-external) solid;;
}
.border-INTERNAL {
  border: 1px var(--color-internal) solid;
  border-left: 8px var(--color-internal) solid;;
}
.border-GROUP-EXTERNAL {
  border: 1px var(--color-group-external) solid;
  border-left: 8px var(--color-group-external) solid;;
}
.border-HOLD {
  border: 1px var(--color-hold) solid;
  border-left: 8px var(--color-hold) solid;;
}

.text-SOLO {
  color: var(--color-solo);
}
.text-SOLO-EXTERNAL {
  color: var(--color-solo-external);
}
.text-INTERNAL {
  color: var(--color-internal);
}
.text-GROUP-EXTERNAL {
  color: var(--color-group-external);
}
.text-HOLD {
  color: var(--color-hold);
}

.feather-24 {
  width: 24px;
  height: 24px;
}

.index-border {
  border-top: solid var(--bs-primary) 20px;
}

.all-scroll {
  cursor: all-scroll;
}

.avatar-circle {
  width: 40px;
  height: 40px;
  text-align: center;
  border-radius: 50%;
  -webkit-border-radius: 50%;
  -moz-border-radius: 50%;
}

.initials {
  position: relative;
  top: 10px; /* 25% of parent */
  font-size: 20px; /* 50% of parent */
  line-height: 20px; /* 50% of parent */
  text-align: center;
  font-weight: bold;
}

/* Compact variant for inline rows (e.g. an action item's linked assignee).
   Centers via flexbox rather than .initials' usual top-offset hack (tuned
   for .avatar-circle's 40px and fragile at other sizes) - this guarantees
   the initials sit dead-center regardless of exact font metrics. */
.avatar-circle-sm {
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.avatar-circle-sm .initials {
  position: static;
  top: auto;
  font-size: 11px;
  line-height: 1;
}

.theme-option .theme-check {
  display: none;
}

[data-bs-theme="light"] .theme-option[data-theme-value="light"] .theme-check,
[data-bs-theme="dark"] .theme-option[data-theme-value="dark"] .theme-check {
  display: inline;
}

/* Standalone toggle button (login.html, for logged-out visitors who have
   no Settings dropdown to hold the Light/Dark mode options above). */
#theme-toggle .theme-icon-light {
  display: none;
}

[data-bs-theme="dark"] #theme-toggle .theme-icon-dark {
  display: none;
}

[data-bs-theme="dark"] #theme-toggle .theme-icon-light {
  display: inline;
}

/* Agenda and Action rows share this column grid so the icon, meta field
   and delete control line up vertically between the two sections even
   though each section's rows have a different number of controls
   (Agenda also has an edit-pencil, Action doesn't) - both control columns
   are the same fixed width and flush right, so the delete X always ends
   up in the same place regardless of what else shares its column. */
.meeting-row-icon-col {
  width: 2.5rem;
  flex-shrink: 0;
  display: flex;
  justify-content: center;
}

.meeting-row-meta-col {
  width: 11.25rem;
  flex-shrink: 0;
}

.meeting-row-controls-col {
  width: 4.5rem;
  flex-shrink: 0;
}

.agenda-item, .action-item {
  transition: background-color var(--duration-fast) var(--ease-out);
}

.agenda-item:hover, .action-item:hover {
  background-color: var(--bs-tertiary-bg);
}

.message-log {
  max-height: 25rem;
  display: flex;
  flex-direction: column-reverse;
}

.inline-title-input, .inline-assignee-input {
  border: none;
  background: transparent;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
}

.inline-title-input {
  width: 80%;
  min-width: 200px;
}

.inline-assignee-input {
  width: 100%;
}

.agenda-item-duration-input.inline-assignee-input {
  width: 3rem;
}

.inline-title-input:focus, .inline-assignee-input:focus {
  outline: none;
  box-shadow: none;
  border-bottom: 1px dashed #ccc;
}

/*
 * Sortable / sticky-header tables
 */

.table-sortable thead th[data-sort] {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

.table-sortable thead th[data-sort]:hover {
  background-color: var(--bs-tertiary-bg);
}

.table-sortable thead th[data-sort]:focus-visible {
  outline: 2px solid var(--bs-primary-text-emphasis);
  outline-offset: -2px;
}

.table-sortable thead th[data-sort]::after {
  content: "";
  display: inline-block;
  width: 0;
  height: 0;
  margin-left: var(--space-1);
  vertical-align: middle;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-bottom: 5px solid var(--bs-secondary-color);
  opacity: .4;
}

.table-sortable thead th[data-sort][aria-sort="ascending"]::after {
  opacity: 1;
}

.table-sortable thead th[data-sort][aria-sort="descending"]::after {
  border-bottom: none;
  border-top: 5px solid var(--bs-secondary-color);
  opacity: 1;
}

/* Sticks just below the fixed navbar - same breakpoint/offset [role="main"]
   already uses to clear it. */
.table-sticky-header thead th {
  position: sticky;
  top: 88px;
  background-color: var(--bs-body-bg);
  z-index: 10;
}

@media (min-width: 576px) {
  .table-sticky-header thead th {
    top: 48px;
  }
}

/* tui-calendar's own JS sets an explicit inline background-color on
   regular day columns (var(--bs-body-bg), from the theme's
   common.backgroundColor) but leaves narrow (weekend) columns as
   background-color: inherit - against this app's near-white body
   background the border between two explicit same-color columns is
   imperceptible, while the inherited/transparent narrow columns happen
   to show it. Matching the exact inline value (rather than targeting
   .toastui-calendar-column generally) leaves the "today" column's own
   inline highlight tint - a different background-color value - alone.
   !important is required to win over the inline style itself. */
#calendar .toastui-calendar-column[style*="background-color: var(--bs-body-bg)"] {
  background-color: transparent !important;
}

/* Keyboard-highlight for the Timezones dropdown's search-and-arrow-keys
   navigation - distinct from .active (the tick/selected state), since a
   row can be highlighted without being selected and vice versa. Skipped
   when the row is also .active: Bootstrap's own selected style is a dark
   background with white text, and this rule's background alone (equal
   specificity, loaded after Bootstrap's CSS) would otherwise win and
   leave that white text stranded on a light background. */
.free-time-timezone-option.keyboard-highlight:not(.active) {
  background-color: var(--bs-tertiary-bg);
}

/* Person page's GitHub-contributions-style heatmap (contribution_graph.py)
   - weeks as columns, one square per day. A CSS grid rather than fixed
   pixel sizes so the whole thing scales up as the browser widens instead
   of sitting at a constant size - attendee.html emits the label column
   (8 items: a corner spacer + 7 weekday labels) then, for each week, a
   month-label cell followed by 7 day cells, all as one flat sequence;
   grid-auto-flow: column packs that sequence into columns using the 8
   explicit row tracks below, without needing any explicit grid-row/
   grid-column indices on individual cells. Levels are a fixed, absolute
   color scale (not scaled per-person, unlike GitHub's own relative
   scale) so the same color means the same amount of time on every
   person's page. */
.contribution-grid {
  display: grid;
  grid-template-columns: 20px;
  grid-auto-columns: minmax(0, 1fr);
  grid-template-rows: 15px repeat(7, auto);
  grid-auto-flow: column;
  gap: 4px;
  min-width: 300px;
  max-width: 700px;
  overflow-x: auto;
}

.contribution-weekday-label {
  display: flex;
  align-items: center;
  font-size: 0.6rem;
}

.contribution-month-label {
  font-size: 0.65rem;
  white-space: nowrap;
}

.contribution-cell {
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 3px;
  background-color: var(--bs-tertiary-bg);
  text-decoration: none;
}

.contribution-cell.level-1 { background-color: color-mix(in srgb, var(--bs-primary) 25%, var(--bs-tertiary-bg)); }
.contribution-cell.level-2 { background-color: color-mix(in srgb, var(--bs-primary) 50%, var(--bs-tertiary-bg)); }
.contribution-cell.level-3 { background-color: color-mix(in srgb, var(--bs-primary) 75%, var(--bs-tertiary-bg)); }
.contribution-cell.level-4 { background-color: var(--bs-primary); }

/* "Today" is the only cell styled differently from its actual time
   level - a bright, unrelated accent color (not a shade of the level
   scale itself) so it pops as a clear "you are here" marker regardless
   of how much time that day happens to have. */
.contribution-cell.contribution-today {
  background-color: var(--bs-warning) !important;
  box-shadow: 0 0 0 1px var(--bs-body-bg);
}

/* ============================================================
   Landing page (index.html) - the pre-login marketing page.

   The hero is scroll-linked: a cluttered week gradually declutters
   into reclaimed free time as the visitor scrolls. index.html's own
   script publishes scroll progress as a single --p custom property
   (0 -> 1) on .landing-hero-track, and every animated piece below
   derives its own state from that one variable - so the whole hero
   costs one style write per frame rather than one per element.

   Deliberately JS-driven rather than using CSS animation-timeline:
   scroll(), which still isn't universally supported - one code path
   that works everywhere beats two that can diverge.
   ============================================================ */

@property --p {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

/* The tall track scrolls past while the panel inside stays pinned;
   that scroll distance is what drives --p from 0 to 1. */
.landing-hero-track {
  position: relative;
  height: 300vh;
}

.landing-hero-panel {
  position: sticky;
  top: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-5);
  padding: var(--space-7) var(--space-4);
  overflow: hidden;
}

/* Grid-stacking (rather than absolute positioning) lets the two
   headline states cross-fade in place while the container still
   sizes itself to the taller of the two. */
.landing-swap {
  display: grid;
  text-align: center;
}

.landing-swap > * {
  grid-area: 1 / 1;
  margin: 0;
}

/* These two ranges deliberately overlap (the outgoing line is still
   fading while the incoming one starts): with a gap instead, there's a
   stretch mid-scroll where neither is visible and the hero looks
   headline-less. */
.landing-headline-before {
  opacity: clamp(0, calc(1 - var(--p) * 2.6), 1);
}

.landing-headline-after {
  opacity: clamp(0, calc((var(--p) - 0.3) * 3.6), 1);
}

.landing-hero-sub {
  max-width: 34rem;
  margin: 0 auto;
}

/* Week grid mockup ------------------------------------------- */

.landing-week {
  width: min(100%, 60rem);
  background-color: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--space-3);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

.landing-week-head,
.landing-week-body {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
}

.landing-week-head {
  background-color: var(--bs-tertiary-bg);
  border-bottom: 1px solid var(--bs-border-color);
}

.landing-week-head > div {
  padding: var(--space-2) var(--space-3);
  font-size: .75rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
  text-align: center;
}

.landing-week-body {
  /* Hour gridlines, so the mockup reads as a calendar rather than an
     abstract chart - without needing 10 more elements per column. */
  background-image: repeating-linear-gradient(
    to bottom,
    var(--bs-border-color) 0, var(--bs-border-color) 1px,
    transparent 1px, transparent 10%
  );
  height: 44vh;
  min-height: 15rem;
}

.landing-col {
  position: relative;
  border-right: 1px solid var(--bs-border-color);
}

.landing-col:last-child {
  border-right: 0;
}

/* One slot in the week. --t/--h place it (as a % of the 08:00-18:00
   day the grid represents); --d staggers when it animates, so a
   column empties progressively instead of all at once. */
.ls {
  position: absolute;
  left: 5%;
  right: 5%;
  top: calc(var(--t) * 1%);
  height: calc(var(--h) * 1%);
  border-radius: 4px;
  border-left: 3px solid;
  padding: 1px 4px;
  font-size: .625rem;
  line-height: 1.25;
  color: var(--bs-emphasis-color);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The meetings that clear out - each fades and drifts away once --p
   passes its own staggered threshold. */
.ls-clutter {
  --bp: clamp(0, calc((var(--p) - var(--d) * 0.3) * 3.2), 1);
  opacity: calc(1 - var(--bp));
  transform: translateX(calc(var(--bp) * 10%)) scale(calc(1 - var(--bp) * 0.14));
}

/* The reclaimed time growing into the vacated space - starts later
   than the clutter begins leaving, so it reads as a consequence
   rather than a simultaneous swap. */
.ls-free {
  --bp: clamp(0, calc((var(--p) - 0.4 - var(--d) * 0.18) * 3.6), 1);
  opacity: var(--bp);
  transform: scaleY(var(--bp));
  transform-origin: top center;
  display: flex;
  align-items: center;
  font-weight: 600;
}

.ls-primary {
  background-color: color-mix(in srgb, var(--bs-primary) 22%, var(--bs-body-bg));
  border-left-color: var(--bs-primary);
}

.ls-info {
  background-color: color-mix(in srgb, var(--bs-info) 22%, var(--bs-body-bg));
  border-left-color: var(--bs-info);
}

.ls-warning {
  background-color: color-mix(in srgb, var(--bs-warning) 22%, var(--bs-body-bg));
  border-left-color: var(--bs-warning);
}

.ls-muted {
  background-color: color-mix(in srgb, var(--bs-secondary-color) 18%, var(--bs-body-bg));
  border-left-color: var(--bs-secondary-color);
}

.ls-green {
  background-color: color-mix(in srgb, var(--bs-success) 22%, var(--bs-body-bg));
  border-left-color: var(--bs-success);
  color: var(--bs-success-text-emphasis);
}

/* Reclaimed-hours readout + scroll cue ----------------------- */

.landing-reclaimed {
  opacity: clamp(0, calc((var(--p) - 0.52) * 3.4), 1);
  font-variant-numeric: tabular-nums;
}

.landing-scroll-cue {
  opacity: clamp(0, calc(1 - var(--p) * 5), 1);
  font-size: .8125rem;
}

/* Sections below the hero ----------------------------------- */

.landing-section {
  padding: var(--space-8) var(--space-4);
}

.landing-feature {
  height: 100%;
  padding: var(--space-6);
  background-color: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--space-3);
  transition: transform .2s ease, box-shadow .2s ease;
}

.landing-feature:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.landing-feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin-bottom: var(--space-4);
  border-radius: var(--space-2);
  background-color: color-mix(in srgb, var(--bs-primary) 15%, var(--bs-body-bg));
  color: var(--bs-primary);
}

.landing-step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  margin-bottom: var(--space-3);
  border-radius: 50%;
  background-color: var(--bs-primary);
  color: #fff;
  font-size: .875rem;
  font-weight: 600;
}

/* Fades each section in as it enters the viewport - added by
   index.html's IntersectionObserver, kept separate from the hero's
   --p so ordinary sections don't pay for scroll maths. */
.landing-reveal {
  opacity: 0;
  transform: translateY(1.25rem);
  transition: opacity .6s ease, transform .6s ease;
}

.landing-reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Short viewports - small laptop windows, or a phone in landscape.
   The hero panel is pinned, so it can't scroll internally: anything
   taller than the viewport is cut off permanently rather than just
   being below the fold. Tighten the spacing and let the grid shrink
   so the whole composition keeps fitting. */
@media (max-height: 700px) {
  .landing-hero-panel {
    gap: var(--space-3);
    padding: var(--space-4);
  }

  .landing-hero-panel h1 {
    font-size: calc(1.4rem + 1.5vw);
  }

  .landing-hero-panel .lead {
    font-size: 1rem;
  }

  .landing-week-body {
    min-height: 11rem;
  }
}

@media (max-height: 520px) {
  .landing-week-body {
    min-height: 9rem;
  }

  /* Least important element here, and by this height it's the
     difference between the calendar fitting and not. */
  .landing-scroll-cue {
    display: none;
  }
}

/* Motion is the whole point of the hero, so rather than freeze it
   half-finished this collapses the scroll track and jumps straight
   to the resolved "week reclaimed" state (see index.html, which sets
   --p to 1) - the same message, delivered statically. */
@media (prefers-reduced-motion: reduce) {
  .landing-hero-track {
    height: auto;
  }

  .landing-hero-panel {
    position: static;
    min-height: 0;
  }

  .landing-scroll-cue {
    display: none;
  }

  .landing-reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .landing-feature:hover {
    transform: none;
  }
}
/* ---------------------------------------------------------------------------
   Dark navbar text for light accent colours.

   The navbar is `navbar-dark ... bg-accent`, so its text is white over
   whatever --bs-primary resolves to. That is fine for the darker accents but
   unreadable on the light ones - measured against #ffffff, Orange (#fcab10)
   gives 1.91:1 and Sky Aqua (#54c6eb) 1.97:1, where WCAG AA asks for 4.5:1.
   The same text in #212529 scores 8.06:1 and 7.83:1.

   Applied by adding .navbar-accent-dark in base.html for those themes.

   !important is needed rather than preferred: Bootstrap's own .text-light and
   .text-white utilities are declared with !important, so no amount of
   specificity beats them. Scoped to .navbar.navbar-accent-dark so it cannot
   affect anything outside this one bar.
   --------------------------------------------------------------------------- */
.navbar.navbar-accent-dark,
.navbar.navbar-accent-dark .navbar-brand,
.navbar.navbar-accent-dark .nav-link,
.navbar.navbar-accent-dark .nav-link.text-light,
.navbar.navbar-accent-dark .nav-link.text-white,
.navbar.navbar-accent-dark .text-light,
.navbar.navbar-accent-dark .text-white,
.navbar.navbar-accent-dark .navbar-up-next,
.navbar.navbar-accent-dark .navbar-up-next-video,
.navbar.navbar-accent-dark .btn-outline-light {
  color: #212529 !important;
}

/* Keep hover/focus dark too - .navbar-up-next:hover sets #fff explicitly, and
   btn-outline-light's hover fills white and switches the label to dark, which
   on a light accent reads as the button vanishing. */
.navbar.navbar-accent-dark .navbar-up-next:hover,
.navbar.navbar-accent-dark .navbar-up-next:focus,
.navbar.navbar-accent-dark .navbar-up-next-video:hover,
.navbar.navbar-accent-dark .text-light:hover,
.navbar.navbar-accent-dark .nav-link:hover,
.navbar.navbar-accent-dark .nav-link:focus,
.navbar.navbar-accent-dark .btn-outline-light:hover,
.navbar.navbar-accent-dark .btn-outline-light:focus,
.navbar.navbar-accent-dark .btn-outline-light:active {
  color: #212529 !important;
}

/* The translucent white pill/circle behind "Up next" reads as a pale smudge on
   a light accent; a translucent black gives the same subtle lift. */
.navbar.navbar-accent-dark .navbar-up-next,
.navbar.navbar-accent-dark .navbar-up-next-video {
  background-color: rgba(0, 0, 0, .12);
}

.navbar.navbar-accent-dark .navbar-up-next:hover,
.navbar.navbar-accent-dark .navbar-up-next-video:hover {
  background-color: rgba(0, 0, 0, .2);
}

.navbar.navbar-accent-dark .btn-outline-light:hover,
.navbar.navbar-accent-dark .btn-outline-light:focus {
  background-color: rgba(0, 0, 0, .12);
}

/* The hamburger/collapse toggle is a background-image, so colour has no
   effect on it - swap in the dark variant of Bootstrap's own icon. */
.navbar.navbar-accent-dark .navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}

/* Feather icons are strokes, so they follow currentColor - but the spinner
   border needs telling explicitly. */
.navbar.navbar-accent-dark .spinner-border {
  border-color: #212529;
  border-right-color: transparent;
}
