!:
;div#forum.relative.wf.hf.scroll-none.fc
  ;details.absolute.z1
    ::
    =style  "top: 15px; right: 25px; width: 85%; max-width: 440px;"
    ;summary.b2.p-1.br1.bd1.wfc.mono.f4
      =style  "margin-left: auto;"
      ; ?
    ==
    ;div.p3.br1.bd1.b1.fc.g5.mono
      =style  "margin-top: 10px;"
      ;form.need-admin(method "post")
        =hx-confirm  "really reset everything?"
        ;input.hidden(name "reset", value " ");
        ;button.loader.b1.br1.p-1.hover.bd1
          ;span.loaded: prune all
          ;span.loading: ...
        ==
      ==
      ;form.need-admin(method "post")
        =hx-confirm  "delete all posts except the most recent 50?"
        ;input.hidden(name "prune", value " ");
        ;button.loader.b1.br1.p-1.hover.bd1
          ;span.loaded: chop to 50 posts
          ;span.loading: ...
        ==
      ==
      ::
      ;div.fc.g3
        ;div.bold: line modes
        ;div.pre.o7
          ;-  %-  trip
          '''
            +monospace
            *bold
            _italic
            )right justify
            =centered text
            >quote
            ~sampel-palnet
            !https://..image.jpg
            <http://link.com
          '''
        ==
        ;div.o9: the first character of a line sets its mode.
        ;div.o9: a mode applies to the whole line.
      ==
      ;a.o6.tr
        =href  "?code"
        [view hawk code]
      ==
    ==
  ==
  ;div#scroller.grow.scroll-y
    ;div.p2.mw-page.wf.fc.fc.g5.ma
      =style  "padding-bottom: 60vh; padding-top: 400px;"
      ;div.p4.fc.g4.wf.break-word
        =id  "posts"
        ;*
        =/  posts  (children:mq (get-id:mq "posts" dat:f))
        ::
        =/  admin  =((rib:c /sys/our) (rib:c /sys/src))
        ?:  &((has:c /reset) admin)
          ::
          ~
          ::
        ?:  &((has:c /prune) admin)
          ::
          (scag 50 posts)
          ::
        ?~  mus=(peb:c /message)
          ::
          posts
          ::
        ::
        =/  lines=wain  (to-wain:format (crip u.mus))
        =/  who         (cite:title (@p +:(rib:c /sys/src)))
        =/  id          +:(scow %p (mug [lines who]))
        ::
        %+  snoc  posts
        ;div(id id)
          ;+  :: author line
            ::
            =,  mq
            =/  last=manx  ?~(posts ;/("") (rear posts))
            =/  poster=tape
              (as-tape (find-class "author" ~[last]))
            =;  m=manx
              ?.  =(poster who)  m
              m(a.g [[%hidden ""] a.g.m])
            ;a.fr.ac.g4.anchor
              =href  "#{id}"
              =style  "margin: 15px 0;"
              ;hr.o1.grow;
              ;div.mono.o4.author:(-who)
            ==
          ::
          ;*
          %+  turn  lines
          |=  line=cord
          ?+    p=((pole @t) (trip line))
            :: default
              ::
              ?:  =(line '')
                ;div(style "height: 8px;");
              ;div:(-p)
            [%'*' rest=*]
              ::
              ;div.bold:(-rest.p)
              ::
            [%'+' rest=*]
              ::
              ;div.mono.pre.o7.scroll-none
                =style  "max-width: 90%;"
                ;-  rest.p
              ==
              ::
            [%'_' rest=*]
              ::
              ;div.italic:(-rest.p)
              ::
            [%'=' rest=*]
              ::
              ;div.tc:(-rest.p)
              ::
            [%')' rest=*]
              ::
              ;div.tr
                =style  "padding-right: 30px;"
                ;-  rest.p
              ==
              ::
            [%'>' rest=*]
              ::
              ;div
                =style  "padding-left: 20px; margin-left: 20px; border-left: 1px solid var(--b3);"
                ;-  rest.p
              ==
              ::
            [%'!' rest=*]
              ::
              ;div.fc.ac.jc
                ;img
                  =src  rest.p
                  =style  "max-height: 40vh; filter: grayscale(1);"
                  ;
                ==
              ==
              ::
            [%'<' rest=*]
              ::
              ;a.f2.underline.break-all
                =href  rest.p
                ;-  rest.p
              ==
              ::
            [%'~' rest=*]
              ::
              =/  mention  (slaw %p line)
              ?~  mention  ;div:(-p)
              ;div.mono.o8.bold
                =data-mention  +:(scow %p u.mention)
                ;-  (scow %p u.mention)
              ==
            ::
          ==
        ==
      ==
      ;form.fr.bd1.br1.scroll-none.wf.need-poke    :: message form
        ::
        =method  "post"
        =hx-on-htmx-before-request  "localStorage.removeItem('center')"
        ;textarea.p2.grow.pre.mono.no-outline
          =name  "message"
          =rows  "2"
          =placeholder  "say hi..."
          =required  ""
          =spellcheck  "false"
          =autocomplete  "off"
          =oninput  "resizeTextarea(this, event.target.value)"
          =onkeydown  "maybeSubmit(this, event)"
          ;
        ==
        ;button.p-1.b1.hover.loader.mono
          =style  "border-left: 1px solid var(--b2)"
          ;span.loaded.f3: send
          ;span.loading.f4: ....
        ==
      ==
      ;form.p4.fc.ac.jc          :: refresher
        ::
        =hx-swap  "outerHTML"
        =hx-target  "#posts"
        =hx-select  "#posts"
        ;button.loader.br1.bd1.p-1.o7.mono
          ;span.loaded: refresh
          ;span.loading: ......
        ==
      ==
      ;div#bottom;
    ==
  ==
  ::
  ::  scripts
    ::
    ;script(src "/session.js");
    ;script: $(`[data-mention='$\{window.ship}']`).addClass('f-4')
    ;script: $('#forum a:not(.anchor)').attr('target', '_blank')
    ;script
      ;-  %-  trip
      '''
      (() => {
        if (!window.location.hash) {
          let center = localStorage.getItem('center')
          if (!!center) {
            let el = document.getElementById(center)
            el?.scrollIntoView({ behavior: "instant", block: "start" })
          } else {
            let el = document.getElementById('bottom')
            el?.scrollIntoView({ behavior: "instant", block: "end" })
          }
        }
        trackCenteredElementOnScroll()
      })()
      function saveCentered() {
        const container = document.querySelector('#scroller');
        if (!container) return;
        const elements = container.querySelectorAll('[id]');
        if (!elements.length) return;
        const centerY = 35;
        let closest = null;
        let closestDistance = Infinity;
        elements.forEach(el => {
          const rect = el.getBoundingClientRect();
          const elCenter = rect.top + (rect.height / 2);
          const distance = Math.abs(centerY - elCenter);
          if (distance < closestDistance) {
            closest = el;
            closestDistance = distance;
          }
        });
        if (closest?.id && closest.id.length == 13) {
          localStorage.setItem('center', closest.id);
        }
      }
      function trackCenteredElementOnScroll() {
        const container = document.querySelector('#scroller');
        if (!container) return;
        container.addEventListener('scroll', () => {
          saveCentered();
        });
        saveCentered();
      }
      function resizeTextarea(el, text) {
        let numLines = Math.max(2, (text.match(/\r\n|\r|\n/g) || []).length + 2);
        el.rows = numLines;
      }
      function maybeSubmit(el, ev) {
        if (ev.key == 'Enter' && (ev.metaKey || ev.ctrlKey)) {
          el.form.requestSubmit();
        }
      }
      '''
    ==
  ::
==