/* Vertical profile sheet for the planned route.
   Sits at the bottom of the map, collapsed to zero height until opened. z-index 999 puts it just
   under the HUD strip at 1000, so when the GPS is on the two stack instead of overlapping. */

.routeProfileSheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 999;
  max-height: 0;
  overflow: hidden;
  background: linear-gradient(180deg, rgba(8, 6, 34, 0.93) 0%, rgba(2, 3, 22, 0.68) 100%);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 -4px 18px rgba(0, 0, 0, 0.45);
  transition: max-height 0.25s ease-in-out;
  /* while closed it must not swallow taps meant for the map underneath */
  pointer-events: none;
}

.routeProfileSheetOpen {
  /* tall enough to read the airspace stack, short enough to leave the map the larger half */
  max-height: min(46vh, 380px);
  pointer-events: auto;
}

/* The peek: enough to see the shape of the airspace and where the plan hits it, and little
   enough that it costs almost no map. Tap anywhere on it to open the full chart. */
.routeProfileSheetPeek {
  /* Deliberately far larger than the strip ever is. max-height here exists only so the open and
     close can be animated - the sheet's real height is its own content, and a cap set anywhere
     near that content is a clipping bug waiting for a device that disagrees about size.
     The old 104px cleared its content by four pixels on a desktop; a home indicator adds thirty
     to the padding, and larger device text adds more again, and either one clipped the bottom of
     the strip so a sliver of the advice line showed through the edge. Measured at 100px, 134px
     and 136px on those three. Capping well above all of them means it can no longer bind. */
  max-height: calc(200px + env(safe-area-inset-bottom, 0px));
  pointer-events: auto;
  cursor: pointer;
}

.routeProfileSheetPeek .routeProfileHeader {
  padding: 4px 12px 0 12px;
}

.routeProfileSheetPeek .routeProfileTitle {
  font-size: 1.2rem;
}

/* the conflict list, the message line and the collapse chevron all belong to the full view */
.routeProfileSheetPeek .routeProfileConflicts,
.routeProfileSheetPeek .routeProfileMessage,
.routeProfileSheetPeek #routeProfileCollapseButton {
  display: none;
}

.routeProfileSheetPeek .routeProfileChart,
.routeProfileSheetPeek .routeProfileAxis {
  height: 56px;
}

.routeProfileSheetPeek .routeProfileBody {
  padding-bottom: calc(4px + env(safe-area-inset-bottom, 0px));
}

.routeProfileHeaderButtons {
  display: flex;
  gap: 6px;
  flex: 0 0 auto;
}

.routeProfileHeader {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 12px 4px 12px;
}

.routeProfileTitleGroup {
  text-align: left;
  min-width: 0;
}

.routeProfileTitle {
  margin: 0;
  font-size: 1.35rem;
  font-weight: bold;
  color: rgb(228, 195, 47);
}

/* while Direct To is active this chart describes a course the aeroplane is not flying, so it
   stops looking like the ordinary case */
.routeProfileTitleWarning {
  color: rgba(255, 193, 87, 0.95);
}

.routeProfileSubtitle {
  margin: 1px 0 0 0;
  font-size: 1.15rem;
  color: rgba(255, 255, 255, 0.6);
}

/* carries the incomplete-data warning into the peek, where the full sentence does not fit */
.routeProfileSubtitleWarning {
  color: rgba(255, 193, 87, 0.95);
}

.routeProfileClose {
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: 7px;
  color: #ffffff;
  font-size: 1.3rem;
  line-height: 1;
  padding: 5px 9px;
  cursor: pointer;
}

.routeProfileClose:hover {
  background-color: rgba(255, 255, 255, 0.12);
}

.routeProfileBody {
  padding: 0 10px calc(8px + env(safe-area-inset-bottom, 0px)) 10px;
}

/* The throwaway map used only to fetch airspace tiles for the route. It must be laid out and have
   a real size or maplibre will not render and no tiles are fetched - so it is moved off-screen
   rather than hidden. Nobody ever sees it, and it is destroyed as soon as it has been read. */
.routeProfileDataMap {
  position: absolute;
  left: -10000px;
  top: 0;
  pointer-events: none;
  visibility: visible;
}

