/* The year calendar (/calendar): docs/design/2026-09-redesign/calendar/build.py's
 * CSS on family.css's tokens. The route filter is an inset list with counts on a
 * phone and a chip row in the sidebar from 960 px (the decided spec); day numbers
 * are 15 px (faint ones 12 px), under the 17 px floor on purpose: each month's
 * footer says its best fare in words (NOTES.md "Risks"). */
body.fam.cal{
  --x:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M1.5 1.5l9 9M10.5 1.5l-9 9' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
  --chev:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 14'%3E%3Cpath d='M1.5 1.5 6.5 7l-5 5.5' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --tick:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 12'%3E%3Cpath d='M1.5 6.5 5 10l7.5-8.5' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* the luminous tiles want a darker ink in the dark, and fainter scaffolding */
@media (prefers-color-scheme:dark){body.fam.cal{--on-book:#00210b;--ink3:#4d4d52}}
.page{max-width:640px;display:flex;flex-direction:column}
.side{display:contents}
.side>.head,.side>.trio,.side>.best,.side>.gone,.side>.none{order:1}
.gone{display:flex;gap:8px;align-items:flex-start;color:var(--ink2);margin:14px 2px 0}
.gone svg.g{margin-top:2px}
.gone b{font-weight:600;color:var(--ink)}
.years{order:2}
.side>.group{order:3;margin-top:28px}
.side>.group+.group{margin-top:14px}
.side>.foot{order:4;padding:40px 8px 44px}
/* the sky of "/": tinted green when the view holds a deal */
.sky{position:absolute;left:0;right:0;top:0;height:560px;z-index:0;pointer-events:none;
  background:radial-gradient(90% 60% at 50% 0%,var(--sky2) 0%,transparent 100%),
    linear-gradient(180deg,var(--sky) 0%,var(--ground) 100%)}
body.v-n{--sky:#e4e5ea;--sky2:#f6f6f8}
@media (prefers-color-scheme:dark){
  .sky{height:420px;background:radial-gradient(ellipse 80% 240px at 50% 40px,rgba(48,209,88,.22) 0%,rgba(48,209,88,.07) 52%,transparent 100%)}
  body.v-n .sky{background:radial-gradient(ellipse 80% 240px at 50% 40px,rgba(255,255,255,.08) 0%,transparent 100%)}
}
.head h1{font-size:2.125rem;font-weight:700;letter-spacing:-.03em;line-height:1.1;margin-top:6px}
.lede{color:var(--ink2);margin-top:6px;text-wrap:pretty}
/* controls: the direction as an iOS segmented control; the routes as an inset
   list with counts on a phone, chips from 960 px */
.ctl{margin-top:18px}
.seg{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,5.5em),1fr));background:var(--fill2);border-radius:12px;padding:2px}
/* two halves while each label's longest word ("Singapore") fits one, its
   words wrapping inside it; with larger text the two stack, full width,
   rather than push the page sideways (F3) */
.seg a{text-align:center;padding:4px;border-radius:10px;font-weight:500;min-height:44px;display:grid;place-items:center}
.seg a[aria-current]{background:var(--thumb);font-weight:600;box-shadow:0 3px 8px rgba(0,0,0,.12),0 3px 1px rgba(0,0,0,.04)}
@media (prefers-color-scheme:dark){.seg a[aria-current]{box-shadow:none}}
.rts{display:block;margin-top:12px;background:var(--card);border-radius:var(--r);overflow:hidden}
.rts a{position:relative;display:flex;align-items:center;gap:12px;min-height:52px;padding:10px 16px 10px 18px}
.rts a+a::before{content:"";position:absolute;top:0;left:18px;right:0;height:.5px;background:var(--sep)}
.rts .tx{flex:1;min-width:0}
.rts b{display:block;font-weight:500}
.rts .s{display:block;color:var(--go);font-weight:600;font-variant-numeric:tabular-nums}
.rts .sh{display:none}
.rts a[aria-current] b{font-weight:600}
.rts a[aria-current]::after{content:"";flex:none;width:14px;height:12px;background:var(--ink);-webkit-mask:var(--tick) center/contain no-repeat;mask:var(--tick) center/contain no-repeat}
/* the trio: what it is, said once */
.trio{margin-top:18px;padding:0 2px}
.trio b{display:block;font-weight:600}
.trio p{color:var(--ink2);text-wrap:pretty}
/* the one answer above the year */
.best{position:relative;display:flex;flex-wrap:wrap;gap:10px 12px;align-items:center;
  margin-top:22px;background:var(--card);border-radius:var(--r);padding:14px 36px 14px 14px}
/* the words beside the dot while 10em fit there (a phone's 320 px at the
   default size); with larger text they go under it, whole (F3). The chevron
   keeps the card's right edge, mid-height, wherever the words go. */
.best .t{flex:1 1 10em;min-width:0}
.best>svg.chev{position:absolute;right:16px;top:calc(50% - 7px)}
.best .dot{flex:none;width:44px;height:44px;border-radius:12px;display:grid;place-items:center;font-weight:700;
  background:var(--lum);color:var(--on-book);box-shadow:var(--lum-h);font-variant-numeric:tabular-nums}
.best .dot.g{background:var(--soft);color:var(--go);box-shadow:var(--soft-h)}
.best .dot.n{background:var(--mist);color:var(--ink);box-shadow:none}
.best .l1{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;column-gap:10px}
.best b{font-weight:600;letter-spacing:-.018em}
.best .s{display:block;color:var(--ink2)}
.best .s.go{color:var(--go);font-weight:600}
.best .p{font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
.best .p.go{color:var(--go)}
/* the current cabin: a row that says so, no chevron */
.cabins .lrow[aria-current] b{font-weight:700}
/* --- the year ------------------------------------------------------------ */
.yh{font-size:1.375rem;font-weight:700;letter-spacing:-.022em;margin:34px 4px 12px}
/* Two months a row on a phone (four on a desktop) for as long as a month is
   --mo wide: what seven day numbers need to stay apart at the reader's text
   size (two digits at .9375rem are about 1.1rem wide, a day a seventh of the
   month less its 2 px gaps). With larger text a month takes a row of its own
   before its numbers meet, at any width: the switch follows the text, not a
   px breakpoint (F3). The gap and the side padding are in rem as well, so
   the container query below, which draws a lone month, switches at exactly
   the width where the grid drops to one: 2 x 8.9 + .75 + 2 x .625 = 19.8rem. */
.years{container:years/inline-size}
.yr{--mo:8.9rem;background:var(--card);border-radius:var(--r);padding:18px .625rem 16px;display:grid;
  grid-template-columns:repeat(auto-fill,minmax(min(100%,max(var(--mo),50% - .375rem - 1px)),1fr));gap:24px .75rem;
  /* a lone month's wide end tiles would reach their tap targets past the
     card, and push the page sideways */
  overflow-x:clip}
/* a month is the box a lone month's day numbers measure themselves
   against (cqi, below) */
.mo{scroll-margin-top:24px;min-width:0;container-type:inline-size}
.mo h3{font-size:1.0625rem;font-weight:600;letter-spacing:-.018em;margin:0 0 6px 4px}
.days{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:2px}
.days>*{aspect-ratio:1;display:grid;place-items:center;border-radius:8px;font-size:.875rem;line-height:1;
  font-variant-numeric:tabular-nums;font-style:normal;color:var(--ink3);position:relative}
/* a day we do not check: a faint small number, the calendar's scaffolding */
.days>i{font-size:.75rem}
.days>i.now{color:var(--ink);font-weight:700}
.days>i.now::before{content:"";position:absolute;inset:0;margin:auto;height:100%;aspect-ratio:1;border-radius:50%;box-shadow:inset 0 0 0 1.5px var(--ink)}
.days>a,.days>i.now{font-size:.9375rem}
.days>a{background:var(--mist);color:var(--ink2);font-weight:500;transition:transform .15s}
/* checked days are never side by side: widen each tap target over its faint
   neighbours (the card clips one reaching past its sides, .yr) */
.days>a::after{content:"";position:absolute;inset:-1px -60%}
.days>a:active{transform:scale(.9)}
.days>a.b{background:var(--lum);color:var(--on-book);font-weight:700;box-shadow:inset 0 1px 0 rgba(255,255,255,.28),var(--lum-h)}
@media (hover:hover){.days>a:hover{transform:scale(1.14);z-index:1}}
.days>a.g{background:var(--soft);color:var(--go);font-weight:700;box-shadow:var(--soft-h)}
.days>a.u{background:var(--mist);opacity:.55;font-weight:400}
.days>a.s{background:none;color:var(--ink3);text-decoration:line-through}
/* the dash sits on the tile's own edge, not the mockup's 3 px inside it: in
   a phone's 21 px two-month tile a two-digit day is wider than that inset,
   and the dashes ran through its digits */
.days>a.x{background:none;color:var(--ink2);outline:1.5px dashed var(--ink3);outline-offset:-1px}
.mf{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 8px;margin:8px 4px 0;min-height:1.41em;
  font-variant-numeric:tabular-nums}
.mf b{font-weight:600}
.mf .go{color:var(--go)}
.mf .dd{color:var(--ink2)}
.mf .q{color:var(--ink2);display:inline-flex;align-items:center;gap:6px}
.mf .q svg.g{width:1.05em;height:1.05em}
.mf a,.mf .fa{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 6px}
.mf a{position:relative}
/* a 44 px target with the footer unmoved: where the deal is one 24 px line
   its target reaches 4 px up, into the gap under the tiles (never over one,
   which keeps its own), and the rest down, over the city line that names
   this same deal */
.mf a::before{content:"";position:absolute;inset:-4px 0 -18px}
.mc{color:var(--ink2);margin:0 4px}
.mf .cap{padding:0 6px}
.su .cap{padding:0 8px}
/* not in the mockup, whose footer had no day: the fare and its saving are
   one unit (.fa) that wraps under the day as a whole, so a saving never sits
   on a line of its own, whatever the fare's digits (F6). In a narrow footer
   (a phone's two months, at any text size) the day always goes above, so
   the months of a row read alike. */
.mf{container:mf/inline-size}
@container mf (max-width:10.5em){.mf .dd{flex-basis:100%}}
/* --- the day sheet: :target works with JavaScript off -------------------- */
.sheet{display:none}
.sheet:target{display:block;position:fixed;inset:0;z-index:50}
html:has(.sheet:target){overflow:hidden}
.scrim{position:absolute;inset:0;background:var(--scrim);animation:fade .2s ease}
.panel{position:absolute;left:0;right:0;bottom:0;max-height:92vh;max-height:92dvh;overflow:auto;overscroll-behavior:contain;background:var(--card);
  border-radius:24px 24px 0 0;padding:10px 20px calc(24px + env(safe-area-inset-bottom,0px));box-shadow:var(--shadow);
  animation:rise .32s cubic-bezier(.2,.85,.25,1)}
.panel::before{content:"";display:block;width:38px;height:5px;border-radius:3px;background:var(--ink3);margin:0 auto 12px}
.shut{position:absolute;top:8px;right:8px;width:44px;height:44px;display:grid;place-items:center;color:var(--ink2)}
.shut span{width:30px;height:30px;border-radius:50%;background:var(--fill);display:grid;place-items:center}
.shut span::before{content:"";width:12px;height:12px;background:currentColor;-webkit-mask:var(--x) center/contain no-repeat;mask:var(--x) center/contain no-repeat}
.eb{padding-right:40px}
.eb b{display:block;font-weight:600}
.eb span{color:var(--ink2)}
.sv{display:flex;align-items:center;gap:8px;font-size:1.375rem;font-weight:600;margin-top:18px;letter-spacing:-.018em;line-height:1.25}
.sv.go{color:var(--go)}
.sv svg.g{width:1em;height:1em}
.sr_{font-weight:600;margin-top:4px}
.sp{font-size:3.5rem;font-weight:600;letter-spacing:-.045em;line-height:1.05;margin-top:2px;font-variant-numeric:tabular-nums}
.sp .cur{font-size:.5em;letter-spacing:-.02em;margin-left:.1em}
.sp .cur.pre{font-size:.6em;margin:0 .04em 0 0}
.su{color:var(--ink2)}
.su .cap{margin-left:8px}
/* the sheet's lines wrap as '/''s do under its button: no word left alone
   on a last line ("…cu bagaje / de cală", "două / bilete"); balance where
   a browser has not got pretty yet */
.panel p{text-wrap:balance;text-wrap:pretty}
.sm{color:var(--ink2);margin-top:12px}
/* the separate-tickets risk, in ink as under '/''s button (family.css .risk) */
.panel .risk{margin-top:10px}
.panel .risk svg.gi{vertical-align:-.16em}
.panel .cta{margin:22px 0 0;max-width:none;text-align:center;padding:10px 18px}
.panel .cta.quiet{margin-top:14px;padding:12px 0}
/* the trio's legs, a small timeline (Task 14) */
.legs{list-style:none;margin-top:16px;position:relative}
/* the last column holds a linked leg's chevron on the row's edge, 12 px
   after its price, as in the same-day rows below, so the prices of both
   lists end on one line; a plain leg leaves it empty */
.legs li{display:grid;grid-template-columns:18px minmax(0,1fr) auto 10px;column-gap:10px;padding:6px 0;position:relative}
.legs::before{content:"";position:absolute;left:8px;top:20px;bottom:30px;width:2px;background:var(--fill2)}
.legs li::before{content:"";width:9px;height:9px;border-radius:50%;background:var(--ink3);margin-top:7px;justify-self:center;
  position:relative;box-shadow:0 0 0 3px var(--card)}
.legs b{font-weight:600;display:block}
/* a leg with its own ticket reads as the others do (the mockup's ink2); it
   says it is a link the way the sheet's other rows do, a chevron after its
   price, and the whole row is its target (ruling R17-b) */
.legs :is(span,a){color:var(--ink2)}
.legs a::after{content:"";position:absolute;inset:0}
/* and its focus ring is that target's, the row, as the sheet's other rows' are */
.legs a:focus-visible{outline:none}
.legs a:focus-visible::after{outline:2.5px solid var(--ink);outline-offset:3px;border-radius:10px}
.legs svg.chev{margin-top:5px;justify-self:end}
.legs i{font-style:normal;font-variant-numeric:tabular-nums;color:var(--ink2);white-space:nowrap}
.panel h3{font-size:1.0625rem;font-weight:600;margin:28px 0 2px}
.also{list-style:none}
.also li+li{box-shadow:inset 0 .5px 0 var(--sep)}
.also a,.also .na{display:grid;grid-template-columns:minmax(0,1fr) auto 8px;column-gap:12px;align-items:center;padding:11px 0;min-height:60px}
.also b{grid-column:1;font-weight:600}
.also i{grid-column:2;grid-row:1/span 2;font-style:normal;font-weight:600;font-variant-numeric:tabular-nums;white-space:nowrap}
/* the row's own line: the spans inside it (a saving held to its sign) take
   their colour from it */
.also :is(a,.na)>span{grid-column:1;color:var(--ink2)}
/* with large text the name takes the row and the price drops to the verdict's
   line, as a row's price does on '/'; with larger text still the price takes
   a line of its own under the name, the words the whole row (F3) */
.also{container:also/inline-size}
@container also (max-width:14em){
  .also b{grid-column:1/3}
  .also i{grid-row:2}
}
@container also (max-width:10em){
  .also :is(b,a>span:first-child){grid-column:1/3}
  .also i{grid-column:1;justify-self:start}
  .also b~span{grid-row:3;grid-column:1/3}
  .also a::after{grid-row:1/span 3}
}
/* a deal's words stay green, as in '/'s rows (the mockup let .also span's grey win) */
.also span .go{color:var(--go);font-weight:600}
.also a::after{content:"";grid-column:3;grid-row:1/span 2;width:8px;height:14px;background:var(--ink3);-webkit-mask:var(--chev) center/contain no-repeat;mask:var(--chev) center/contain no-repeat}
.also .na i{color:var(--ink2);font-weight:400}
/* the trio's two ways home: in its sheet a list of links, as a same-day row
   its verdict and then each way with its own link */
.shapes{margin-top:14px}
.also li.tri{padding:11px 0}
.also li.tri>b{display:block;font-weight:600}
.also li.tri>span{display:block;color:var(--ink2)}
.also li.tri a{min-height:44px;padding:6px 0}
@keyframes rise{from{transform:translateY(100%)}to{transform:none}}
@keyframes fade{from{opacity:0}to{opacity:1}}
/* a lone month a row (larger text, or a phone under 360 px): wider tiles, as
   tall as their numbers need rather than square. Two months always have
   room for theirs (--mo); a lone month on the narrowest phone with doubled
   text has not, so its numbers are the reader's size up to what a seventh
   of the month holds apart (9.5cqi of the month, .mo). The media query is
   for a browser with no container queries (iOS 15, the last of the 320 px
   iPhone SE), as before. */
@media (max-width:359px){
  .yr{grid-template-columns:minmax(0,1fr);padding:16px 12px;row-gap:22px}
  .days{gap:3px}
  .days>*{aspect-ratio:1.35}
  .days>i{font-size:.8125rem}
}
@container years (width < 19.8rem){
  .yr{grid-template-columns:minmax(0,1fr);padding:16px 12px;row-gap:22px}
  .days{gap:3px}
  .days>*{aspect-ratio:1.35;font-size:min(.875rem,9.5cqi)}
  .days>i{font-size:min(.8125rem,9.5cqi)}
  .days>a,.days>i.now{font-size:min(.9375rem,9.5cqi)}
}
/* below the mockup's 390 px at the default size (and so sooner with larger
   text) a leg's name takes the whole line and its price drops to the date's,
   as a row's price does on '/' at 320: beside a price and a chevron a name
   like "Amsterdam → Singapore" no longer fits one line */
.legs{container:legs/inline-size}
@container legs (max-width:20.5em){
  .legs li>:is(a,span){grid-area:1/2/2/-1}
  .legs li>i{grid-area:1/3;align-self:end}
  .legs li>svg.chev{grid-area:1/4;align-self:end;margin:0 0 5px}
}
/* and with larger text, where the date and the price no longer share a line,
   the price takes its own, under the date */
@container legs (max-width:12.5em){
  .legs li>i{grid-area:2/2;justify-self:start;align-self:auto}
  .legs li>svg.chev{grid-area:1/4/3;align-self:center;margin:0}
}
@media (min-width:700px){
  .panel{left:50%;right:auto;top:50%;bottom:auto;width:480px;border-radius:26px;padding:22px 28px 26px;
    transform:translate(-50%,-50%);animation:pop .24s cubic-bezier(.2,.85,.25,1)}
  .panel::before{display:none}
  @keyframes pop{from{opacity:0;transform:translate(-50%,-47%) scale(.97)}to{opacity:1;transform:translate(-50%,-50%)}}
}
/* desktop: a sidebar (the question, the filters, the answer) beside the
   whole year, which fits one 1280 x 900 screen */
@media (min-width:960px){
  .page{max-width:1248px;display:grid;grid-template-columns:320px minmax(0,1fr);column-gap:40px;align-items:start}
  .bar{grid-column:1/-1;padding-top:14px}
  .side{display:block;position:sticky;top:20px}
  .head h1{font-size:2.5rem}
  .ctl{margin-top:22px}
  .rts{display:flex;flex-wrap:wrap;gap:7px;background:none;border-radius:0;overflow:visible}
  .rts a{flex:none;min-height:38px;height:38px;padding:0 13px;border-radius:999px;background:var(--fill2);margin:3px 0}
  .rts a+a::before,.rts a[aria-current]::after,.rts .lg,.rts .s{display:none}
  .rts .sh{display:inline}
  .rts a[aria-current]{background:var(--btn);color:var(--btn-ink)}
  .best{margin-top:26px}
  .side>.group{margin-top:14px}
  .side>.foot{padding:26px 4px;text-align:left}
  .years{margin-top:-6px}
  .yh{margin:0 0 8px 2px;font-size:1.375rem}
  .yr+.yh{margin-top:18px}
  /* at most four months a row, fewer where four would bring their numbers together */
  .yr{grid-template-columns:repeat(auto-fill,minmax(min(100%,max(var(--mo),25% - 23.5px)),1fr));
    gap:14px 30px;padding:16px 22px 12px}
  .mo h3{margin-bottom:4px}
  .days{gap:2px}
  .days>*{border-radius:8px;aspect-ratio:1.15}
  .mf{margin-top:5px}
  .mf,.mc{margin-left:2px}
}
/* last, so it outranks the desktop .panel rule above at equal specificity:
   the sheet opens instantly for a reader who asked for less motion, at
   every width (the 320 px phone and the >=700 px dialog alike). */
@media (prefers-reduced-motion:reduce){.panel,.scrim{animation:none}}
