@layer tokens, base, components, layouts, screens;

@layer tokens {
  /* The font of DESIGN.md (DSV-10): Hanken Grotesk, variable, SIL OFL (fonts/OFL.txt). Two
     subsets; the browser loads the second only for a character outside Latin-1. */
  @font-face {
    font-family: "Hanken Grotesk";
    src: url("fonts/hanken-grotesk-latin.woff2") format("woff2");
    font-weight: 100 900;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  }

  @font-face {
    font-family: "Hanken Grotesk";
    src: url("fonts/hanken-grotesk-latin-ext.woff2") format("woff2");
    font-weight: 100 900;
    font-display: swap;
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }

  :root {
    --font: "Hanken Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    /* The palette of DESIGN.md (DSV-10): warm stone paper in light, warm stone in dark. Where its
       prose and its frontmatter differ, the prose wins: canvas #f9f8f5, ink #1c1917. */
    --bg: light-dark(#f9f8f5, #0c0a09); /* base canvas */
    --surface: light-dark(#ffffff, #1c1917); /* elevated canvas: rows, header, sheets */
    --surface-2: light-dark(#f5f5f4, #292524); /* a bought row, a field set on --surface */
    --text: light-dark(#1c1917, #f5f5f4); /* primary ink */
    --muted: light-dark(#78716c, #a8a29e); /* muted ink */
    --struck: light-dark(#a8a29e, #78716c); /* struck ink: a bought name, an idle tick */
    --line: light-dark(#e7e5e4, #292524);
    --control: light-dark(#d6d3d1, #44403c);
    --ink: light-dark(#1c1917, #f5f5f4);
    --on-ink: light-dark(#ffffff, #1c1917);
    --secondary: light-dark(#ecebe8, #322e2b);
    /* Terracotta, the alert accent. #c85a32 is 4.2:1 on white, so text takes a darker step. */
    --danger: light-dark(#ba1a1a, #ffb4ab); /* error red: a destructive action, a failure */
    --warning: light-dark(#b34a24, #f0906a); /* terracotta: an alert, a recurrent item */
    --scrim: rgb(28 25 23 / 0.45);

    --radius-m: 6px;
    --radius-l: 6px;
    --radius-xl: 12px;
    /* No blur at rest: borders give depth. Level 2 (sheets, a lifted row) has a warm edge. */
    --shadow-s: 0 1px 2px rgb(41 37 36 / 0.16);
    --shadow-l: 0 4px 12px rgb(120 113 108 / 0.12);

    --text-s: 0.875rem; /* body-sm: 14px */
    --text-m: 1rem; /* body-md: 16px */
    --text-row: 1.125rem; /* body-lg: 18px, the name of a row */
    --text-group: 1.125rem; /* headline-sm: 18px, the head of a group */
    --text-h: 1.375rem; /* headline-md: 22px */
    --text-l: 1.625rem; /* headline-lg-mobile: 26px, the screen title */
    --weight-row: 500;
    --tick: 32px; /* the round tick of a row */
    --tick-stroke: 2.5px; /* the mark inside a checked tick */
    --strike: 1.5px; /* the line through a bought name */
    --border-control: 1.5px; /* the border of an input */
    --tracking-caps: 0.04em; /* a category tag in small capitals */
    --day-bar: 4px; /* the emerald bar before the name of a day */
    --text-label-lg: 0.9375rem; /* label-lg: 15px, the week of Menú */
    --text-label-md: 0.8125rem; /* label-md: 13px, the label of a meal */
    --meal-min: 88px; /* the least height of a meal tile */
    --chevron-box: 24px; /* the box of the chevron of a settings row */
    --chevron-mark: 8px; /* the drawn mark inside it */
    --dot: 6px; /* the dot before the status of a meal */

    /* Emerald: one green for the main action, progress and the checked state. */
    --done: light-dark(#13734b, #4fbf88);
    --on-done: light-dark(#ffffff, #0c1a12);
    --done-soft: light-dark(#e3f1ea, #163325);
    --primary: var(--done);
    --on-primary: var(--on-done);
    --primary-soft: var(--done-soft);
    /* The tags of the lists of a member (SHARE-14): blue for the own list, amber for the shared
       one. Neither is green, the colour of done. */
    --list-own: light-dark(#2a5db8, #8fb5f7);
    --list-shared: light-dark(#9a5b00, #f2b65c);
  }

  /* The columns of the app from 600px; the frame, the gutter and --page-max are the design system's
     (LAY-1). */
  @media (min-width: 600px) {
    :root {
      /* The column of the panels of Compra and Menú from 840px (LIST-38). */
      --panel-width: clamp(240px, 30vw, 320px);
      /* The column of Ajustes from 840px (NAV-27). */
      --settings-max: 960px;
      /* The recipe pane (REC-39): half the area beside the rail, at most 480px. */
      --pane-detail: min(480px, (100vw - var(--rail-width) - var(--frame-side)) / 2 - var(--gutter));
    }
  }
}

@layer base {
  body {
    margin: 0;
    display: flex;
    flex-direction: column;
    height: 100dvh;
    /* The page keeps out of a cutout on either side (LAY-4); from 600px the rail takes its side. */
    padding-inline-start: env(safe-area-inset-left);
    padding-inline-end: env(safe-area-inset-right);
    background: var(--bg);
    color: var(--text);
    font: var(--text-m)/1.4 var(--font);
    font-variant-numeric: tabular-nums;
    -webkit-tap-highlight-color: transparent;
  }

  /* From 600px the fixed rail stands at the start with either hand (LAY-2): the body pads the rail at
     the start and the frame's end margin at the end (LAY-4). */
  @media (min-width: 600px) {
    body {
      padding-left: calc(var(--rail-width) + env(safe-area-inset-left));
      padding-right: calc(var(--frame-side) + env(safe-area-inset-right));
    }
  }

  /* The empty state sits in the lower half of the list area: `main` gives it all the room below the
     search field, and the DS `low` variant ends its content at the bottom. */
  main:has(> .empty-state.low:not([hidden])) {
    display: flex;
    flex-direction: column;
  }

  main > .empty-state.low {
    flex: 1;
  }

  button,
  input {
    font: inherit;
    color: inherit;
  }
}

@layer components {
  /* @extends app-header */
  /* The progress bar of Compra is the last child and takes a full line below the title and the
     buttons. */
  .app-header {
    flex-wrap: wrap;
  }

  .app-header .progress {
    flex: none;
  }

  /* The header of DESIGN.md (DSV-13): an elevated band, and its tools as bordered square tiles. */
  .app-header {
    background: var(--surface);
  }

  .app-header .button.icon {
    border: 1px solid var(--line);
    background: var(--bg);
  }

  .app-header .button.icon:not([hidden]) ~ .button.icon:not([hidden]) {
    margin-left: calc(var(--space-1) * -1);
  }

  /* Below 600px the dock holds the search, so the header has no search button. */
  body:has(.dock:not([hidden])) .app-header .button.icon[aria-controls="search"] {
    display: none;
  }

  @media (min-width: 600px) {
    body:has(.dock:not([hidden])) .app-header .button.icon[aria-controls="search"] {
      display: inline-flex;
    }
  }

  /* A short window, such as a phone on its side, gives more of its height to the list. */
  @media (max-height: 480px) {
    .app-header {
      padding-top: calc(var(--space-2) + env(safe-area-inset-top));
      padding-bottom: var(--space-2);
    }
  }

  /* @extends sheet */
  /* From 840px (DSV-6) a sheet with a head takes the column of the panels of Compra (LIST-38),
     --panel-width wide, in the card of LAY-5. The content makes room for that column when it has no
     panels, so the sheet covers no row. Each sheet has a head (NAV-22); a :has() may not hold another
     :has(). */
  @media (min-width: 840px) {
    .sheet-layer:has(.sheet-head) .sheet {
      width: var(--panel-width);
    }

    body:has(.sheet-layer:not(.side-pane):not([hidden])) main:not(:has(> .side-panels:not([hidden]))),
    body:has(.sheet-layer:not(.side-pane):not([hidden])):not(:has(.side-panels:not([hidden]))) .app-header {
      padding-inline: var(--gutter) calc(var(--gutter) * 2 + var(--panel-width));
    }
  }

  /* @extends sheet
     @variant side-pane  On sheet-layer: from 840px the layer is a pane at the end of the frame, with
                         no veil and no slide, and the content of main moves beside it (REC-39). */
  @media (min-width: 840px) {
    .sheet-layer.side-pane:has(.sheet-head) {
      inset: var(--space-4) calc(var(--frame-side) + env(safe-area-inset-right) + var(--gutter)) var(--space-4) auto;
      z-index: 5;
      display: block;
      width: var(--pane-detail);
      padding: 0;
      transition: none;
    }

    .side-pane .sheet-veil {
      display: none;
    }

    .sheet-layer.side-pane .sheet {
      width: 100%;
      height: 100%;
      max-height: 100%;
      border-radius: var(--radius-l);
      transform: none;
      transition: none;
    }

    body:has(.side-pane:not([hidden])) main,
    body:has(.side-pane:not([hidden])) .app-header {
      padding-inline: var(--gutter) calc(var(--pane-detail) + var(--gutter) * 2);
    }

    body:has(.side-pane:not([hidden])) .item-list:has(> .item-row.recipe) {
      grid-template-columns: minmax(0, 1fr);
    }
  }

  /* @extends dock
     @state searching  The search is open: the field and Cancelar take the dock.
     @part dock-shop-name  The name of the list in the chip of the dock, cut with an ellipsis. */
  /* The search button and the add button stay at the end of the dock, also with no shop list:
     the add button keeps no auto margin, so it closes the group beside the search button. */
  .dock {
    justify-content: flex-end;
  }

  .dock > .dock-add {
    margin-inline: 0;
  }

  /* @extends dock */
  /* Left hand: the shared mirror would read the arrows backwards, so ‹ chip › keep their order and
     only the search button and the add button move to the start side, the add button first. The
     search mode mirrors as the shared dock. */
  [data-hand="left"] .dock {
    flex-direction: row;
    justify-content: flex-start;
  }

  [data-hand="left"] .dock > .button.icon[aria-controls="dock-field"] {
    order: -1;
  }

  [data-hand="left"] .dock:not(.searching) > .dock-add {
    order: -2;
  }

  [data-hand="left"] .dock.searching {
    flex-direction: row-reverse;
  }

  .dock-field > .icon {
    flex: none;
    margin-inline-start: var(--space-3);
    color: var(--muted);
  }

  .dock-field > .icon + .dock-input {
    padding-inline-start: var(--space-2);
  }

  /* The dock holds five controls below 600px: the chip shrinks, and a long name ends in an ellipsis. */
  /* A flex chip, so that a long name (`Todas las tiendas`, a long shop) ends in an ellipsis and the
     count and the chevron stay inside the chip. */
  .dock .chip {
    display: flex;
    align-items: center;
    min-width: 0;
  }

  .dock-shop-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .dock .chip .chevron {
    margin-inline-start: var(--space-2);
  }

  /* An icon button beside an input stands at its bottom edge. */
  .input-row > .button.icon {
    align-self: flex-end;
  }

  /* Guardado holds a check icon before its text. */
  .sheet-saved {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
  }

  /* @extends input */
  /* The fields of DESIGN.md (DSV-12): a 1.5px stone border, and a 2px emerald one on focus. */
  .input {
    border-width: var(--border-control);
  }

  .input:focus-visible,
  .field-input:focus {
    border-width: 2px;
    border-color: var(--done);
  }

  .input::placeholder {
    color: var(--struck);
  }

  /* @extends item-row
     @variant recipe              A recipe: its name and its meta line, then its send button.
     @part recipe-in-list         The badge en la lista of a recipe whose products are all in its list (REC-38, REC-40).
     @part recipe-products        The products of a recipe on one line, on a tinted box, cut with an ellipsis (REC-40).
     @state bought    A ticked item of Actual.
     @state current   The recipe that is open; from 840px it stands beside its pane (REC-39). */

  @media (min-width: 840px) {
    .item-row.recipe.current .item-row-body {
      background: var(--secondary);
    }
  }

  /* From 840px the recipes stand in two columns, so more of them fit on the screen of a tablet. An
     unfolded phone keeps one column. The gap shows the page between the columns. */
  @media (min-width: 840px) {
    .item-list:has(> .item-row.recipe) {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      column-gap: var(--space-2);
    }
  }

  /* The row of DESIGN.md (DSV-12): the name in body-lg, a round tick of --tick, and a bought row
     on --surface-2 with its name struck in --struck. */
  .item-row-name {
    font-size: var(--text-row);
    font-weight: var(--weight-row);
  }

  /* The quantity keeps one line, so a long name never splits "1 kg". */
  .item-row-qty {
    white-space: nowrap;
  }

  .item-row-check {
    width: var(--tick);
    height: var(--tick);
    margin-inline: var(--space-1);
    margin-block: var(--space-3);
    border-color: var(--struck);
    border-radius: var(--radius-round);
    background: var(--surface);
  }

  .item-row-check:checked {
    animation: tick-snap var(--motion-fast) var(--ease);
  }

  .item-row-check:checked::after {
    width: 12px;
    height: 6px;
    border-width: 0 0 var(--tick-stroke) var(--tick-stroke);
  }

  @keyframes tick-snap {
    from {
      transform: scale(0.9);
    }
  }

  /* The divider starts at the text, after the larger tick. */
  .item-row:has(.item-row-check) {
    border-image: linear-gradient(to right, transparent calc(var(--space-2) * 2 + var(--space-1) * 3 + var(--tick)), var(--line) 0) 0 0 1 0 / 0 0 1px 0;
  }

  .item-row.bought,
  .item-row.done,
  .item-row.bought .item-row-body,
  .item-row.done .item-row-body {
    background: var(--surface-2);
  }

  .item-row.bought .item-row-name,
  .item-row.done .item-row-name {
    color: var(--struck);
    text-decoration: line-through var(--strike) var(--struck);
  }

  /* Densidad Compacta (NAV-20): the token set of the design system makes a row --tap-l (48px) high;
     the check takes less room around it. */
  :root[data-density="compact"] .item-row-check {
    margin-block: var(--space-2);
  }

  /* @extends group-head
     @part group-head-button  The full-width button that closes and opens the group, with aria-expanded.
     @part group-head-end     The count and the chevron, at the end of the button.
     @part group-head-title   The title with an icon before it, as the head of an aisle has (GROUP-22).
     @part group-head-icon    The icon of the kind of an aisle, in --primary. */

  /* The head of a group is headline-sm of DESIGN.md (DSV-12). */
  .group-head {
    font-size: var(--text-group);
  }

  .group-head:has(> .group-head-button) {
    padding: 0;
  }

  .group-head-button {
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    min-height: var(--tap);
    padding: 0 var(--space-4);
    border-radius: 0;
    color: inherit;
    font: inherit;
  }

  /* A recipe head slides to the right as a row does (GROUP-21): the button fills the sliding body. */
  .group-head:has(> .item-row-swipe) {
    display: block;
    padding: 0;
  }

  /* The sliding body keeps the band's ground, so a recipe head looks like any other head. */
  .group-head .item-row-swipe .item-row-body {
    min-height: var(--tap);
    padding: 0;
    background: var(--bg);
  }

  /* A search shows each group open and disables its head (GROUP-16); the head keeps its colour. */
  .group-head-button:disabled {
    opacity: 1;
  }

  /* The count is second text: muted, small and at the normal weight. */
  .group-head-end {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--muted);
    font-size: var(--text-s);
    font-weight: var(--weight-normal);
  }


  /* @extends nav-bar */
  /* The bar of DESIGN.md (DSV-13): colour and weight only; the place and size of the bar stay those
     of the design system (LAY-2). The current section is emerald, with no pill. */
  .nav-bar {
    background: var(--surface);
  }

  .nav-item {
    font-weight: var(--weight-strong);
  }

  .nav-item[aria-current="page"],
  .nav-item[aria-current="page"] .icon {
    background: none;
    color: var(--done);
    font-weight: var(--weight-strong);
  }

  @media (min-width: 1024px) {
    .nav-item[aria-current="page"] {
      background: var(--done-soft);
    }
  }

  /* @extends item-row
     @part item-row-repeat  The mark of a recurrent item in the meta line, in the alert accent. */
  /* The rows of a group stand in a card (DSV-13): an inset box with a border and 6px corners below
     its head. A row that starts a group takes the top of the card, one that ends it the bottom. */
  .item-list:has(> .item-row) {
    margin-inline: var(--space-4);
  }

  @media (min-width: 600px) {
    .item-list:has(> .item-row) {
      margin-inline: 0;
    }
  }

  /* The sides and the top of the card are a 1px padding on the --line ground of the row, clipped
     to the padding box: the divider of a row with a check is a border-image, which takes the place
     of every border of the row, and the body covers anything drawn inside the row. */
  .item-list > .item-row {
    padding-inline: 1px;
    background: var(--line);
    background-clip: padding-box;
  }

  .item-list > .item-row:first-child,
  .item-list > :is(.group-head, .section-head) + .item-row {
    padding-top: 1px;
    border-start-start-radius: var(--radius-l);
    border-start-end-radius: var(--radius-l);
  }

  .item-list > .item-row:last-child,
  .item-list > .item-row:has(+ :is(.group-head, .section-head)) {
    border-end-start-radius: var(--radius-l);
    border-end-end-radius: var(--radius-l);
    overflow: hidden;
  }

  .item-list > .item-row:first-child,
  .item-list > :is(.group-head, .section-head) + .item-row {
    overflow: hidden;
  }

  /* The last row of a card draws the bottom of the box over its whole width. */
  .item-list > .item-row:last-child,
  .item-list > .item-row:has(+ :is(.group-head, .section-head)) {
    border-image: none;
    border-bottom: 1px solid var(--line);
  }

  /* The name button is two columns: the name and its meta line, then the quantity chip. */
  .item-row-name:has(> .item-row-qty) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: var(--space-2);
    align-items: center;
    align-content: center;
  }

  .item-row-name > .item-row-qty {
    grid-column: 2;
    grid-row: 1 / span 2;
  }

  .item-row-name > .item-row-meta {
    grid-column: 1;
  }

  /* In Recetas each row is a card of its own, as in the screen of Stitch, also in the grid of
     840px. */
  .item-list:has(> .item-row.recipe) {
    margin-top: var(--space-3);
  }

  .item-list:has(> .item-row.recipe) > .item-row {
    margin-bottom: var(--space-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-l);
    border-image: none;
    overflow: hidden;
  }

  @media (min-width: 840px) {
    .item-list:has(> .item-row.recipe) > .group-head {
      grid-column: 1 / -1;
    }

    /* A card as short as the other card of its grid row still fills that row. */
    .item-list:has(> .item-row.recipe) > .item-row {
      display: grid;
    }

    .item-list:has(> .item-row.recipe) > .item-row > .item-row-swipe > .item-row-body {
      min-height: 100%;
    }
  }

  /* The quantity is a bordered chip, as in the screen of Stitch: a --line edge on --surface-2. */
  .item-row-qty {
    justify-self: end;
    padding: var(--space-half) var(--space-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--surface-2);
    color: var(--text);
    font-weight: var(--weight-medium);
  }

  .item-row.bought .item-row-qty,
  .item-row.done .item-row-qty {
    background: var(--surface);
    color: var(--struck);
  }

  .item-row-meta {
    font-weight: var(--weight-medium);
  }

  .item-row-repeat {
    color: var(--warning);
    font-weight: var(--weight-strong);
  }

  .item-row.bought .item-row-repeat {
    color: inherit;
  }

  /* @extends group-head
     @part group-head-count  The count of the group, as a pill. */
  /* A head stands on the canvas above its card, with room above it and no line below. */
  .group-head {
    margin-top: var(--space-3);
    border-bottom: 0;
    font-weight: var(--weight-strong);
  }

  .group-head:first-child {
    margin-top: 0;
  }

  .group-head-count {
    padding: var(--space-half) var(--space-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    background: var(--surface-2);
    color: var(--muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-strong);
  }

  /* @extends section-head */
  .section-head {
    margin-top: var(--space-3);
  }

  /* @extends list-group */
  /* The head of a group of Ajustes is a category tag of DESIGN.md: small capitals, spaced. */
  .settings .list-group-head {
    color: var(--muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-strong);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
  }

  .list-group.card {
    margin-inline: var(--space-4);
  }

  @media (min-width: 600px) {
    .list-group.card {
      margin-inline: 0;
    }
  }

}

@layer screens {
  /* @extends empty-state
     @variant apart  An empty Compra or Recetas: the title and the text in the middle of the room
                     above the buttons, and the buttons at the bottom, in reach of the thumb. */
  .empty-state.low.apart > .empty-state-title,
  .empty-state.low.apart > .button.primary {
    margin-top: auto;
  }

  /* @component side-panels
     @scope list
     @summary The panels of Compra (Por tienda, Próxima compra) and Menú (Para la cesta), from 600px: below the content until 840px, then a sticky column beside it (LIST-38, LIST-39, MENU-21).
     @part side-panel        One panel on --surface: a head, an optional note, a list.
     @part side-panel-head   The title and, at its end, a count or a unit in --muted.
     @part side-panel-end    The end of the head.
     @part side-panel-note   One line about the list, in --muted.
     @part side-panel-list   The rows of the panel.
     @part side-panel-row    A name and, at its end, a value; a bar may follow on a line of its own.
     @part side-panel-value  The value of a row, in tabular figures.
     @part side-panel-bar    A 4px track.
     @part side-panel-fill   The fill of the track, at the true ratio.
     @part side-panel-more   The last line: how many more rows there are, or that the list is empty.
     @example <aside class="side-panels" style="display: grid"><section class="side-panel"><h2 class="side-panel-head"><span>Por tienda</span><small class="side-panel-end">quedan</small></h2><ul class="side-panel-list"><li class="side-panel-row"><span>Mercadona</span><span class="side-panel-value">5 de 7</span><span class="side-panel-bar"><span class="side-panel-fill" style="width: 100%"></span></span></li></ul></section></aside> */
  .side-panels {
    display: none;
  }

  .side-panel {
    padding: var(--space-4) var(--space-5);
    border-radius: var(--radius-l);
    background: var(--surface);
  }

  .side-panel-head {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    margin: 0;
    font-size: var(--text-m);
    font-weight: var(--weight-medium);
  }

  .side-panel-end {
    margin-inline-start: auto;
    color: var(--muted);
    font-size: var(--text-s);
    font-weight: var(--weight-normal);
    font-variant-numeric: tabular-nums;
  }

  .side-panel-note {
    margin: var(--space-1) 0 0;
    color: var(--muted);
    font-size: var(--text-s);
  }

  .side-panel-list {
    margin: var(--space-2) 0 0;
    padding: 0;
    list-style: none;
  }

  .side-panel-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    gap: var(--space-1) var(--space-3);
    padding: var(--space-2) 0;
  }

  .side-panel-row + .side-panel-row {
    border-top: 1px solid var(--line);
  }

  .side-panel-value {
    color: var(--muted);
    font-size: var(--text-s);
    font-variant-numeric: tabular-nums;
  }

  .side-panel-bar {
    grid-column: 1 / -1;
    height: var(--space-1);
    border-radius: var(--radius-pill);
    background: var(--surface-2);
  }

  .side-panel-fill {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--text);
  }

  .side-panel-more {
    padding-top: var(--space-2);
    color: var(--muted);
    font-size: var(--text-s);
  }

  /* From 600px (foldable) the panels stand below the content, in its column. */
  @media (min-width: 600px) {
    .side-panels:not([hidden]) {
      display: grid;
      gap: var(--space-4);
      margin: var(--space-6) 0 var(--space-4);
    }
  }

  /* From 840px (tablet and desktop) they are a sticky column beside the content, --gutter apart; the
     content and the header fill the content area, as a pane does (LAY-3). */
  @media (min-width: 840px) {
    body:has(main > .side-panels:not([hidden])) {
      --page-max: 100%;
    }

    main:has(> .side-panels:not([hidden])) {
      display: grid;
      grid-template-columns: minmax(0, 1fr) var(--panel-width);
      column-gap: var(--gutter);
      align-content: start;
    }

    main:has(> .side-panels:not([hidden])) > * {
      grid-column: 1;
    }

    main > .side-panels:not([hidden]) {
      position: sticky;
      top: var(--space-4);
      grid-column: 2;
      grid-row: 1 / span 40;
      align-self: start;
      margin: var(--space-4) 0;
    }
  }

  /* @component shop-lists
     @scope list
     @summary The shop lists of Actual, below the header.
     @example <div class="cluster scroll shop-lists"><button class="chip surface" type="button" aria-pressed="true"><span>Todas</span><span class="count">4</span></button></div> */
  .shop-lists {
    margin: 0;
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--line);
  }

  /* @component recipe-chips
     @scope recipes
     @summary The filters of Recetas, below the header: Todas, En la lista and Con enlace, each with its count (REC-40).
     @example <div class="cluster scroll recipe-chips"><button class="chip surface" type="button" aria-pressed="true"><span>Todas</span><span class="count">6</span></button><button class="chip surface" type="button" aria-pressed="false"><span>Con enlace</span><span class="count">4</span></button></div> */
  .recipe-chips {
    margin: 0;
    padding: var(--space-2) var(--space-3);
  }

  /* @component list-chips
     @scope list
     @summary The lists of a member of a share, below the header of Compra at every width: Todas, the own list and the shared list (SHARE-14).
     @example <div class="cluster scroll list-chips"><button class="chip surface" type="button" aria-pressed="true"><span>Todas</span><span class="count">5</span></button><button class="chip surface" type="button"><span>Mi lista</span><span class="count">2</span></button><button class="chip surface" type="button"><span>Ana</span><span class="count">3</span></button></div> */
  .list-chips {
    margin: 0;
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--line);
  }

  /* The tag of a row of Todas widens the end slot up to two buttons more; a longer name ends in
     an ellipsis (SHARE-14). */
  .item-row-end:has(.pill) {
    gap: var(--space-1);
    width: auto;
    min-width: calc(var(--tap) * 2);
  }

  .item-row-end .pill {
    max-width: calc(var(--tap) * 2);
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* @component list-tag
     @scope list
     @summary The tag of the list of a row of a member, an outline in the colour of its list: --list-own for Mía, --list-shared for the shared list (SHARE-14). A merged row shows both.
     @variant own  The tag of the own list, in --list-own.
     @variant shared  The tag of the shared list, in --list-shared.
     @example <span class="pill list-tag own">Mía</span> <span class="pill list-tag shared">Ana</span> */
  .pill.list-tag {
    border: 1px solid currentColor;
    background: none;
  }

  .pill.list-tag.own {
    color: var(--list-own);
  }

  .pill.list-tag.shared {
    color: var(--list-shared);
  }

  /* Below 600px the dock holds the shop list of Compra and the search. */
  body:has(.dock:not([hidden])) .shop-lists,
  body:has(.dock:not([hidden])) .search {
    display: none;
  }

  @media (min-width: 600px) {
    body:has(.dock:not([hidden])) .shop-lists {
      display: flex;
    }

    body:has(.dock:not([hidden])) .search {
      display: block;
    }
  }

  /* @component trip-open
     @scope list
     @summary A line of the trip sheet above Terminar compra: the counts, and what happens to the items not bought.
     @example <p class="hint trip-open">Los 2 artículos que no has comprado se descartan. Puedes cambiarlo en Ajustes.</p> */
  .hint.trip-open {
    padding: var(--space-1) 0 var(--space-3);
  }

  /* The Ajustes button is a link inside the sentence: no side padding and no gap before the
     full stop, underlined in the colour of the text. It keeps the tap height (TAP-1). */
  .hint.trip-open .button {
    gap: 0;
    padding-inline: 0;
    color: var(--text);
    text-decoration: underline;
    text-underline-offset: 3px;
  }

  .hint.trip-open .button::after {
    content: ".";
    text-decoration: none;
  }

  /* Two wide buttons of a sheet stand apart, as Seguir comprando below Terminar compra. */
  .sheet-body > .button.wide + .button.wide {
    margin-top: var(--space-2);
  }

  /* @component search
     @scope list
     @summary The search box of Actual and Recetas, below the header.
     @example <div class="search"><input class="input raised" type="search" placeholder="Buscar"></div> */
  .search {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--line);
  }

  /* @component link-card
     @scope recipes
     @summary The link of a recipe at the top of its sheet (REC-15): a tap opens the video, or the page, out of the app.
     @part link-card-text   The title above the host.
     @part link-card-title  Ver vídeo for a video site, Abrir enlace for another page.
     @part link-card-host   The host of the link, with no www.
     @example <a class="link-card" href="#"><svg class="icon" aria-hidden="true"><use href="icons.svg#play"/></svg><span class="link-card-text"><span class="link-card-title">Ver vídeo</span><small class="link-card-host">youtube.com</small></span><svg class="icon small" aria-hidden="true"><use href="icons.svg#external"/></svg></a> */
  .link-card {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--tap-l);
    margin-top: var(--space-4);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-l);
    background: var(--surface-2);
    color: var(--text);
    text-decoration: none;
  }

  .link-card:hover {
    background: var(--secondary);
  }

  .link-card[hidden] {
    display: none;
  }

  .link-card-text {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
  }

  .link-card-title {
    font-weight: var(--weight-medium);
  }

  .link-card-host {
    overflow: hidden;
    color: var(--muted);
    font-size: var(--text-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .link-card > .icon.small {
    color: var(--muted);
  }

  /* @component product-form
     @scope recipes
     @summary The row that adds a product to a recipe, in the recipe sheet.
     @part product-name  Its input, which takes the focus after an add.
     @example <form class="input-row product-form"><input class="input product-name" type="text" placeholder="Añadir producto"><button class="button primary" type="submit">Añadir</button></form> */
  .product-form {
    padding: var(--space-3) 0 var(--space-2);
  }

  /* @component hint
     @scope list
     @summary A line of help below a list.
     @example <p class="hint">Un artículo con ↻ activo vuelve aquí al comprarlo.</p> */
  .hint {
    margin: 0;
    padding: var(--space-3) var(--space-4);
    color: var(--muted);
    font-size: var(--text-s);
  }

  /* @component product-add
     @scope recipes
     @summary The add row of a recipe with the suggestions below it.
     @example <div class="product-add" style="width: 100%"><ul class="list-group plain"><li><button class="list-row" type="button">Huevos</button></li></ul></div> */
  .product-add > .list-group {
    padding-bottom: var(--space-2);
  }

  /* @component version-status
     @scope settings
     @summary The state of the installed version: a sign, and a text for the outdated state only.
     @part version-status-text  The text of the state. With a sign it stays for screen readers only.
     @example <span class="version-status" data-status="current" role="status"><svg data-for="current" class="icon small" aria-hidden="true"><use href="icons.svg#check-circle"/></svg><span class="version-status-text">Al día</span></span> */
  /* Relative, so the hidden text stays inside the scrolling main and does not scroll the page. */
  .version-status {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-1);
  }

  .version-status [data-for] {
    display: none;
  }

  .version-status[data-status='checking'] [data-for='checking'],
  .version-status[data-status='current'] [data-for='current'],
  .version-status[data-status='outdated'] [data-for='outdated'],
  .version-status[data-status='unknown'] [data-for='unknown'] {
    display: block;
  }

  .version-status[data-status='checking'] .icon {
    animation: spin var(--motion-slow) linear infinite;
  }

  .version-status[data-status='current'] {
    color: var(--done);
  }

  .version-status[data-status='outdated'] {
    flex-basis: 100%;
    justify-content: center;
    color: var(--warning);
  }

  .version-status[data-status='unknown'] {
    color: var(--danger);
  }

  .version-status:not([data-status='outdated']) .version-status-text {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  @keyframes spin {
    to {
      transform: rotate(1turn);
    }
  }

  /* @component week-bar
     @scope menu
     @summary The week of Menú in view, with a button to the week before and to the week after (MENU-23).
     @part week-bar-label  Esta semana, or Semana del 26 oct.
     @part week-bar-range  The days of the week in view after its name, as 20–26 may, in --muted (MENU-25).
     @example <div class="week-bar" style="width: 100%"><button class="button icon" type="button" aria-label="Semana anterior">‹</button><h2 class="week-bar-label">Esta semana</h2><button class="button icon" type="button" aria-label="Semana siguiente" disabled>›</button></div> */
  .week-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
  }

  .week-bar-label {
    margin: 0;
    color: var(--text);
    font-size: var(--text-label);
    font-weight: var(--weight-medium);
    text-align: center;
  }

  /* @component day
     @scope menu
     @summary One day of the menu: its name, then its two meals side by side.
     @part day-title  The name of the day.
     @part day-long   The long name of the day.
     @part day-short  The short name of the day: it shows instead of the long one in the compact density below 600px.
     @part day-date   The date of the day, as 20 may, in muted small capitals at the end of the head (MENU-25).
     @part meals      The two meals.
     @example <ul class="item-list" style="width: 100%"><li class="day"><h3 class="day-title"><span class="day-long">Lunes</span><span class="day-short">lun</span></h3><div class="meals"><button class="meal" type="button"><small class="meal-label">Comida</small><span class="meal-name">Tortilla</span></button><button class="meal free" type="button"><small class="meal-label">Cena</small><span class="meal-name">Elegir receta</span></button></div></li></ul> */
  /* A day stands on the ground of the page: its name is a head, its meals are the tiles. */
  .day {
    padding: var(--space-2) var(--space-4);
  }

  .day-title {
    margin: 0 0 var(--space-2);
    padding-inline-start: var(--space-2);
    border-inline-start: var(--day-bar) solid var(--done);
    color: var(--text);
    font-size: var(--text-group);
    font-weight: var(--weight-strong);
  }

  /* Sentence case: the browser writes a weekday in lower case in some languages (`lunes`). */
  /* The head is a flex box, so the capital goes on each name, a flex item, and not on the head. */
  .day-title::first-letter,
  .day-long::first-letter,
  .day-short::first-letter {
    text-transform: uppercase;
  }

  .meals {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2);
  }

  /* From 600px the name of the day stands at the start of its row, so the week fits on the screen
     of a tablet or an unfolded phone. A subgrid gives each name the width of the longest one, in
     any language. */
  @media (min-width: 600px) {
    .item-list:has(> .day) {
      display: grid;
      grid-template-columns: max-content minmax(0, 1fr);
    }

    .day {
      display: grid;
      grid-column: 1 / -1;
      grid-template-columns: subgrid;
      align-items: center;
      column-gap: var(--space-3);
      padding-block: var(--space-2);
    }

    .day-title {
      margin: 0;
    }
  }

  /* Densidad Compacta (NAV-20), below 600px: the day head stays above its meals, with the short name
     of the day, and the name of a meal takes two lines. From 600px the long name stands beside the
     meals again. */
  .day-short {
    display: none;
  }

  :root[data-density="compact"] .day {
    padding-block: var(--space-1) var(--space-2);
  }

  :root[data-density="compact"] .day-long {
    display: none;
  }

  :root[data-density="compact"] .day-short {
    display: inline;
  }

  :root[data-density="compact"] .day-title {
    margin-bottom: var(--space-1);
  }

  :root[data-density="compact"] .meal {
    min-height: var(--tap);
    padding-block: var(--space-1);
  }

  :root[data-density="compact"] .meal-name {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-height: 1.25;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  @media (min-width: 600px) {
    :root[data-density="compact"] .day {
      padding-block: var(--space-1);
    }

    :root[data-density="compact"] .day-long {
      display: inline;
    }

    :root[data-density="compact"] .day-short {
      display: none;
    }

    :root[data-density="compact"] .day-title {
      margin-bottom: 0;
    }
  }

  /* @component meal
     @scope menu
     @summary One meal of a day: its label, then its recipe or the call to choose one.
     @variant free   A meal with no recipe and no dish.
     @variant dish   A meal with a dish that has no recipe.
     @variant past   A meal of a past week: read only, its name muted (MENU-23).
     @part meal-label  Comida or Cena.
     @part meal-name   The recipe, cut with an ellipsis.
     @part meal-entry  A meal of a member with scope all: its name and the tag of its list (MENU-20).
     @part meal-add    The ⊕ icon above the call of a free meal.
     @part meal-status The status line of a slot with one meal, after a dot: Receta lista in --primary, or Anotación rápida in --muted for a dish (MENU-25).
     @example <button class="meal" type="button"><small class="meal-label">Comida</small><span class="meal-name">Tortilla</span></button> */
  .meal {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    min-width: 0;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--line);
    background: var(--surface);
    text-align: left;
  }

  .meal.free {
    border: 1px dashed var(--control);
    background: none;
  }

  .meal.dish {
    border-color: var(--muted);
  }

  /* A meal of a past week (MENU-23) is read only: a tile with no press. */
  .meal.past .meal-name {
    color: var(--muted);
  }

  /* @component meal-actions
     @scope menu
     @summary The buttons of the meal sheet, on a split: Crear receta and Quitar side by side, or
              Quitar alone across the full width.
     @example <div class="split meal-actions" style="width: 100%"><button class="button primary hand-side" type="button">Crear receta</button><button class="button outline danger" type="button">Quitar</button></div> */
  .meal-actions:has(> [hidden]) > :not([hidden]) {
    grid-column: 1 / -1;
  }

  .meal-label {
    color: var(--muted);
    font-size: var(--text-xs);
  }

  .meal-name {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .meal.free .meal-name {
    color: var(--muted);
  }

  /* MENU-20: the tag of a meal keeps its place; the name gives way with an ellipsis. */
  .meal-entry {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    max-width: 100%;
    min-width: 0;
  }

  .meal-entry .pill,
  .chip .pill,
  .product .pill {
    flex: none;
    max-width: calc(var(--tap) * 2);
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .chip .pill {
    margin-inline-start: var(--space-1);
  }

  /* @component meal-planned
     @scope menu
     @summary The two meals of a slot in the meal sheet of a member, each with the tag of its list and its own Quitar, apart from the field below (MENU-20).
     @example <ul class="products meal-planned"><li class="product"><span class="product-text">Sopa</span><span class="pill list-tag own">Mía</span><button class="button icon" type="button">✕</button></li><li class="product"><span class="product-text">Paella</span><span class="pill list-tag shared">Ana</span><button class="button icon" type="button">✕</button></li></ul> */
  .products.meal-planned {
    margin: 0 0 var(--space-2);
  }

  /* Menú shows no add button (MENU-14), so `main` keeps no room for one at its end: the bar of Menú
     sticks to the bottom edge of `main` and ends the scroll. */
  main:has(> .action-bar:not([hidden])) {
    padding-bottom: 0;
  }

  /* Llenar cesta and Vaciar menú share the bar: the split takes its full width. */
  .action-bar > .split {
    flex: 1;
  }

  /* @component action-label
     @scope menu
     @summary The text of a button with an icon in a split action bar. Half the bar each: below
       about 390px a long label (`Riempi cestino`) ends in an ellipsis instead of crossing the border.
       The buttons pad 12px at the sides, so `Llenar cesta` keeps its words at 360px.
     @example <div class="split" style="width: 160px"><button class="button primary" type="button" style="min-width: 0"><span class="action-label">Riempi cestino</span></button><button class="button outline danger" type="button">Svuota</button></div> */
  .action-bar > .split > .button {
    min-width: 0;
    padding-inline: var(--space-3);
  }

  .action-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* @component products
     @scope recipes
     @summary The products of a recipe in the recipe sheet, each with its remove button.
     @part product       One product.
     @part product-text  Its name.
     @state leaving  On product: a row that closes before it leaves.
     @state flash    On product: a row that has just come: it flashes.
     @variant flush  On products: rows across the sheet, as the rows of a list. A name field with no box,
       one line below each row, from the start of the name to the end. The aisles of Ajustes.
     @example <ul class="products" style="width: 100%"><li class="product"><span class="product-text">Patatas</span><button class="button icon" type="button" aria-label="Quitar">✕</button></li></ul> */
  .products {
    margin: var(--space-2) 0 0;
    padding: 0;
    list-style: none;
  }

  .product {
    display: flex;
    align-items: center;
    min-height: var(--tap);
    border-top: 1px solid var(--line);
  }

  .product-text {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
  }

  .products > .leaving {
    overflow: hidden;
    min-height: 0;
    opacity: 0;
    transition: height var(--motion) ease-out, opacity var(--motion) ease-out;
  }

  .product.flash {
    animation: flash var(--motion-slow) ease-out;
  }

  .products.flush {
    margin-inline: calc(-1 * var(--space-4));
  }

  .products.flush > .product {
    border-top: 0;
    border-bottom: 1px solid var(--line);
    border-image: linear-gradient(to right, transparent var(--space-4), var(--line) 0) 0 0 1 0 / 0 0 1px 0;
  }

  /* The line of the row in edit is the colour of the text, as the border of a box in focus. */
  .products.flush > .product:focus-within {
    border-image-source: linear-gradient(to right, transparent var(--space-4), var(--text) 0);
  }

  .products.flush .input-row {
    flex: 1;
    gap: 0;
    padding-inline-end: var(--space-2);
  }

  .products.flush .input-row > .input {
    padding-inline: var(--space-4) var(--space-2);
    border: 0;
    border-radius: 0;
    background: none;
  }

  /* The fill of a disabled submit (input-row) is not for the moves of an aisle: the first up and
     the last down fade as any disabled button. */
  .products.flush .input-row > .button.icon:disabled {
    background: none;
    opacity: 0.4;
  }

  /* @component settings
     @scope settings
     @summary The Ajustes section: full-width groups of settings rows (list-group) under a list-group-head. A row of Preferencias holds a real select laid over it, so a tap opens the native picker.
     @part settings-hint    A line of help below a group head, on a hint.
     @part settings-section A group head, its hint and its group. From 840px (NAV-27) the head and the hint form a column beside the group.
     @part settings-row-icon The icon at the start of a settings row, in --muted; a danger row draws it in --danger.
     @example <section class="settings" style="width: 100%"><h2 class="list-group-head">Preferencias</h2><div class="list-group"><div class="settings-row"><span class="settings-row-label">Idioma</span><span class="settings-row-value">Español</span><span class="chevron" aria-hidden="true">›</span><select class="select" aria-label="Idioma"><option>Español</option></select></div></div></section> */
  .settings-hint {
    padding: 0 var(--space-4) var(--space-2);
    text-align: start;
    hyphens: auto;
  }

  /* From 840px (NAV-27) Ajustes takes a wider column, and each section is a row: its head and its
     hint at the left, its group at the right, sections split by a line. */
  @media (min-width: 840px) {
    body:has(main > .settings:not([hidden])) {
      --page-max: var(--settings-max);
    }

    .settings-section {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
      grid-template-rows: auto 1fr;
      column-gap: var(--space-6);
      align-items: start;
      padding-block: var(--space-5);
    }

    .settings-section + .settings-section {
      border-top: 1px solid var(--line);
    }

    .settings-section > .list-group-head,
    .settings-section > .settings-hint {
      grid-column: 1;
      padding-inline: 0;
    }

    .settings-section > .list-group-head {
      margin: var(--space-4) 0 var(--space-1);
    }

    .settings-section > .list-group {
      grid-column: 2;
      grid-row: 1 / span 2;
    }
  }

  /* @extends list-group */
  .settings .settings-row {
    position: relative;
  }

  /* @component account-row
     @scope settings
     @summary The row Cuenta (ACC-10): the email, and the last sync under it in the muted style. A long email breaks anywhere, not at a hyphen.
     @example <div class="settings-row account-row" style="width: 100%"><span class="settings-row-label">ciro@example.test</span><span class="settings-row-value">Última sincronización: 24 oct, 18:37</span></div> */
  .settings .settings-row.account-row {
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
    gap: var(--space-1);
    padding-block: var(--space-2);
  }

  .settings .settings-row.account-row > .settings-row-label,
  .settings .settings-row.account-row > .settings-row-value {
    flex: none;
    overflow-wrap: anywhere;
    hyphens: none;
  }

  .settings .settings-row.account-row > .settings-row-value {
    font-size: var(--text-s);
  }

  /* @component share-case
     @scope settings
     @summary One case of the sheet Compartir lista (SHARE-9): the people and the invite field of an owner, the invite of an invitee, or the share of a member.
     @part share-text     A text of the case, which breaks a long email anywhere.
     @part share-person   The email of a person, the state of the invite and the scope, one under the other. The scope of an active person is a button: a tap changes it.
     @part share-actions  The buttons at the end of the row of a person: Invitar de nuevo of a cancelled person, then ✕.
     @example <div class="share-case" style="width: 100%"><p class="share-text">ana@example.test te invita a compartir su lista.</p><button class="button primary wide" type="button">Aceptar</button><button class="button tonal wide" type="button">Rechazar</button></div> */
  .share-case {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding-bottom: var(--space-4);
  }

  .share-person {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
    padding-block: var(--space-2);
    padding-inline-start: var(--space-4);
    overflow-wrap: anywhere;
  }

  .share-person > .button {
    align-self: flex-start;
    min-height: var(--tap);
    padding-inline: 0;
  }

  .share-actions {
    display: flex;
    flex: none;
    align-items: center;
    gap: var(--space-2);
  }

  .share-case > .share-text {
    margin: 0;
    overflow-wrap: anywhere;
  }

  /* The select is invisible: the row shows the ring when the select has the keyboard focus. */
  .settings .settings-row:has(> .select:focus-visible) {
    outline: 2px solid var(--text);
    outline-offset: -2px;
  }

  .settings .settings-row > .select {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
  }

  .settings .list-group > .segmented {
    margin: var(--space-2);
  }

  /* @component whatsnew
     @scope whatsnew
     @summary The release notes in a sheet: one block for each version, newest first.
     @part whatsnew-body     The body of the sheet, with the blocks.
     @part whatsnew-entry    One version: its line and its notes.
     @part whatsnew-version  The version and its day.
     @part whatsnew-notes    The notes of a version, as a list.
     @example <section class="whatsnew-entry"><h3 class="whatsnew-version">Versión 1.4.0 · 20 de octubre de 2026</h3><ul class="whatsnew-notes"><li>Un menú nuevo.</li></ul></section> */
  .whatsnew-body {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
  }

  .whatsnew-version {
    margin: 0 0 var(--space-2);
    color: var(--muted);
    font-size: var(--text-xs);
    font-weight: 600;
  }

  .whatsnew-notes {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding-left: var(--space-5);
  }

  /* @component about
     @scope settings
     @summary The version lines at the end of Ajustes.
     @part about-line  One line: the version and its state.
     @example <footer class="about"><p class="about-line"><span>Versión 1.0.20</span></p></footer> */
  .about {
    margin: var(--space-6) var(--space-3) 0;
    color: var(--muted);
    font-size: var(--text-xs);
    text-align: center;
  }

  .about-line {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: var(--space-1) var(--space-2);
    margin: 0;
  }

  /* @component account
     @scope account
     @summary The screens of the account over the full app (ACC-2): Iniciar sesión and Esperando
       aprobación. One page shows at a time, in the width of the column, centred.
     @part account-top     The top bar: Volver a la lista, only when the device has a list.
     @part account-page    One screen: Iniciar sesión, or the box of Esperando aprobación.
     @part account-logo    The app icon, at the top of Iniciar sesión.
     @part account-title   The name of the screen.
     @part account-text    The line below the title.
     @part account-wait    The large icon of Esperando aprobación.
     @example <div class="account-page" style="width: 100%"><h2 class="account-title">Iniciar sesión</h2><p class="account-text">Inicia sesión para tener tus listas en todos tus dispositivos.</p><button class="button tonal wide" type="button"><svg class="icon small" aria-hidden="true"><use href="icons.svg#google"/></svg>Continuar con Google</button><button class="button accent" type="button">Usar sin cuenta</button></div> */
  .account {
    position: fixed;
    inset: 0;
    /* Over the walkthrough: the login of a first start comes first. */
    z-index: 40;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: calc(var(--space-5) + env(safe-area-inset-top)) calc(var(--space-5) + env(safe-area-inset-right))
      calc(var(--space-5) + env(safe-area-inset-bottom)) calc(var(--space-5) + env(safe-area-inset-left));
    background: var(--bg);
    overflow: hidden auto;
  }

  @media (min-width: 600px) {
    .account {
      padding-left: max(var(--space-5) + env(safe-area-inset-left), (100% - var(--content-max)) / 2);
      padding-right: max(var(--space-5) + env(safe-area-inset-right), (100% - var(--content-max)) / 2);
    }
  }

  .account-top {
    display: flex;
    flex: none;
    min-height: var(--tap);
  }

  .account-page {
    display: flex;
    flex: 1 0 auto;
    flex-direction: column;
    gap: var(--space-3);
  }

  .account-logo {
    align-self: center;
    width: var(--logo);
    height: var(--logo);
  }

  .account-title {
    margin: 0;
    font-size: var(--text-l);
    text-align: center;
  }

  .account-title:focus {
    outline: none;
  }

  .account-text {
    margin: 0 0 var(--space-2);
    color: var(--muted);
    text-align: center;
  }

  .account-wait {
    align-self: center;
    width: var(--logo);
    height: var(--logo);
    margin-top: var(--space-6);
    color: var(--muted);
  }

  .account-page > .button.accent {
    align-self: center;
  }

  /* @component sync-note
     @scope list
     @summary The note over the list while the device is offline (SYNC-11): the changes stay saved.
     @example <p class="sync-note"><svg class="icon small" aria-hidden="true"><use href="icons.svg#offline"/></svg><span>Sin conexión · cambios guardados</span></p> */
  .sync-note {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    margin: 0;
    padding: var(--space-2) var(--space-4);
    color: var(--muted);
    font-size: var(--text-s);
  }

  /* @component onboarding
     @scope onboarding
     @summary The walkthrough of the first start, over the full app: a page, the dots, the buttons.
     @variant move             The screen of the old address (ADDR-1): over every layer, Iniciar sesión too.
     @part onboarding-top      The top bar: Atrás at the start and Saltar at the end. It keeps out of the top safe-area inset.
     @part onboarding-logo     The app icon, at the top of the first page only.
     @part onboarding-page     The page in view: on the first page the app icon, then its drawing, its title and its text.
     @part onboarding-title    The title of a page.
     @part onboarding-text     The text of a page.
     @part onboarding-key      A control named in a text, such as + or an icon: its icon and its name in bold --text, with no box.
     @part onboarding-dots     One dot for each page.
     @part onboarding-dot      One dot.
     @part onboarding-switch   The switch of the sample data on the last page. It keeps its room on each page.
     @state away               On onboarding-switch: a page with no switch. The room stays, so nothing moves.
     @state on                 On onboarding-dot: the page in view.
     @state enter-next         On onboarding-page: it comes in from the right.
     @state enter-previous     On onboarding-page: it comes in from the left.
     @example <div class="onboarding-top" style="width: 100%"><button class="button accent" type="button">Atrás</button><button class="button quiet" type="button">Saltar</button></div>
     @example <div class="onboarding-dots"><span class="onboarding-dot on"></span><span class="onboarding-dot"></span><span class="onboarding-dot"></span></div> */
  .onboarding {
    position: fixed;
    inset: 0;
    z-index: 30;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: calc(var(--space-5) + env(safe-area-inset-top)) calc(var(--space-5) + env(safe-area-inset-right))
      calc(var(--space-5) + env(safe-area-inset-bottom)) calc(var(--space-5) + env(safe-area-inset-left));
    background: var(--bg);
    /* A horizontal slide changes the page, so the browser must not take it. */
    touch-action: pan-y;
    overflow: hidden auto;
  }

  /* ADDR-1: the move screen covers the account screens (z-index 40) too. */
  .onboarding.move {
    z-index: 50;
  }

  /* From 600px the walkthrough keeps the width of the column, centred. */
  @media (min-width: 600px) {
    .onboarding {
      padding-left: max(var(--space-5) + env(safe-area-inset-left), (100% - var(--content-max)) / 2);
      padding-right: max(var(--space-5) + env(safe-area-inset-right), (100% - var(--content-max)) / 2);
    }
  }

  /* A short window, such as a phone on its side, has no room for the logo above the drawing. */
  @media (max-height: 480px) {
    .onboarding-logo {
      display: none;
    }
  }

  /* Atrás sits at the start and Saltar at the end. Saltar keeps its place when Atrás is away. */
  .onboarding-top {
    display: flex;
    flex: none;
    justify-content: space-between;
  }

  .onboarding-top > .button:last-child {
    margin-inline-start: auto;
  }

  .onboarding-logo {
    flex: none;
    width: var(--logo);
    height: var(--logo);
  }

  /* The page group takes the free space between the top bar and the dots, centred. */
  .onboarding-page {
    display: flex;
    flex: none;
    margin-block: auto;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-6);
    text-align: center;
  }

  .onboarding-page.enter-next,
  .onboarding-page.enter-previous {
    animation: enter var(--motion) ease-out;
  }

  .onboarding-page.enter-next {
    --enter-from: 48px;
  }

  .onboarding-page.enter-previous {
    --enter-from: -48px;
  }

  /* A title can take two lines in some catalogs. It keeps the room of two, with its last line
     next to the text, so the drawing does not move. */
  .onboarding-title {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: 2lh;
    margin: 0 0 var(--space-2);
    font-size: var(--text-l);
  }

  /* The longest text of a page, in any catalog, is five lines at 32ch. The text keeps room for
     five, so each page has the same height and the drawing and the logo do not move. */
  .onboarding-text {
    max-width: 32ch;
    /* A line break of a catalog text starts a new line. */
    white-space: pre-line;
    min-height: 5lh;
    margin: 0;
    color: var(--muted);
  }

  .onboarding-switch {
    align-self: stretch;
    border-radius: var(--radius-l);
    background: var(--surface);
  }

  .onboarding-switch.away {
    visibility: hidden;
  }

  .onboarding-key {
    color: var(--text);
    font-weight: var(--weight-strong);
    white-space: nowrap;
  }

  /* The title takes the focus when the walkthrough opens, so it shows no ring. */
  .onboarding-title:focus {
    outline: none;
  }

  /* The icon of a key sits on the line of the text, with a small gap before its text. */
  .onboarding-key > .icon {
    display: inline-block;
    width: 1em;
    height: 1em;
    margin-right: var(--space-1);
    vertical-align: -0.2em;
  }

  .onboarding-key > .icon:only-child {
    margin-right: 0;
  }

  .onboarding-dots {
    display: flex;
    flex: none;
    justify-content: center;
    gap: var(--space-2);
  }

  .onboarding-dot {
    width: var(--space-2);
    height: var(--space-2);
    border-radius: var(--radius-round);
    background: var(--muted);
  }

  .onboarding-dot.on {
    background: var(--ink);
  }

  .onboarding > .button.wide {
    flex: none;
    min-height: var(--tap-l);
  }

  /* @component onboarding-art
     @scope onboarding
     @summary The line drawing of a walkthrough page, inline in the page. Its shapes take these parts.
     @part art-card     A card shape, filled with --surface.
     @part art-accent   A --primary stroke.
     @part art-soft     A --primary-soft fill with a --primary stroke.
     @part art-solid    A solid --primary fill.
     @part art-on-solid A stroke on a solid fill.
     @example <div class="onboarding-art"><svg viewBox="0 0 240 160" aria-hidden="true"><rect class="art-card art-accent" x="20" y="20" width="90" height="120" rx="10"/><circle class="art-solid" cx="160" cy="80" r="13"/><path class="art-on-solid" d="M160 74v12M154 80h12"/></svg></div> */
  .onboarding-art > svg {
    display: block;
    width: min(var(--art-width), 70vw);
    height: auto;
    fill: none;
    stroke: var(--muted);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .art-card {
    fill: var(--surface);
  }

  .art-accent {
    stroke: var(--primary);
  }

  .art-soft {
    fill: var(--primary-soft);
    stroke: var(--primary);
  }

  .art-solid {
    fill: var(--primary);
    stroke: none;
  }

  .art-on-solid {
    stroke: var(--on-primary);
  }

  /* DSV-14: the detail of the Stitch screens, with no change of behaviour. */

  /* A recipe card: a 18px name at weight 600, the link button a bordered tile and the send button
     an emerald tile. The tap target keeps the size of a button icon. */
  .item-row.recipe .item-row-body {
    padding-block: var(--space-3);
  }

  .item-row.recipe .item-row-label {
    font-size: var(--text-group);
    font-weight: var(--weight-strong);
  }

  .item-row.recipe .item-row-end {
    gap: 0;
  }

  /* The tile is 40px inside the 48px target (--tap), drawn behind the icon. */
  .item-row.recipe .item-row-end > .button.icon {
    position: relative;
    isolation: isolate;
    background: none;
  }

  .item-row.recipe .item-row-end > .button.icon::before {
    content: "";
    position: absolute;
    inset: var(--space-1);
    z-index: -1;
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--surface);
  }

  .item-row.recipe .item-row-end > .button.icon:last-child {
    color: var(--on-primary);
  }

  .item-row.recipe .item-row-end > .button.icon:last-child::before {
    border-color: var(--primary);
    background: var(--primary);
  }

  /* REC-40: the badge of a recipe in its list, and the products of a recipe on a tinted line. */
  .recipe-in-list {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    align-self: flex-start;
    margin-block: var(--space-1) var(--space-half);
    padding: 0 var(--space-2);
    border: 1px solid var(--primary);
    border-radius: var(--radius-s);
    background: var(--primary-soft);
    color: var(--primary);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
  }

  .recipe-in-list::before {
    content: "";
    width: var(--dot);
    height: var(--dot);
    border-radius: 50%;
    background: currentColor;
  }

  .recipe-products {
    display: block;
    box-sizing: border-box;
    width: 100%;
    max-width: 100%;
    margin-top: var(--space-2);
    padding: var(--space-1) var(--space-2);
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--radius-s);
    background: var(--surface-2);
    color: var(--muted);
    font-size: var(--text-s);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  :root[data-density="compact"] .recipe-products {
    display: none;
  }

  /* The week of Menú is a bordered bar on --surface. */
  .week-bar {
    margin: var(--space-2) var(--space-4) 0;
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--surface);
  }

  .week-bar-label {
    font-size: var(--text-label-lg);
    font-weight: var(--weight-strong);
  }

  /* A meal is a tall tile: its label at 13px, then the name of its recipe at 18px. A free meal
     centres its call in the dashed tile; a dish stands on --surface-2. */
  .meal {
    justify-content: flex-start;
    gap: var(--space-1);
    min-height: var(--meal-min);
    padding: var(--space-3);
  }

  .meal-label {
    font-size: var(--text-label-md);
    font-weight: var(--weight-medium);
  }

  .meal-name {
    font-size: var(--text-row);
    font-weight: var(--weight-row);
  }

  .meal.free {
    align-items: center;
    justify-content: center;
    text-align: center;
  }

  .meal.free .meal-name {
    font-size: var(--text-label);
  }

  .meal.dish {
    border-color: var(--line);
    background: var(--surface-2);
  }

  :root[data-density="compact"] .meal {
    min-height: 0;
  }

  /* A danger button with an outline is drawn in its own colour on --surface. */
  .button.outline.danger {
    border-color: currentColor;
    background: var(--surface);
  }

  /* MENU-25: the range of the week, the date of a day and the status line of a meal. */
  .week-bar-range {
    color: var(--muted);
    font-weight: var(--weight-normal);
  }

  .day-title {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
  }

  .day-date {
    margin-inline-start: auto;
    color: var(--muted);
    font-size: var(--text-label-md);
    font-weight: var(--weight-medium);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
  }

  @media (min-width: 600px) {
    .day-title {
      flex-direction: column;
      gap: 0;
    }

    .day-date {
      margin-inline-start: 0;
    }
  }

  .meal-add {
    color: var(--muted);
  }

  .meal-status {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    margin-top: auto;
    color: var(--primary);
    font-size: var(--text-label-md);
  }

  .meal-status::before {
    content: "";
    width: var(--dot);
    height: var(--dot);
    border-radius: 50%;
    background: currentColor;
  }

  .meal-status.dish {
    color: var(--muted);
  }

  :root[data-density="compact"] .meal-status {
    display: none;
  }

  /* The head of an aisle has the icon of its kind before its title (GROUP-22). */
  .group-head-title {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
  }

  .group-head-icon {
    flex: none;
    color: var(--primary);
  }

  /* The icon at the start of a settings row, as in the screen of Stitch. */
  .settings-row-icon {
    flex: none;
    color: var(--muted);
  }

  .settings-row.danger .settings-row-icon {
    color: inherit;
  }

  /* The chevron of a settings row is a 24px mark with a 2px stroke, as in the screen of Stitch. The
     glyph of the font is too small, so the span hides it and draws the mark. */
  .settings-row .chevron {
    position: relative;
    align-self: center;
    width: var(--chevron-box);
    height: var(--chevron-box);
    font-size: 0;
    line-height: 0;
  }

  /* The mark is out of the flow: the hidden glyph stays a line of its own. */
  .settings-row .chevron::before {
    content: "";
    position: absolute;
    inset: 0;
    margin: auto;
    width: var(--chevron-mark);
    height: var(--chevron-mark);
    border-top: 2px solid currentColor;
    border-right: 2px solid currentColor;
    transform: translateX(-2px) rotate(45deg);
  }
}