/* axis on the left, plot to the right of it */
.routeProfilePlot {
  display: flex;
  align-items: stretch;
  /* Sky, not paper. A vertical profile is a side-on view of the air and the ground, and every
     tool that does this well says so with colour: blue above, green below, no legend needed. A
     near-white panel was readable but read as a spreadsheet dropped into the map, and it glared.
     Paling toward the horizon, which is what the sky does - but far lighter than it first was,
     and opaque.

     Lighter because the old gradient was too dark to draw on. Measured against it, not one mark on
     the chart cleared the 3:1 WCAG asks of a graphical object: the class D blue managed 1.28, the
     restricted red 1.03, and the magenta planned line 1.00 - the same luminance as the sky behind
     it. The blues were merely the pair you noticed first, because they shared a hue as well as a
     value. Lifted, seven of the ten marks clear 3:1 unaided and the rest are cased.

     Opaque because it was not before, and that quietly undid the lift. The old stops carried alpha
     (cc/e3/cf) over a sheet which is itself translucent over the moving map, so #2f6ea8 actually
     landed near #27598d and drifted with whatever the map was showing. A chart you read altitudes
     off should not have terrain sliding about behind it. */
  background: linear-gradient(180deg, #b3d2e8 0%, #cfe3f2 55%, #eaf5fc 100%);
  /* border: 1px solid rgba(10, 30, 50, 0.45) */
  border-radius: 8px;
  overflow: hidden;
}

/* Fixed while the plot scrolls under it. Without this the altitude scale slides off the screen
   the moment you pan sideways, which makes the whole chart unreadable. */
/* A solid strip rather than transparent sky. The gradient runs from deep blue at altitude to pale
   at the horizon, so a label sitting on it would need to be light at the top of the scale and dark
   at the bottom - impossible with one colour. Its own dark ground fixes that permanently, and it
   also separates the fixed axis from the plot that scrolls under it. */
.routeProfileAxis {
  flex: 0 0 34px;
  height: 190px;
  background: #050425d9;
  border-right: 1px solid rgba(255, 255, 255, 0.16);
}

/* The plot is allowed to be wider than the sheet: on a phone a seventeen-leg route squeezed to
   350px gives each leg 20px, which is neither readable nor draggable. It gets the width it needs
   and this scrolls. touch-action: pan-x lets the browser own the sideways swipe while the
   pointer handlers keep the vertical drag that edits a level. */
.routeProfileChart {
  flex: 1 1 auto;
  min-width: 0;
  height: 190px;
  overflow-x: auto;
  overflow-y: hidden;
  /* Every gesture is handled in JavaScript - sideways drags pan the plot, vertical ones on an
     altitude line edit it. The browser is told to keep out entirely, because leaving it pan-x
     meant it arbitrated between the two and could cancel a drag mid-gesture. Panning is driven
     by scrollLeft, so the wheel and the scrollbar still work as they always did. */
  touch-action: none;
  -webkit-overflow-scrolling: touch;
  cursor: grab;
}

.routeProfileChart.routeProfilePanning {
  cursor: grabbing;
}

/* the line is an edit control; it keeps its own cursor */
.routeProfileChart .routeProfileLegHit {
  cursor: ns-resize;
}

.routeProfileChart::-webkit-scrollbar {
  height: 5px;
}

.routeProfileChart::-webkit-scrollbar-thumb {
  background-color: rgba(255, 255, 255, 0.25);
  border-radius: 3px;
}

/* a peek is a whole-route glance, so it never scrolls and needs no axis */
.routeProfileSheetPeek .routeProfileAxis {
  display: none;
}

.routeProfileSheetPeek .routeProfileChart {
  overflow-x: hidden;
  touch-action: auto;
  cursor: pointer;
}

/* the ribbon is drawn on the sheet itself, so the pale chart panel is dropped in this state */
.routeProfileSheetPeek .routeProfilePlot {
  background: transparent;
  border: none;
  border-radius: 0;
}

/* Height fills the box, width comes from the SVG's own width attribute - the plot is deliberately
   wider than its container on a long route, and a width:100% here would squash it back and undo
   the scrolling. The renderer never emits less than the container width, so it still fills. */
.routeProfileSvg {
  display: block;
  height: 100%;
}

/* ---- chart furniture ---- */

/* Dark on sky now, not white. White worked when the sky was deep - it read at 5.36:1 against the
   old top stop - but against the lifted gradient the same line falls to 1.58 and disappears. The
   gridlines had to move with the background; they are the one element the lift breaks. Kept
   translucent so they stay a scaffold rather than becoming marks in their own right. */
.routeProfileGrid {
  stroke: rgba(16, 48, 70, 0.25);
  stroke-width: 1;
}

/* the distance labels live in the plot, low down where the sky is pale, so they stay dark */
.routeProfileAxisLabel {
  fill: #16394f;
  font-size: 10px;
}

/* the altitude labels live on the dark axis strip, so they are light */
#routeProfileAxis .routeProfileAxisLabel {
  fill: rgba(255, 255, 255, 0.85);
}

/* flipped dark for the same reason as the gridlines above */
.routeProfileWaypointTick {
  stroke: rgba(16, 48, 70, 0.3);
  stroke-width: 1;
  stroke-dasharray: 3 3;
}

.routeProfileWaypointLabel {
  fill: #16394f;
  font-size: 9px;
}

/* Real place names on the axis rather than DEP / 1 / ARR, on a chip so they stay readable
   wherever the terrain happens to be. */
/* A white chip on the pale end of the lifted sky is 1.11:1 against its own background, so the
   border is now the only thing holding it together and it has been darkened to do that job. */
.routeProfileWaypointChip {
  fill: #ffffff;
  opacity: 0.92;
  stroke: #5b7f96;
  stroke-width: 1;
}

/* Airspace blocks. Faint fill so overlapping airspace stays readable - in the Netherlands a
   route is routinely inside four at once - with the floor picked out, since the floor is the
   line a VFR pilot is actually trying to stay under. */
/* Barely there. The floor line carries the information; the fill only says which way is "inside",
   and four overlapping airspaces is the normal case rather than the exception. */
.routeProfileBand {
  opacity: 0.13;
}

/* the hatch carries its own weight; tinting it down to 13% would erase it */
.routeProfileBandHatched {
  opacity: 1;
}

.routeProfileBandFloor {
  stroke-width: 2;
  opacity: 0.95;
}

/* The label takes its own airspace's colour so a name and its block belong together without a
   leader line. The white halo is what lets a saturated blue label survive the top of the sky
   gradient, where the background is nearly as dark as the ink. */
.routeProfileBandLabel {
  font-size: 9.5px;
  font-weight: 600;
  paint-order: stroke;
  stroke: #ffffff;
  stroke-width: 2.6px;
  stroke-linejoin: round;
}

/* ---- the ground ----
   Solid and opaque, unlike the airspace blocks: you can be above or below an airspace shelf, but
   the ground is the floor of the picture and should read as such. Earthy rather than chart-
   coloured so it never competes with an airspace band for meaning. */
/* Opaque, not translucent: the ground is the floor of the picture, not another layer of airspace
   you might be above or below. Solid earth against a pale sky is what makes it read as ground
   without a legend - the previous gold fill was both see-through and the same hue as the planned
   altitude line, so the two most important things on the chart were competing. */
/* Green, because that is what ground is on every profile a pilot has seen, and because against a
   blue sky it needs no explaining at all - which the earlier brown-on-navy did. */
.routeProfileTerrainFill {
  fill: #5aa84a;
}

.routeProfileTerrainLine {
  fill: none;
  stroke: #2f6b28;
  stroke-width: 1.3;
  stroke-linejoin: round;
}

/* terrain clearance is not a rule you can be cleared through, so it gets its own colour */
.routeProfileConflict-terrain {
  border-left-color: #b3651f;
}

/* ---- the planned altitude ---- */

/* Magenta, because that is the colour a planned course already is on every moving map a pilot has
   used - and because it leaves gold doing one job instead of two. On the old dark panel the
   planned level and the terrain line were both warm yellows, so the eye could not separate "the
   ground" from "my altitude", which are the two things the chart exists to compare. */
.routeProfilePlanned {
  stroke: #c2189b;
  stroke-width: 2.8;
  stroke-linecap: round;
}

/* a leg still on the default reads as unfinished, not as a decision */
.routeProfilePlannedDefault {
  stroke: rgba(194, 24, 155, 0.45);
  stroke-dasharray: 5 4;
  stroke-width: 2.2;
}

.routeProfilePlannedStep {
  stroke: rgba(194, 24, 155, 0.75);
  stroke-width: 1.8;
  stroke-dasharray: 2 2;
}

/* haloed for the same reason the airspace labels are: it has to survive the whole sky gradient */
.routeProfilePlannedLabel {
  fill: #a3117f;
  font-size: 10px;
  font-weight: bold;
  paint-order: stroke;
  stroke: #ffffff;
  stroke-width: 3px;
  stroke-linejoin: round;
}

.routeProfilePlannedLabelDefault {
  fill: #8e4d80;
  font-weight: normal;
}

/* a dot at each turning point, so the route's shape is legible as a route and not just a level */
.routeProfileWaypointDot {
  fill: #ffffff;
  stroke: #c2189b;
  stroke-width: 2;
}

/* The grip. Small, because it is a hint that the line moves rather than the thing you have to
   hit - the hit area below is far bigger than this. */
.routeProfileLegHandle {
  fill: #c2189b;
  opacity: 0.9;
  pointer-events: none;
}

/* What the finger actually lands on: 24px tall and invisible. A 2.6px line is not something
   anyone can reliably hit on a phone, least of all in turbulence.

   touch-action: none overrides the container's pan-x for this element alone. The container needs
   pan-x so the plot can be scrolled sideways, but on the altitude line itself that lets the
   browser claim a gesture the moment it drifts horizontally - which on a touchscreen is most of
   them - and cancel the drag. The line is unambiguously an edit control, so it takes every
   gesture that starts on it and the rest of the chart is what you scroll with. */
.routeProfileLegHit {
  stroke: transparent;
  stroke-width: 24;
  cursor: ns-resize;
  pointer-events: stroke;
  touch-action: none;
}

/* ---- the peek ribbon ----
   The peek is not a chart. It is one bar along the route, coloured by whatever constrains you at
   each point, with a line of text above it saying what is coming and a line below saying what to
   do about it. Drawn straight onto the dark sheet - it needs no pale panel because it carries no
   airspace stack to disentangle. */
.routeProfileRibbonSegment {
  opacity: 0.8;
}

.routeProfileRibbonLabel {
  fill: #ffffff;
  font-size: 9.5px;
  font-weight: 600;
  pointer-events: none;
}

/* the one fact worth reading at a glance, so it gets the size and the weight */
.routeProfileRibbonHeadline {
  fill: #ffb38a;
  font-size: 12px;
  font-weight: bold;
}

.routeProfileRibbonClear {
  fill: #8cdc96;
}

.routeProfileRibbonNumbers {
  fill: rgba(255, 255, 255, 0.72);
  font-size: 11px;
}

.routeProfileRibbonAdvice {
  fill: rgba(255, 255, 255, 0.55);
  font-size: 10px;
}

/* where the aeroplane is on the bar; gold, which is free again now the planned level is magenta */
.routeProfileRibbonShip {
  fill: rgb(228, 195, 47);
}

/* ---- messages and conflicts ---- */

.routeProfileMessage {
  display: none;
  margin: 2px 0 0 0;
  font-size: 1.15rem;
  text-align: left;
  color: rgba(255, 193, 87, 0.9);
}

/* shown only when there is something to say - and the peek's own rule still outranks this, so a
   collapsed strip never has the text hanging out of the bottom of it */
.routeProfileMessageVisible {
  display: block;
}

.routeProfileConflicts {
  margin-top: 6px;
  max-height: 92px;
  overflow-y: auto;
}

.routeProfileClear {
  margin: 0;
  font-size: 1.15rem;
  text-align: left;
  color: rgba(140, 220, 150, 0.9);
}

.routeProfileConflictRow {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 3px 6px;
  border-left: 3px solid rgba(255, 255, 255, 0.25);
  margin-bottom: 3px;
  text-align: left;
}

.routeProfileConflict-restricted { border-left-color: #d40000; }
.routeProfileConflict-controlled { border-left-color: #0050d0; }
.routeProfileConflict-rmz        { border-left-color: #909090; }

.routeProfileConflictName {
  flex: 0 1 auto;
  font-size: 1.2rem;
  color: #ffffff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.routeProfileConflictDetail {
  flex: 1 1 auto;
  font-size: 1.1rem;
  color: rgba(255, 255, 255, 0.6);
}

.routeProfileButtonGlyph {
  font-size: 1.7rem;
  line-height: 1;
  color: #ffffff;
}

/* When the GPS is on the HUD strip owns the very bottom, so the profile stacks directly on top of
   it. The offset is the strip's measured height (routeProfileUpdateHudOffset), not a constant -
   the strip grows with the font size and with env(safe-area-inset-bottom), and a fixed 58px left
   a visible gap above a 46px strip. The fallback only applies before the first measurement. */
.hudStripActive .routeProfileSheet {
  bottom: var(--hud-strip-height, 46px);
}

/* Everything that has to get out of the open sheet's way, mirroring how .hudStripActive works.
   The distance is a variable because it depends on the sheet's state (peek or expanded), on the
   viewport height, and on whether the HUD has pushed the sheet up as well.

   The 20px is deliberate slack. The on-map buttons are touch-sized and their rendered size moves
   with the device's font scaling, so clearing the sheet by a hair is not the same as clearing it -
   this was 12px and left the bottom row of buttons sitting on the sheet's top edge. */
.routeProfileActive .mapRightColumnContainer {
  padding-bottom: calc(var(--route-profile-height, 0px) + 20px);
}

.routeProfileActive .leaflet-bottom,
.routeProfileActive .maplibregl-ctrl-bottom-left,
.routeProfileActive .maplibregl-ctrl-bottom-right {
  bottom: calc(var(--route-profile-height, 0px) + 4px) !important;
}

/* On a phone the sheet has to leave the map usable, so the chart gives up height first. */
@media (max-width: 620px) {
  .routeProfileChart, .routeProfileAxis { height: 150px; }
  .routeProfileConflicts { max-height: 66px; }
}
